diff options
Diffstat (limited to 'lib/debugger/src/dbg_wx_trace_win.erl')
-rwxr-xr-x | lib/debugger/src/dbg_wx_trace_win.erl | 1029 |
1 files changed, 1029 insertions, 0 deletions
diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl new file mode 100755 index 0000000000..6e7a291493 --- /dev/null +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -0,0 +1,1029 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(dbg_wx_trace_win). + +%% External exports +-export([init/0, stop/1]). +-export([create_win/4, + get_window/1, + configure/2, + enable/2, is_enabled/1, select/2, + add_break/3, update_break/2, delete_break/2, + clear_breaks/1, clear_breaks/2, + display/2, % Help messages + is_shown/2, % Code area + show_code/3, show_no_code/1, remove_code/2, + mark_line/3, unmark_line/1, + select_line/2, selected_line/1, + eval_output/3, % Evaluator area + update_bindings/2, % Bindings area + trace_output/2, % Trace area + handle_event/2 + ]). +-export([helpwin/2]). + +-include_lib("wx/include/wx.hrl"). + +-record(breakInfo, {point, status, break}). +-record(break, {mb, smi, emi, dimi, demi}). %% BUGBUG defined in dbg_wx_win +-record(winInfo, {window, % wxobj() + size, % {W, H} + find, % #find{} + m_szr, % {Panel, Sizer}, + e_szr, % {bool Shown, Sizer}, + + code, % code editor #sub{} + sb, % status bar + sg, % Search/Goto #sub{} + bs, % Buttons #sub{} subwindow info + eval, % Eval #sub{} subwindow info + bind, % Bindings #sub{} subwindow info + trace, % Trace #sub{} subwindow info + + marked_line=0, % integer() Current line + selected_line=0, % integer() Selected line + + breaks=[], % [#breakInfo{}] Known breakpoints + + editor, % {Mod, Editor} Visible code editor + editors=[] % [{Mod,Editor}] Code editors + }). + +-record(sub, {enable=true, % Subwindow is enabled + win, % Sash Sub window obj + in, % undefined or input window obj + out, % undefined or output window obj + name % name + }). + +-record(sa, {search, % Search input ctrl + goto, % Goto input ctrl + radio}). % Radio buttons + +-record(find, {start, % start pos + strlen, % search string len + found % status + }). + + +-define(StepButton, 401). +-define(NextButton, 402). +-define(ContinueButton, 403). +-define(FinishButton, 404). +-define(WhereButton, 405). +-define(UpButton, 406). +-define(DownButton, 407). + +-define(EVAL_ENTRY, 410). +-define(EVAL_LOG, 411). +-define(BIND_PANEL, 412). +-define(SEARCH_ENTRY, 413). +-define(GOTO_ENTRY, 414). + + +-define(SASH_CODE, 425). +-define(SASH_EVAL, 426). +-define(SASH_TRACE, 427). + +-define(WIN_W, 700). +-define(WIN_H, 650). + +-define(CODE_H, 400). +-define(BUTT_H, 50). % Maximum +-define(EVAL_H, 200). +-define(BIND_H, 200). +-define(TRACE_H, 100). + +%%==================================================================== +%% External exports +%%==================================================================== + +%%-------------------------------------------------------------------- +%% init() -> GS +%% GS = term() +%%-------------------------------------------------------------------- +init() -> + dbg_wx_win:init(). + +stop(#winInfo{window=Win}) -> + (catch wxFrame:destroy(Win)), + ok. + +%%-------------------------------------------------------------------- +%% create_win(GS, Title, TraceWin, Menus) -> #winInfo{} +%% GS = gsobj() +%% Title = string() +%% TraceWin = [WinArea] +%% WinArea = 'Button|Evaluator|Bindings|Trace Area' +%% Menus = [menu()] See dbg_wx_win.erl +%%-------------------------------------------------------------------- +create_win(Parent, Title, Windows, Menus) -> + Do = + fun() -> + Win = wxFrame:new(Parent, ?wxID_ANY, dbg_wx_win:to_string(Title), + [{size, {?WIN_W,?WIN_H}}]), + Panel = wxPanel:new(Win, [{size, {?WIN_W,?WIN_H}}]), + MenuBar = wxMenuBar:new(), + dbg_wx_win:create_menus(MenuBar, Menus, Win, 1), + wxFrame:setMenuBar(Win, MenuBar), + + Sizer = wxBoxSizer:new(?wxVERTICAL), + Code = code_area(Panel), + wxSizer:add(Sizer, Code#sub.win, + [{proportion,1}, {border, 2}, + {flag, ?wxEXPAND bor ?wxDOWN}]), + wxSizer:setVirtualSizeHints(Sizer, Code#sub.win), + + ExpandWithBorder = [{border, 3},{flag,?wxEXPAND bor ?wxALL}], + Search = search_area(Panel), + wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder), + Bs = button_area(Panel), + wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder), + + InfoArea = wxBoxSizer:new(?wxHORIZONTAL), + wxSizer:setMinSize(InfoArea, {100, ?EVAL_H}), + Eval = eval_area(Panel), + wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]), + Bind = bind_area(Panel), + wxSizer:add(InfoArea, Bind#sub.win, + [{proportion,1},{border, 2}, + {flag,?wxEXPAND bor ?wxLEFT}]), + wxSizer:add(Sizer, InfoArea, ExpandWithBorder), + + Trace = trace_area(Panel), + wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder), + SB = wxFrame:createStatusBar(Win,[]), + + %% Note id and lastId to get the event when it dragged is complete + wxFrame:connect(Win, sash_dragged, [{id,?SASH_CODE}, + {lastId,?SASH_TRACE}]), + wxFrame:connect(Win, close_window, [{skip, true}]), + wxFrame:connect(Win, size, [{skip, true}]), + wxWindow:connect(Win, key_up, [{skip,true}]), + wxWindow:setFocus(Code#sub.out), + + Wi0 = #winInfo{window=Win, + m_szr={Panel, Sizer}, + e_szr={true, InfoArea}, + code=Code, sb=SB, sg=Search, bs=Bs, + eval=Eval, trace=Trace, bind=Bind, + editor={'$top', Code#sub.out}, + editors=[{'$top', Code#sub.out}]}, + + Wi = show_windows(enable_windows(Wi0,Windows)), + wxWindow:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Win), + wxSizer:setSizeHints(Sizer,Win), + + IconFile = dbg_wx_win:find_icon("erlang_bug.png"), + Icon = wxIcon:new(IconFile, [{type,?wxBITMAP_TYPE_PNG}]), + wxFrame:setIcon(Win, Icon), + wxIcon:destroy(Icon), + + wxFrame:show(Win), + put(window, Win), + Wi + end, + + try wx:batch(Do) + catch E:R -> + io:format("Crashed ~p ~p",[E,R]), + erlang:error(E) + end. + + +%%-------------------------------------------------------------------- +%% get_window(WinInfo) -> Window +%% WinInfo = #winInfo{} +%% Window = gsobj() +%%-------------------------------------------------------------------- +get_window(WinInfo) -> + WinInfo#winInfo.window. + +%%-------------------------------------------------------------------- +%% configure(WinInfo, Windows) -> WinInfo +%% WinInfo = #winInfo{} +%% Windows = [WinArea] +%% WinArea = 'Button|Evaluator|Bindings|Trace Area' +%% Window areas should be opened or closed. +%%-------------------------------------------------------------------- +configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) -> + wx:batch(fun() -> + show_windows(Wi), + wxSizer:layout(Sizer), + %%wxWindow:setSizerAndFit(Panel,Sizer), + wxWindow:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Win), + wxSizer:setSizeHints(Sizer,Win), + Wi + end). + +configure(Wi0=#winInfo{window=Win,m_szr={Panel,Sizer}}, Windows) -> + wx:batch(fun() -> + Wi = enable_windows(Wi0, Windows), + show_windows(Wi), + wxSizer:layout(Sizer), + wxWindow:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Win), + wxSizer:setSizeHints(Sizer,Win), + Wi + end). + +enable_windows(Wi=#winInfo{e_szr={_,InfoArea},bs=Bs0,sg=SG0, + eval=Eval0,trace=Trace0,bind=Bind0},Windows) -> + Subs = [Window#sub{enable=lists:member(Window#sub.name,Windows)} + || Window <- [SG0,Bs0,Eval0,Trace0,Bind0]], + [SG, Bs,Eval,Trace,Bind] = Subs, + ESzr = Eval#sub.enable orelse Bind#sub.enable, + Wi#winInfo{e_szr={ESzr, InfoArea},sg=SG,bs=Bs, + eval=Eval,trace=Trace,bind=Bind}. + + +show_windows(Wi=#winInfo{m_szr={_,Sizer}, e_szr={_,InfoArea},bs=Bs,sg=SG, + eval=Eval,trace=Trace,bind=Bind}) -> + case SG#sub.enable of + false -> wxSizer:hide(Sizer, SG#sub.win); + _ -> wxSizer:show(Sizer, SG#sub.win) + end, + case Bs#sub.enable of + false -> wxSizer:hide(Sizer, Bs#sub.win); + _ -> wxSizer:show(Sizer, Bs#sub.win) + end, + if (not Eval#sub.enable) andalso (not Bind#sub.enable) -> + wxSizer:hide(Sizer, InfoArea); + not Eval#sub.enable -> + wxSizer:show(Sizer, InfoArea), + wxSizer:hide(InfoArea, Eval#sub.win), + wxSizer:show(InfoArea, Bind#sub.win); + not Bind#sub.enable -> + [EvalSI|_] = wxSizer:getChildren(InfoArea), + wxSizerItem:setProportion(EvalSI, 1), + wxSizer:show(Sizer, InfoArea), + wxSizer:hide(InfoArea, Bind#sub.win), + wxSizer:show(InfoArea, Eval#sub.win), + true; + true -> + wxSizer:show(Sizer, InfoArea), + wxSizer:show(InfoArea, Eval#sub.win), + wxSizer:show(InfoArea, Bind#sub.win) + end, + case Trace#sub.enable of + false -> wxSizer:hide(Sizer, Trace#sub.win); + _ -> wxSizer:show(Sizer, Trace#sub.win) + end, + Wi. + +%%-------------------------------------------------------------------- +%% enable([MenuItem], Bool) +%% is_enabled(MenuItem) -> Bool +%% MenuItem = atom() +%% Bool = boolean() +%%-------------------------------------------------------------------- +enable(MenuItems, Bool) -> + wx:foreach(fun(MenuItem) -> + MI = get(MenuItem), + wxMenuItem:enable(MI, [{enable, Bool}]), + case is_button(MenuItem) of + {true, ButtonId} -> + Parent = get(window), + Butt = wxWindow:findWindowById(ButtonId, + [{parent, Parent}]), + case wx:is_null(Butt) of + true -> ignore; + false -> + wxButton:enable(Butt, [{enable, Bool}]) + end; + _ -> + ignore + end + end, + MenuItems). + +is_enabled(MenuItem) -> + MI = get(MenuItem), + wxMenuItem:isEnabled(MI). + +%%-------------------------------------------------------------------- +%% select(MenuItem, Bool) +%% MenuItem = atom() +%% Bool = boolean() +%%-------------------------------------------------------------------- +select(MenuItem, Bool) -> + MI = get(MenuItem), + wxMenuItem:check(MI, [{check, Bool}]). + +%%-------------------------------------------------------------------- +%% add_break(WinInfo, Name, {Point, Options}) -> WinInfo +%% WinInfo = #winInfo{} +%% Name = atom() Menu name +%% Point = {Mod, Line} +%% Options = [Status, Action, Mods, Cond] +%% Status = active | inactive +%% Action = enable | disable | delete +%% Mods = null (not used) +%% Cond = null | {Mod, Func} +%%-------------------------------------------------------------------- +add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) -> + case WinInfo#winInfo.editor of + {Mod, Editor} -> + dbg_wx_code:add_break_to_code(Editor, Line, Status); + _ -> ignore + end, + add_break_to_menu(WinInfo, Menu, Break). + +add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) -> + Break = dbg_wx_win:add_break(WinInfo#winInfo.window, Menu, Point), + dbg_wx_win:update_break(Break, Options), + BreakInfo = #breakInfo{point=Point, status=Status, break=Break}, + WinInfo#winInfo{breaks=[BreakInfo|WinInfo#winInfo.breaks]}. + +%%-------------------------------------------------------------------- +%% update_break(WinInfo, {Point, Options}) -> WinInfo +%% WinInfo = #winInfo{} +%% Point = {Mod, Line} +%% Options = [Status, Action, Mods, Cond] +%% Status = active | inactive +%% Action = enable | disable | delete +%% Mods = null (not used) +%% Cond = null | {Mod, Func} +%%-------------------------------------------------------------------- +update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) -> + case WinInfo#winInfo.editor of + {Mod, Editor} -> + dbg_wx_code:add_break_to_code(Editor, Line, Status); + _ -> ignore + end, + update_break_in_menu(WinInfo, Break). + +update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) -> + {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, + WinInfo#winInfo.breaks), + dbg_wx_win:update_break(BreakInfo#breakInfo.break, Options), + BreakInfo2 = BreakInfo#breakInfo{status=Status}, + WinInfo#winInfo{breaks=lists:keyreplace(Point, #breakInfo.point, + WinInfo#winInfo.breaks, + BreakInfo2)}. + +%%-------------------------------------------------------------------- +%% delete_break(WinInfo, Point) -> WinInfo +%% WinInfo = #winInfo{} +%% Point = {Mod, Line} +%%-------------------------------------------------------------------- +delete_break(WinInfo, {Mod,Line}=Point) -> + case WinInfo#winInfo.editor of + {Mod, Editor} -> dbg_wx_code:del_break_from_code(Editor, Line); + _ -> ignore + end, + delete_break_from_menu(WinInfo, Point). + +delete_break_from_menu(WinInfo, Point) -> + {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, + WinInfo#winInfo.breaks), + dbg_wx_win:delete_break(BreakInfo#breakInfo.break), + WinInfo#winInfo{breaks=lists:keydelete(Point, #breakInfo.point, + WinInfo#winInfo.breaks)}. + +%%-------------------------------------------------------------------- +%% clear_breaks(WinInfo) -> WinInfo +%% clear_breaks(WinInfo, Mod) -> WinInfo +%% WinInfo = #winInfo{} +%%-------------------------------------------------------------------- +clear_breaks(WinInfo) -> + clear_breaks(WinInfo, all). +clear_breaks(WinInfo, Mod) -> + Remove = if + Mod==all -> WinInfo#winInfo.breaks; + true -> + lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> + if + Mod2==Mod -> true; + true -> false + end + end, + WinInfo#winInfo.breaks) + end, + lists:foreach(fun(#breakInfo{point=Point}) -> + delete_break(WinInfo, Point) + end, + Remove), + Remain = WinInfo#winInfo.breaks -- Remove, + WinInfo#winInfo{breaks=Remain}. + +%%-------------------------------------------------------------------- +%% display(Arg) +%% Arg = idle | {Status,Mod,Line} | {running,Mod} +%% � {exit,Where,Reason} | {text,Text} +%% Status = break | wait � Level +%% Level = int() +%% Mod = atom() +%% Line = integer() +%% Where = {Mod,Line} | null +%% Reason = term() +%% Text = string() +%%-------------------------------------------------------------------- +display(#winInfo{window=Win, sb=Sb},Arg) -> + Str = case Arg of + idle -> "State: uninterpreted"; + {exit, {Mod,Line}, Reason} -> + wxWindow:raise(Win), + dbg_wx_win:to_string("State: EXITED [~w.erl/~w], Reason:~w", + [Mod, Line, Reason]); + {exit, null, Reason} -> + wxWindow:raise(Win), + dbg_wx_win:to_string("State: EXITED [uninterpreted], " + "Reason:~w", [Reason]); + {Level, null, _Line} when is_integer(Level) -> + dbg_wx_win:to_string("*** Call level #~w " + "(in non-interpreted code)", + [Level]); + {Level, Mod, Line} when is_integer(Level) -> + dbg_wx_win:to_string("*** Call level #~w [~w.erl/~w]", + [Level, Mod, Line]); + {Status, Mod, Line} -> + What = case Status of + wait -> 'receive'; + _ -> Status + end, + dbg_wx_win:to_string("State: ~w [~w.erl/~w]", + [What, Mod, Line]); + {running, Mod} -> + dbg_wx_win:to_string("State: running [~w.erl]", [Mod]); + {text, Text} -> dbg_wx_win:to_string(Text) + end, + wxStatusBar:setStatusText(Sb, Str). + +%%-------------------------------------------------------------------- +%% is_shown(WinInfo, Mod) -> {true, WinInfo} | false +%% show_code(WinInfo, Mod, Contents) -> WinInfo +%% show_no_code(WinInfo) -> WinInfo +%% remove_code(WinInfo, Mod) -> WinInfo +%% WinInfo = #winInfo{} +%% Mod = atom() +%% Contents = string() +%% Note: remove_code/2 should not be used for currently shown module. +%%-------------------------------------------------------------------- +is_shown(WinInfo, Mod) -> + case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of + {value, {Mod, Editor}} -> + gs:config(Editor, raise), %% BUGBUG + {true, WinInfo#winInfo{editor={Mod, Editor}}}; + false -> false + end. + +show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) -> + %% Insert code and update breakpoints, if any + dbg_wx_code:load_code(Ed, Contents), + + lists:foreach(fun(BreakInfo) -> + case BreakInfo#breakInfo.point of + {Mod2, Line} when Mod2==Mod -> + Status = BreakInfo#breakInfo.status, + dbg_wx_code:add_break_to_code(Ed, Line,Status); + _Point -> ignore + end + end, + WinInfo#winInfo.breaks), + + WinInfo#winInfo{editor={Mod,Ed},find=undefined}. + +show_no_code(WinInfo = #winInfo{editor={_, Ed}}) -> + dbg_wx_code:unload_code(Ed), + WinInfo#winInfo{editor={'$top', Ed}}. + +remove_code(WinInfo, _Mod) -> + WinInfo. + +%%-------------------------------------------------------------------- +%% mark_line(WinInfo, Line, How) -> WinInfo +%% WinInfo = #winInfo{} +%% Line = integer() +%% How = break | where +%% Mark the code line where the process is executing. +%%-------------------------------------------------------------------- +mark_line(WinInfo = #winInfo{editor={_,Ed}}, Line, _How) -> + dbg_wx_code:mark_line(Ed, WinInfo#winInfo.marked_line, Line), + WinInfo#winInfo{marked_line=Line}. + +unmark_line(WinInfo) -> + mark_line(WinInfo, 0, false). + + +%%-------------------------------------------------------------------- +%% select_line(WinInfo, Line) -> WinInfo +%% selected_line(WinInfo) -> undefined | Line +%% WinInfo = #winInfo{} +%% Line = integer() +%% Select/unselect a line (unselect if Line=0). +%%-------------------------------------------------------------------- +select_line(WinInfo, Line) -> + {_Mod, Ed} = WinInfo#winInfo.editor, + + %% Since 'Line' may be specified by the user in the 'Go To Line' + %% help window, it must be checked that it is correct + Size = dbg_wx_code:get_no_lines(Ed), + if + Line==0 -> + dbg_wx_code:goto_line(Ed,1), + WinInfo#winInfo{selected_line=0}; + Line<Size -> + dbg_wx_code:goto_line(Ed,Line), + WinInfo#winInfo{selected_line=Line}; + true -> + WinInfo + end. + +selected_line(#winInfo{editor={_,Ed}}) -> + wxStyledTextCtrl:getCurrentLine(Ed)+1. + +%%-------------------------------------------------------------------- +%% eval_output(winInfo{}, Str, Face) +%% Str = string() +%% Face = normal | bold +%%-------------------------------------------------------------------- +eval_output(#winInfo{eval=#sub{out=Log}}, Text, _Face) -> + wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)), + ok. + +%%-------------------------------------------------------------------- +%% update_bindings(Bs) +%% Bs = [{Var,Val}] +%%-------------------------------------------------------------------- +update_bindings(#winInfo{bind=#sub{out=BA}}, Bs) -> + wxListCtrl:deleteAllItems(BA), + wx:foldl(fun({Var,Val},Row) -> + wxListCtrl:insertItem(BA, Row, ""), + wxListCtrl:setItem(BA, Row, 0, dbg_wx_win:to_string(Var)), + wxListCtrl:setItem(BA, Row, 1, dbg_wx_win:to_string("~200p",[Val])), + Row+1 + end, 0, Bs), + put(bindings,Bs), + ok. + +%%-------------------------------------------------------------------- +%% trace_output(Str) +%% Str = string() +%%-------------------------------------------------------------------- +trace_output(#winInfo{trace=#sub{out=Log}}, Text) -> + wxTextCtrl:appendText(Log, dbg_wx_win:to_string(Text)), + ok. + +%%-------------------------------------------------------------------- +%% handle_event(GSEvent, WinInfo) -> Command +%% GSEvent = {gs, Id, Event, Data, Arg} +%% WinInfo = #winInfo{} +%% Command = ignore +%% | {win, WinInfo} +%% | stopped +%% | {coords, {X,Y}} +%% +%% | {shortcut, Key} +%% | MenuItem | {Menu, [MenuItem]} +%% MenuItem = Menu = atom() +%% | {break, Point, What} +%% What = add | delete | {status,Status} |{trigger,Trigger} +%% | {module, Mod, view} +%% +%% | {user_command, Cmd} +%% +%% | {edit, {Var, Val}} +%%-------------------------------------------------------------------- +%% Window events +handle_event(_Ev=#wx{event=#wxClose{}}, _WinInfo) -> + stopped; + +handle_event(#wx{event=#wxSize{size=Size}}, Wi0) -> + Wi = Wi0#winInfo{size=Size}, + resize(Wi), + {win, Wi}; + +handle_event(#wx{event=#wxSash{dragStatus=?wxSASH_STATUS_OUT_OF_RANGE}},_Wi) -> + ignore; +handle_event(#wx{id=?SASH_CODE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) -> + #winInfo{code=Code,m_szr={_,Sizer},e_szr={Enable,InfoSzr},trace=Trace} = Wi, + + case Enable orelse Trace#sub.enable of + false -> + ignore; + true -> + {_, CMH} = wxWindow:getMinSize(Code#sub.win), + case CMH > H of + true -> wxSashWindow:setMinSize(Code#sub.win, {500, H}); + _ -> ignore + end, + {_, CH} = wxWindow:getSize(Code#sub.win), + Change = CH - H, + ChangeH = fun(Item) -> + {ItemW, ItemH} = wxSizerItem:getMinSize(Item), + wxSizerItem:setInitSize(Item, ItemW, max(ItemH+Change,-1)) + end, + if Enable -> + {IW, IH} = wxSizer:getMinSize(InfoSzr), + [ChangeH(Child) || Child <- wxSizer:getChildren(InfoSzr)], + wxSizer:setMinSize(InfoSzr, {IW, IH+Change}), + ok; + Trace#sub.enable -> + {TW, TH} = wxWindow:getMinSize(Trace#sub.win), + wxWindow:setMinSize(Trace#sub.win, {TW, TH+Change}), + ok + end, + wxSizer:layout(Sizer), + ignore + end; + +handle_event(#wx{id=?SASH_EVAL, event=#wxSash{dragRect={_X,_Y,W,_H}}}, Wi) -> + #winInfo{m_szr={_,Sizer},e_szr={Enable,InfoSzr}, + eval=#sub{enable=Enable, win=EvalSzr}} = Wi, + case Enable of + false -> + ignore; + true -> + [Eval,Bind] = wxSizer:getChildren(InfoSzr), + {Tot,_} = wxSizer:getSize(InfoSzr), + EvalWidth = Tot-W, + + Change = fun(Szr, Width) -> + {_EW,EH} = wxSizerItem:getMinSize(Szr), + wxSizerItem:setInitSize(Szr, Width, EH) + end, + + Change(Eval, EvalWidth), + [Change(Kid, EvalWidth) || Kid <- wxSizer:getChildren(EvalSzr)], + Change(Bind, W), + + wxSizerItem:setProportion(Eval, 0), + wxSizer:layout(InfoSzr), + wxSizer:layout(Sizer), + + resize(Wi), + ignore + end; + +handle_event(#wx{id=?SASH_TRACE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) -> + #winInfo{code=Code,m_szr={_,Sizer},e_szr={Enable,InfoSzr},trace=Trace} = Wi, + {TW, TH} = wxWindow:getSize(Trace#sub.win), + Change = TH - H, + case Enable of + false -> %% Eval Area or Bindings + {_, CH} = wxWindow:getSize(Code#sub.win), + {_, CMH} = wxWindow:getMinSize(Code#sub.win), + case CMH > CH+Change of + true -> wxSashWindow:setMinSize(Code#sub.win, {500, CH+Change}); + _ -> ignore + end, + wxWindow:setMinSize(Trace#sub.win, {TW, H}), + wxSizer:layout(Sizer), + ignore; + true -> %% Change the Eval and Bindings area + ChangeH = fun(Item) -> + {ItemW, ItemH} = wxSizerItem:getMinSize(Item), + wxSizerItem:setInitSize(Item, ItemW, max(ItemH+Change,-1)) + end, + {IW, IH} = wxSizer:getMinSize(InfoSzr), + [ChangeH(Child) || Child <- wxSizer:getChildren(InfoSzr)], + Wanted = IH+Change, + wxSizer:setMinSize(InfoSzr, {IW, Wanted}), + {_,RH} = wxSizer:getMinSize(InfoSzr), + case RH > Wanted of + true -> %% Couldn't get the size we wanted try adjusting the code area + {_, CH} = wxWindow:getSize(Code#sub.win), + {_, CMH} = wxWindow:getMinSize(Code#sub.win), + CC = CH - (RH-Wanted), + case CMH > CC of + true when CC > 50 -> + wxWindow:setMinSize(Trace#sub.win, {TW, H}), + wxSashWindow:setMinSize(Code#sub.win, {500, CC}); + _ when CC < 50 -> + ignore; + _ -> + wxWindow:setMinSize(Trace#sub.win, {TW, H}) + end, + ok; + false -> + wxWindow:setMinSize(Trace#sub.win, {TW, H}) + end, + wxSizer:layout(Sizer), + ignore + end; + +%% Menus, buttons and keyboard shortcuts +handle_event(_Ev = #wx{event=#wxKey{keyCode=Key, controlDown=true}}, _WinInfo) -> + %% io:format("Key ~p ~n",[_Ev]), + if + Key/=?WXK_UP, Key/=?WXK_DOWN, Key /=? WXK_RETURN -> + try + {shortcut, list_to_atom([Key+($a-$A)])} + catch _:_ -> ignore + end; + true -> + ignore + end; +handle_event(#wx{userData={dbg_ui_winman, Win}, + event=#wxCommand{type=command_menu_selected}}, _WinInfo) -> + dbg_wx_winman:raise(Win), + ignore; + +handle_event(#wx{userData={break, Point, status}, + event=#wxCommand{type=command_menu_selected}}, + WinInfo) -> + {value, BreakInfo} = lists:keysearch(Point, #breakInfo.point, + WinInfo#winInfo.breaks), + %% This is a temporary hack !! + #breakInfo{break=#break{smi=Smi}} = BreakInfo, + + case wxMenuItem:getText(Smi) of + "Enable" -> {break, Point, {status, active}}; + "Disable" -> {break, Point, {status, inactive}} + end; + +handle_event(#wx{userData=Data, + event=_Cmd=#wxCommand{type=command_menu_selected}}, + _WinInfo) -> + %%io:format("Command ~p ~p~n",[Data,_Cmd]), + Data; + +%% Code area +handle_event(#wx{event=#wxStyledText{type=stc_doubleclick}}, + WinInfo = #winInfo{editor={Mod,Ed}}) -> + Line = wxStyledTextCtrl:getCurrentLine(Ed), + Point = {Mod, Line+1}, + case lists:keysearch(Point, #breakInfo.point,WinInfo#winInfo.breaks) of + {value, _BreakInfo} -> {break, Point, delete}; + false -> {break, Point, add} + end; + +%% Search Area +handle_event(#wx{id=?GOTO_ENTRY, event=#wxCommand{cmdString=Str}}, WinInfo) -> + try + Line = list_to_integer(Str), + {gotoline, Line} + catch + _:_ -> + display(WinInfo, {text,"Not a line number"}), + ignore + end; +handle_event(#wx{id=?SEARCH_ENTRY, event=#wxFocus{}}, Wi) -> + {win, Wi#winInfo{find=undefined}}; +handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{type=command_text_enter,cmdString=Str}}, + Wi = #winInfo{code=Code,find=Find, sg=#sub{in=#sa{radio={NextO,_,CaseO}}}}) + when Find =/= undefined -> + Dir = wxRadioButton:getValue(NextO) xor wx_misc:getKeyState(?WXK_SHIFT), + Case = wxCheckBox:getValue(CaseO), + Pos = if Find#find.found, Dir -> %% Forward Continuation + wxStyledTextCtrl:getAnchor(Code#sub.out); + Find#find.found -> %% Backward Continuation + wxStyledTextCtrl:getCurrentPos(Code#sub.out); + Dir -> %% Forward wrap + 0; + true -> %% Backward wrap + wxStyledTextCtrl:getLength(Code#sub.out) + end, + dbg_wx_code:goto_pos(Code#sub.out,Pos), + case dbg_wx_code:find(Code#sub.out, Str, Case, Dir) of + true -> + display(Wi, {text,""}), + {win, Wi#winInfo{find=Find#find{found=true}}}; + false -> + display(Wi, {text,"Not found (Hit Enter to wrap search)"}), + {win, Wi#winInfo{find=Find#find{found=false}}} + end; +handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=""}}, + Wi=#winInfo{code=Code}) -> + %% Reset search (and selection pos) + Pos = dbg_wx_code:current_pos(Code#sub.out), + dbg_wx_code:goto_pos(Code#sub.out,Pos), + {win, Wi#winInfo{find=undefined}}; +handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}}, + Wi = #winInfo{code=Code,find=Find, + sg=#sub{in=#sa{radio={NextO,_,CaseO}}}}) -> + Dir = wxRadioButton:getValue(NextO), + Case = wxCheckBox:getValue(CaseO), + + Cont = case Find of + undefined -> + Pos = dbg_wx_code:current_pos(Code#sub.out), + #find{start=Pos, strlen=length(Str)}; + #find{strlen=Old} when Old < length(Str) -> + Find#find{strlen=length(Str)}; + _ -> + dbg_wx_code:goto_pos(Code#sub.out,Find#find.start), + Find#find{strlen=length(Str)} + end, + case dbg_wx_code:find(Code#sub.out, Str, Case, Dir) of + true -> + display(Wi, {text,""}), + {win, Wi#winInfo{find=Cont#find{found=true}}}; + false -> + display(Wi, {text,"Not found (Hit Enter to wrap search)"}), + {win, Wi#winInfo{find=Cont#find{found=false}}} + end; + +%% Button area +handle_event(#wx{id=ID, event=#wxCommand{type=command_button_clicked}},_Wi) -> + {value, {Button, _}} = lists:keysearch(ID, 2, buttons()), + Button; + +%% Evaluator area +handle_event(#wx{id=?EVAL_ENTRY, event=#wxCommand{type=command_text_enter}}, + Wi = #winInfo{eval=#sub{in=TC}}) -> + case wxTextCtrl:getValue(TC) of + [10] -> + eval_output(Wi, "\n", normal), + ignore; + Cmd -> + eval_output(Wi, [$>, Cmd, 10], normal), + wxTextCtrl:setValue(TC,""), + {user_command, Cmd} + end; + +%% Bindings area +handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}},Wi) -> + Bs = get(bindings), + {Var,Val} = lists:nth(Row+1, Bs), + Str = io_lib:format("< ~s = ~p~n", [Var, Val]), + eval_output(Wi, Str, bold), + ignore; +handle_event(#wx{event=#wxList{type=command_list_item_activated, itemIndex=Row}},_Wi) -> + Bs = get(bindings), + Binding = lists:nth(Row+1, Bs), + {edit, Binding}; + +handle_event(_GSEvent, _WinInfo) -> + %%io:format("~p: unhandled ~p~n",[?MODULE, _GSEvent]), + ignore. + + +%%==================================================================== +%% resize(WinInfo) -> WinInfo + +resize(#winInfo{bind=Bind}) -> + %% Tweak the Binding settings text size + if + Bind#sub.enable =:= false -> + ok; + Bind#sub.enable -> + {EW, _} = wxWindow:getClientSize(Bind#sub.out), + B0W = wxListCtrl:getColumnWidth(Bind#sub.out, 0), + wxListCtrl:setColumnWidth(Bind#sub.out, 1, EW - B0W), + ok + end. + +%%==================================================================== +%% Internal functions +%%==================================================================== + +%%--Code Area------------------------------------------------------- +code_area(Win) -> + CodeWin = wxSashWindow:new(Win, [{id,?SASH_CODE}, + {size, {?WIN_W,?CODE_H}}, + {style, ?wxSW_3D}]), + Code = dbg_wx_code:code_area(CodeWin), + wxSashWindow:setSashVisible(CodeWin, ?wxSASH_BOTTOM, true), + wxWindow:setMinSize(CodeWin, {600, ?CODE_H}), + #sub{name='Code Area',enable=true, win=CodeWin, out=Code}. + + +%%--Button Area------------------------------------------------------- + +buttons() -> + [{'Step',?StepButton}, {'Next',?NextButton}, + {'Continue',?ContinueButton}, {'Finish',?FinishButton}, + {'Where',?WhereButton}, {'Up',?UpButton}, {'Down',?DownButton}]. + +is_button(Name) -> + case lists:keysearch(Name, 1, buttons()) of + {value, {Name, Button}} -> {true, Button}; + false -> false + end. + +button_area(Parent) -> + Sz = wxBoxSizer:new(?wxHORIZONTAL), + wx:foreach(fun({Name, Button}) -> + B=wxButton:new(Parent, Button, + [{label,dbg_wx_win:to_string(Name)}]), + Id = wxWindow:getId(B), + wxSizer:add(Sz,B, []), + wxButton:connect(B, command_button_clicked, [{id,Id}]) + end, buttons()), + #sub{name='Button Area', win=Sz}. + +%%--Search/Goto Area------------------------------------------------- + +search_area(Parent) -> + HSz = wxBoxSizer:new(?wxHORIZONTAL), + wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), + [{flag,?wxALIGN_CENTER_VERTICAL}]), + TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), + wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]), + Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"), + wxRadioButton:setValue(Nbtn, true), + wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + Pbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Previous"), + wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"), + wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]), + wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), + [{flag,?wxALIGN_CENTER_VERTICAL}]), + TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), + wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]), + wxTextCtrl:connect(TC1, command_text_updated), + wxTextCtrl:connect(TC1, command_text_enter), + wxTextCtrl:connect(TC1, kill_focus), + wxTextCtrl:connect(TC2, command_text_enter), + wxWindow:connect(Parent, command_button_clicked), + + #sub{name='Search Area', win=HSz, + in=#sa{search=TC1,goto=TC2,radio={Nbtn,Pbtn,Cbtn}}}. + +%%--Evaluator Area---------------------------------------------------- + +eval_area(Parent) -> + VSz = wxBoxSizer:new(?wxVERTICAL), + HSz = wxBoxSizer:new(?wxHORIZONTAL), + + wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"), + [{flag,?wxALIGN_CENTER_VERTICAL}]), + TC = wxTextCtrl:new(Parent, ?EVAL_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), + wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]), + wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]), + TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style, ?wxTE_DONTWRAP bor + ?wxTE_MULTILINE bor ?wxTE_READONLY}]), + wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]), + + wxTextCtrl:connect(TC, command_text_enter), + #sub{name='Evaluator Area', win=VSz, in=TC, out=TL}. + +%%--Bindings Area----------------------------------------------------- + +bind_area(Parent) -> + Style = {style, ?wxSW_3D bor ?wxCLIP_CHILDREN}, + Win = wxSashWindow:new(Parent, [{id, ?SASH_EVAL},Style]), + wxSashWindow:setSashVisible(Win, ?wxSASH_LEFT, true), + + BA = wxListCtrl:new(Win, [{style, ?wxLC_REPORT bor ?wxLC_SINGLE_SEL}]), + LI = wxListItem:new(), + + wxListItem:setText(LI, "Name"), + wxListItem:setAlign(LI, ?wxLIST_FORMAT_LEFT), + wxListCtrl:insertColumn(BA, 0, LI), + + wxListItem:setText(LI, "Value"), + wxListCtrl:insertColumn(BA, 1, LI), + wxListItem:destroy(LI), + + wxListCtrl:setColumnWidth(BA, 0, 100), + wxListCtrl:setColumnWidth(BA, 1, 150), + wxListCtrl:connect(BA, command_list_item_selected), + wxListCtrl:connect(BA, command_list_item_activated), + + #sub{name='Bindings Area', win=Win, out=BA}. + +%%--Trace Area-------------------------------------------------------- + +trace_area(Parent) -> + Style = {style, ?wxSW_3D bor ?wxCLIP_CHILDREN}, + Win = wxSashWindow:new(Parent, [{id, ?SASH_TRACE}, + {size, {?WIN_W,?TRACE_H}}, Style]), + wxSashWindow:setSashVisible(Win, ?wxSASH_TOP, true), + wxWindow:setMinSize(Win, {500, ?TRACE_H}), + TC = wxTextCtrl:new(Win, ?wxID_ANY, [{style, ?wxTE_MULTILINE bor ?wxTE_READONLY}]), + #sub{name='Trace Area', win=Win, out=TC}. + +%%==================================================================== +%% 'Go To Line' and 'Search' help windows +%%==================================================================== + +helpwin(Type, WinInfo = #winInfo{sg=Sg =#sub{in=Sa}}) -> + Wi = case Sg#sub.enable of + false -> configure(WinInfo#winInfo{sg=Sg#sub{enable=true}}); + true -> WinInfo + end, + case Type of + gotoline -> wxWindow:setFocus(Sa#sa.goto); + search -> wxWindow:setFocus(Sa#sa.search) + end, + Wi. + +max(X,Y) when X > Y -> X; +max(_,Y) -> Y. + + + |