From b65b0c4bf4896021cbd327a4bc1acd426645a5f3 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 11 Nov 2011 15:00:11 +0100 Subject: [observer] Rework tracing part of the gui No tracing is implemented yet. --- lib/observer/src/observer_defs.hrl | 13 +- lib/observer/src/observer_lib.erl | 4 + lib/observer/src/observer_pro_wx.erl | 110 +- lib/observer/src/observer_trace_wx.erl | 668 +++++---- lib/observer/src/observer_traceoptions_wx.erl | 1971 +++++++++++++------------ lib/observer/src/observer_tv_table.erl | 2 +- lib/observer/src/observer_tv_wx.erl | 2 +- lib/observer/src/observer_wx.erl | 46 +- 8 files changed, 1506 insertions(+), 1310 deletions(-) (limited to 'lib') diff --git a/lib/observer/src/observer_defs.hrl b/lib/observer/src/observer_defs.hrl index 9e68bd7f07..d83a1e2fa5 100644 --- a/lib/observer/src/observer_defs.hrl +++ b/lib/observer/src/observer_defs.hrl @@ -26,10 +26,12 @@ on_all_link = false, main_window = true}). --record(match_spec, {alias, - term_ms = [], - str_ms = [], - fun2ms}). +-record(match_spec, {name = "", + term = [], + str = [], + func = ""}). + +-record(tpattern, {m, fa, ms}). -record(traced_func, {func_name, %atom arity, %integer @@ -49,9 +51,12 @@ }). -record(attrs, {even, odd, deleted, changed, searched}). +-define(EVEN(Row), ((Row rem 2) =:= 0)). -define(BG_EVEN, {230,230,250}). -define(BG_ODD, {255,255,255}). -define(BG_DELETED, {100,100,100}). -define(FG_DELETED, {240,30,30}). -define(BG_SEARCHED,{235,215,90}). -define(BG_CHANGED, {230,230,250}). + +-define(LCTRL_WDECR, 4). %% Remove some pixels in column width to avoid creating unnecessary scrollbar diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index b3a2dcf5ec..90c270e977 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -170,6 +170,10 @@ to_str({time_ms, MS}) -> true -> integer_to_list(S) ++ " Secs" end; +to_str({func, {F,A}}) when is_atom(F), is_integer(A) -> + lists:concat([F, "/", A]); +to_str({func, {F,'_'}}) when is_atom(F) -> + atom_to_list(F); to_str({A, B}) when is_atom(A), is_atom(B) -> lists:concat([A, ":", B]); to_str({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 1c0b3ed5ec..cfc1c0665f 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -71,10 +71,7 @@ panel, popup_menu, parent_notebook, - trace_options=#trace_options{}, - match_specs=[], timer, - tracemenu_opened, procinfo_menu_pids=[], sel={[], []}, holder}). @@ -88,9 +85,7 @@ init([Notebook, Parent]) -> Self = self(), Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end), {ProPanel, State} = setup(Notebook, Parent, Holder), - MatchSpecs = generate_matchspecs(), - - {ProPanel, State#state{holder=Holder, match_specs=MatchSpecs}}. + {ProPanel, State#state{holder=Holder}}. setup(Notebook, Parent, Holder) -> ProPanel = wxPanel:new(Notebook, []), @@ -105,41 +100,16 @@ setup(Notebook, Parent, Holder) -> Popup = create_popup_menu(ProPanel), - State = #state{parent=Parent, - grid=Grid, - panel=ProPanel, - popup_menu=Popup, - parent_notebook=Notebook, - tracemenu_opened=false, - holder=Holder, - timer={false, 10} + State = #state{parent=Parent, + grid=Grid, + panel=ProPanel, + popup_menu=Popup, + parent_notebook=Notebook, + holder=Holder, + timer={false, 10} }, {ProPanel, State}. -generate_matchspecs() -> - try - StrMs1 = "[{'_', [], [{return_trace}]}].", - StrMs2 = "[{'_', [], [{exception_trace}]}].", - StrMs3 = "[{'_', [], [{message, {caller}}]}].", - StrMs4 = "[{'_', [], [{message, {process_dump}}]}].", - - {ok, Tokens1, _} = erl_scan:string(StrMs1), - {ok, Tokens2, _} = erl_scan:string(StrMs2), - {ok, Tokens3, _} = erl_scan:string(StrMs3), - {ok, Tokens4, _} = erl_scan:string(StrMs4), - {ok, Term1} = erl_parse:parse_term(Tokens1), - {ok, Term2} = erl_parse:parse_term(Tokens2), - {ok, Term3} = erl_parse:parse_term(Tokens3), - {ok, Term4} = erl_parse:parse_term(Tokens4), - - [#match_spec{term_ms = Term1, str_ms = StrMs1}, - #match_spec{term_ms = Term2, str_ms = StrMs2}, - #match_spec{term_ms = Term3, str_ms = StrMs3}, - #match_spec{term_ms = Term4, str_ms = StrMs4}] - catch - _:_ -> - [] - end. %% UI-creation @@ -155,8 +125,9 @@ create_pro_menu(Parent, Holder) -> #create_menu{id=?ID_REFRESH_INTERVAL, text="Refresh Interval"}]}, {"Trace", [#create_menu{id=?ID_TRACEMENU, text="Trace selected processes"}, - #create_menu{id=?ID_TRACE_NEW_MENU, text="Trace new processes"}, - #create_menu{id=?ID_TRACE_ALL_MENU, text="Trace all processes"}]} + #create_menu{id=?ID_TRACE_NEW_MENU, text="Trace new processes"} + %% , #create_menu{id=?ID_TRACE_ALL_MENU, text="Trace all processes"} + ]} ], observer_wx:create_menus(Parent, MenuEntries). @@ -271,11 +242,6 @@ handle_info(refresh_interval, #state{holder=Holder}=State) -> Holder ! refresh, {noreply, State}; -handle_info({tracemenu_closed, TraceOpts, MatchSpecs}, State) -> - {noreply, State#state{tracemenu_opened=false, - trace_options=TraceOpts, - match_specs=MatchSpecs}}; - handle_info({procinfo_menu_closed, Pid}, #state{procinfo_menu_pids=Opened}=State) -> NewPids = lists:delete(Pid, Opened), @@ -365,59 +331,20 @@ handle_event(#wx{id = ?ID_PROC}, {noreply, State#state{procinfo_menu_pids=Opened2}}; handle_event(#wx{id = ?ID_TRACEMENU}, - #state{holder=Holder, - popup_menu=Pop, - trace_options=Options, - match_specs=MatchSpecs, - sel={_, Pids}, - tracemenu_opened=false, - panel=Panel}=State) -> + #state{popup_menu=Pop, sel={_, Pids}, panel=Panel}=State) -> wxWindow:show(Pop, [{show, false}]), case Pids of [] -> observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; Pids -> - Node = call(Holder, {get_node, self()}), - observer_trace_wx:start(Node, - Pids, - Options, - MatchSpecs, - Panel, - self()), - {noreply, State#state{tracemenu_opened=true}} + observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids), + {noreply, State} end; -handle_event(#wx{id=?ID_TRACE_ALL_MENU, event=#wxCommand{type=command_menu_selected}}, - #state{holder=Holder, - trace_options=Options, - match_specs=MatchSpecs, - tracemenu_opened=false, - panel=Panel}=State) -> - Node = call(Holder, {get_node, self()}), - observer_trace_wx:start(Node, - all, - Options, - MatchSpecs, - Panel, - self()), - {noreply, State#state{tracemenu_opened=true}}; - - -handle_event(#wx{id=?ID_TRACE_NEW_MENU, event=#wxCommand{type=command_menu_selected}}, - #state{holder=Holder, - trace_options=Options, - match_specs=MatchSpecs, - tracemenu_opened=false, - panel=Panel}=State) -> - Node = call(Holder, {get_node, self()}), - observer_trace_wx:start(Node, - new, - Options, - MatchSpecs, - Panel, - self()), - {noreply, State#state{tracemenu_opened=true}}; +handle_event(#wx{id=?ID_TRACE_NEW_MENU, event=#wxCommand{type=command_menu_selected}}, State) -> + observer_trace_wx:add_processes(observer_wx:get_tracer(), [new]), + {noreply, State}; handle_event(#wx{event=#wxSize{size={W,_}}}, #state{grid=Grid}=State) -> @@ -425,8 +352,9 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, Cols = wxListCtrl:getColumnCount(Grid), Last = lists:foldl(fun(I, Last) -> Last - wxListCtrl:getColumnWidth(Grid, I) - end, W-2, lists:seq(0, Cols - 2)), + end, W-Cols*3-?LCTRL_WDECR, lists:seq(0, Cols - 2)), Size = max(200, Last), + %% io:format("Width ~p ~p => ~p~n",[W, Last, Size]), wxListCtrl:setColumnWidth(Grid, Cols-1, Size) end), {noreply, State}; diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index b79358193e..23b9b1fe6b 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -18,7 +18,7 @@ -module(observer_trace_wx). --export([start/6]). +-export([start_link/2, add_processes/2]). -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_cast/2]). @@ -33,154 +33,214 @@ -define(CLEAR, 304). -define(SAVE_TRACEOPTS, 305). -define(LOAD_TRACEOPTS, 306). - - --record(state, { - parent, - frame, - text_ctrl, - trace_options, - toggle_button, - node, - traceoptions_open, - traced_procs, - traced_funcs = dict:new(), % Key =:= Module::atom, Value =:= [ #traced_func ] - match_specs = []}). % [ #match_spec{} ] - - -start(Node, TracedProcs, TraceOpts, MatchSpecs, ParentFrame, ParentPid) -> - wx_object:start(?MODULE, [Node, TracedProcs, TraceOpts, MatchSpecs, ParentFrame, ParentPid], []). - -init([Node, TracedProcs, TraceOpts, MatchSpecs, ParentFrame, ParentPid]) -> - State = - wx:batch(fun() -> - create_window(ParentFrame, TraceOpts) - end), - - Frame = State#state.frame, - TraceOpts2 = State#state.trace_options, - TracedFuncs = State#state.traced_funcs, - - wx_object:start(observer_traceoptions_wx, - [Frame, self(), Node, TraceOpts2, TracedFuncs, MatchSpecs], - []), - - {Frame, State#state{parent = ParentPid, - node = Node, - traced_procs = TracedProcs, - match_specs = MatchSpecs, - traceoptions_open = true}}. - - -create_window(ParentFrame, TraceOpts) -> +-define(TOGGLE_TRACE, 307). +-define(ADD_NEW, 308). +-define(ADD_TP, 309). +-define(PROCESSES, 350). +-define(MODULES, 351). +-define(FUNCTIONS, 352). + +-record(state, + {parent, + panel, + p_view, + m_view, + f_view, + nodes = [], + toggle_button, + tpids = [], %% #tpid + def_trace_opts = [], + tpatterns = dict:new(), % Key =:= Module::atom, Value =:= {M, F, A, MatchSpec} + match_specs = []}). % [ #match_spec{} ] + +-record(tpid, {pid, opts}). + +start_link(Notebook, ParentPid) -> + wx_object:start_link(?MODULE, [Notebook, ParentPid], []). + +add_processes(Tracer, Pids) when is_list(Pids) -> + wx_object:cast(Tracer, {add_processes, Pids}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([Notebook, ParentPid]) -> + wx:batch(fun() -> create_window(Notebook, ParentPid) end). + +create_window(Notebook, ParentPid) -> %% Create the window - Frame = wxFrame:new(ParentFrame, ?wxID_ANY, "Tracer", - [{style, ?wxDEFAULT_FRAME_STYLE}, - {size, {900, 900}}]), - wxFrame:connect(Frame, close_window,[{skip,true}]), - Panel = wxPanel:new(Frame, []), + Panel = wxPanel:new(Notebook, [{size, wxWindow:getClientSize(Notebook)}]), Sizer = wxBoxSizer:new(?wxVERTICAL), - - %% Menues - MenuBar = wxMenuBar:new(), - create_menues(MenuBar), - wxFrame:setMenuBar(Frame, MenuBar), - wxMenu:connect(Frame, command_menu_selected, []), - + Splitter = wxSplitterWindow:new(Panel, [{size, wxWindow:getClientSize(Panel)}]), + ProcessView = create_process_view(Splitter), + {MatchSpecView,ModView,FuncView} = create_matchspec_view(Splitter), + wxSplitterWindow:setSashGravity(Splitter, 0.5), + wxSplitterWindow:setMinimumPaneSize(Splitter,50), + wxSplitterWindow:splitHorizontally(Splitter, ProcessView, MatchSpecView), + wxSizer:add(Sizer, Splitter, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}, {proportion, 1}]), %% Buttons - ToggleButton = wxToggleButton:new(Panel, ?wxID_ANY, "Start Trace", []), - wxSizer:add(Sizer, ToggleButton, [{flag, ?wxALL}, - {border, 5}]), - wxMenu:connect(ToggleButton, command_togglebutton_clicked, []), - - TxtCtrl = wxTextCtrl:new(Panel, ?wxID_ANY, - [{style,?wxTE_READONLY bor - ?wxTE_MULTILINE}, - {size, {400, 300}}]), - - wxSizer:add(Sizer, TxtCtrl, [{proportion, 1}, - {flag, ?wxEXPAND}]), - - %% Display window + Buttons = wxBoxSizer:new(?wxHORIZONTAL), + ToggleButton = wxToggleButton:new(Panel, ?TOGGLE_TRACE, "Start Trace", []), + wxSizer:add(Buttons, ToggleButton), + New = wxButton:new(Panel, ?ADD_NEW, [{label, "Trace New Processes"}]), + wxSizer:add(Buttons, New), + ATP = wxButton:new(Panel, ?ADD_TP, [{label, "Add Trace Pattern"}]), + wxSizer:add(Buttons, ATP), + wxMenu:connect(Panel, command_togglebutton_clicked, []), + wxMenu:connect(Panel, command_button_clicked, []), + wxSizer:add(Sizer, Buttons, [{flag, ?wxALL},{border, 2}, {proportion,0}]), wxWindow:setSizer(Panel, Sizer), - wxFrame:show(Frame), - #state{frame = Frame, - text_ctrl = TxtCtrl, - toggle_button = ToggleButton, - trace_options = TraceOpts#trace_options{main_window = false}}. - -create_menues(MenuBar) -> + {Panel, #state{parent=ParentPid, panel=Panel, + p_view=ProcessView, m_view=ModView, f_view=FuncView, + match_specs=default_matchspecs()}}. + +default_matchspecs() -> + Ms = [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"}, + {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"}, + {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"}, + {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}], + [make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms]. + +create_process_view(Parent) -> + Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES, + Grid = wxListCtrl:new(Parent, [{winid, ?PROCESSES}, {style, Style}]), + Li = wxListItem:new(), + AddListEntry = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Grid, Col, Li), + wxListCtrl:setColumnWidth(Grid, Col, DefSize), + Col + 1 + end, + ListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + lists:foldl(AddListEntry, 0, ListItems), + wxListItem:destroy(Li), + + %% wxListCtrl:connect(Grid, command_list_item_activated), + %% wxListCtrl:connect(Grid, command_list_item_selected), + wxListCtrl:connect(Grid, size, [{skip, true}]), + + wxWindow:setFocus(Grid), + Grid. + +create_matchspec_view(Parent) -> + Panel = wxPanel:new(Parent), + MainSz = wxBoxSizer:new(?wxHORIZONTAL), + Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES, + Splitter = wxSplitterWindow:new(Panel, []), + Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES}, {style, Style}]), + Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCTIONS}, {style, Style}]), + Li = wxListItem:new(), + wxListItem:setText(Li, "Modules"), + wxListCtrl:insertColumn(Modules, 0, Li), + wxListItem:setText(Li, "Functions"), + wxListCtrl:insertColumn(Funcs, 0, Li), + wxListCtrl:setColumnWidth(Funcs, 0, 150), + wxListItem:setText(Li, "Match Spec"), + wxListCtrl:insertColumn(Funcs, 1, Li), + wxListCtrl:setColumnWidth(Funcs, 1, 300), + wxListItem:destroy(Li), + wxSplitterWindow:setSashGravity(Splitter, 0.0), + wxSplitterWindow:setMinimumPaneSize(Splitter,50), + wxSplitterWindow:splitVertically(Splitter, Modules, Funcs, [{sashPosition, 150}]), + wxSizer:add(MainSz, Splitter, [{flag, ?wxEXPAND}, {proportion, 1}]), + + wxListCtrl:connect(Modules, size, [{skip, true}]), + wxListCtrl:connect(Funcs, size, [{skip, true}]), + wxListCtrl:connect(Modules, command_list_item_selected), + %% wxListCtrl:connect(Funcs, command_list_item_selected), + wxPanel:setSizer(Panel, MainSz), + {Panel, Modules, Funcs}. + +create_menues(Parent) -> Menus = [{"File", [#create_menu{id = ?LOAD_TRACEOPTS, text = "Load settings"}, - #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}, - separator, - #create_menu{id = ?SAVE_BUFFER, text = "Save buffer"}, - separator, - #create_menu{id = ?CLOSE, text = "Close"} - ]}, - {"View", [#create_menu{id = ?CLEAR, text = "Clear buffer"}]}, - {"Options", [#create_menu{id = ?OPTIONS, text = "Trace options"}]} - ], - observer_lib:create_menus(Menus, MenuBar, new_window). - + #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}] + }], + observer_wx:create_menus(Parent, Menus). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %Main window - -handle_event(#wx{id = ?CLOSE, event = #wxCommand{type = command_menu_selected}}, - #state{parent = Parent, - trace_options = TraceOpts, - match_specs = MatchSpecs} = State) -> - Parent ! {tracemenu_closed, TraceOpts, MatchSpecs}, - {stop, shutdown, State}; - -handle_event(#wx{id = ?OPTIONS, event = #wxCommand{type = command_menu_selected}}, - #state{frame = Frame, trace_options = TraceOpts, - traced_funcs = TracedFuncs, - node = Node, - match_specs = MatchSpecs, - traceoptions_open = false} = State) -> - - wx_object:start(observer_traceoptions_wx, - [Frame, self(), Node, TraceOpts, TracedFuncs, MatchSpecs], - []), - - {noreply, State#state{traceoptions_open = true}}; - -handle_event(#wx{id = ?CLEAR, event = #wxCommand{type = command_menu_selected}}, - #state{text_ctrl = TxtCtrl} = State) -> - wxTextCtrl:clear(TxtCtrl), - {noreply, State}; - -handle_event(#wx{id = ?SAVE_BUFFER, event = #wxCommand{type = command_menu_selected}}, - #state{frame = Frame, text_ctrl = TxtCtrl} = State) -> - Dialog = wxFileDialog:new(Frame, [{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), - case wxFileDialog:showModal(Dialog) of - ?wxID_OK -> - Path = wxFileDialog:getPath(Dialog), - wxDialog:destroy(Dialog), - case filelib:is_file(Path) of - true -> - observer_wx:create_txt_dialog(Frame, "File already exists: " ++ Path ++ "\n", - "Error", ?wxICON_ERROR); - false -> - wxTextCtrl:saveFile(TxtCtrl, [{file, Path}]) - end; - _ -> - wxDialog:destroy(Dialog), +%%Main window +handle_event(#wx{obj=Obj, event=#wxSize{size={W,_}}}, State) -> + case wx:getObjectType(Obj) =:= wxListCtrl of + true -> + wx:batch(fun() -> + Cols = wxListCtrl:getColumnCount(Obj), + Last = lists:foldl(fun(I, Last) -> + Last - wxListCtrl:getColumnWidth(Obj, I) + end, W-?LCTRL_WDECR, lists:seq(0, Cols - 2)), + Size = max(150, Last), + wxListCtrl:setColumnWidth(Obj, Cols-1, Size) + end); + false -> ok end, {noreply, State}; +handle_event(#wx{id=?ADD_NEW}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> + case observer_traceoptions_wx:process_trace(Parent, TraceOpts) of + {ok, Opts} -> + Process = #tpid{pid=new, opts=Opts}, + {noreply, do_add_processes([Process], State#state{def_trace_opts=Opts})}; + cancel -> + {noreply, State} + end; + +handle_event(#wx{id=?ADD_TP}, + State = #state{panel=Parent, nodes=Nodes, match_specs=Ms}) -> + Node = case Nodes of + [N|_] -> N; + [] -> node() + end, + case observer_traceoptions_wx:trace_pattern(self(), Parent, Node, Ms) of + cancel -> + {noreply, State}; + Patterns -> + {noreply, do_add_patterns(Patterns, State)} + end; + +handle_event(#wx{id=?MODULES, event=#wxList{type=command_list_item_selected, itemIndex=Row}}, + State = #state{tpatterns=TPs, m_view=Mview, f_view=Fview}) -> + Module = list_to_atom(wxListCtrl:getItemText(Mview, Row)), + update_functions_view(dict:fetch(Module, TPs), Fview), + {noreply, State}; + +%% handle_event(#wx{id = ?CLEAR, event = #wxCommand{type = command_menu_selected}}, +%% #state{text_ctrl = TxtCtrl} = State) -> +%% wxTextCtrl:clear(TxtCtrl), +%% {noreply, State}; + +%% handle_event(#wx{id = ?SAVE_BUFFER, event = #wxCommand{type = command_menu_selected}}, +%% #state{frame = Frame, text_ctrl = TxtCtrl} = State) -> +%% Dialog = wxFileDialog:new(Frame, [{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), +%% case wxFileDialog:showModal(Dialog) of +%% ?wxID_OK -> +%% Path = wxFileDialog:getPath(Dialog), +%% wxDialog:destroy(Dialog), +%% case filelib:is_file(Path) of +%% true -> +%% observer_wx:create_txt_dialog(Frame, "File already exists: " ++ Path ++ "\n", +%% "Error", ?wxICON_ERROR); +%% false -> +%% wxTextCtrl:saveFile(TxtCtrl, [{file, Path}]) +%% end; +%% _ -> +%% wxDialog:destroy(Dialog), +%% ok +%% end, +%% {noreply, State}; + handle_event(#wx{id = ?SAVE_TRACEOPTS, event = #wxCommand{type = command_menu_selected}}, - #state{frame = Frame, - trace_options = TraceOpts, - match_specs = MatchSpecs} = State) -> - Dialog = wxFileDialog:new(Frame, [{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), + #state{panel = Panel, + def_trace_opts = TraceOpts, + match_specs = MatchSpecs, + tpatterns = TracePatterns + } = State) -> + Dialog = wxFileDialog:new(Panel, [{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), case wxFileDialog:showModal(Dialog) of ?wxID_OK -> Path = wxFileDialog:getPath(Dialog), - write_file(Frame, Path, TraceOpts, MatchSpecs); + write_file(Panel, Path, TraceOpts, MatchSpecs, dict:to_list(TracePatterns)); _ -> ok end, @@ -189,8 +249,8 @@ handle_event(#wx{id = ?SAVE_TRACEOPTS, handle_event(#wx{id = ?LOAD_TRACEOPTS, event = #wxCommand{type = command_menu_selected}}, - #state{frame = Frame} = State) -> - Dialog = wxFileDialog:new(Frame, [{style, ?wxFD_FILE_MUST_EXIST}]), + #state{panel = Panel} = State) -> + Dialog = wxFileDialog:new(Panel, [{style, ?wxFD_FILE_MUST_EXIST}]), State2 = case wxFileDialog:showModal(Dialog) of ?wxID_OK -> Path = wxFileDialog:getPath(Dialog), @@ -202,92 +262,180 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS, {noreply, State2}; -handle_event(#wx{event = #wxClose{type = close_window}}, - #state{parent = Parent, - trace_options = TraceOpts, - match_specs = MatchSpecs} = State) -> - Parent ! {tracemenu_closed, TraceOpts, MatchSpecs}, - {stop, shutdown, State}; - -handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, - #state{node = Node, - traced_procs = TracedProcs, - traced_funcs = TracedDict, - trace_options = TraceOpts, - text_ctrl = TextCtrl, - toggle_button = ToggleBtn} = State) -> - - start_trace(Node, TracedProcs, TracedDict, TraceOpts), - wxTextCtrl:appendText(TextCtrl, "Start Trace:\n"), - wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), - {noreply, State}; - -handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 0}}, %%Stop tracing - #state{text_ctrl = TxtCtrl, - toggle_button = ToggleBtn} = State) -> - dbg:stop_clear(), - wxTextCtrl:appendText(TxtCtrl, "Stop Trace.\n"), - wxToggleButton:setLabel(ToggleBtn, "Start Trace"), - {noreply, State}; - +%% handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, +%% #state{node = Node, +%% traced_procs = TracedProcs, +%% traced_funcs = TracedDict, +%% trace_options = TraceOpts, +%% text_ctrl = TextCtrl, +%% toggle_button = ToggleBtn} = State) -> + +%% start_trace(Node, TracedProcs, TracedDict, TraceOpts), +%% wxTextCtrl:appendText(TextCtrl, "Start Trace:\n"), +%% wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), +%% {noreply, State}; + +%% handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 0}}, %%Stop tracing +%% #state{text_ctrl = TxtCtrl, +%% toggle_button = ToggleBtn} = State) -> +%% dbg:stop_clear(), +%% wxTextCtrl:appendText(TxtCtrl, "Stop Trace.\n"), +%% wxToggleButton:setLabel(ToggleBtn, "Start Trace"), +%% {noreply, State}; + +handle_event(#wx{id=ID, event = What}, State) -> + io:format("~p:~p: Unhandled event: ~p, ~p ~n", [?MODULE, self(), ID, What]), + {noreply, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +handle_call(Msg, _From, State) -> + io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), + {reply, ok, State}. -handle_event(#wx{event = What}, State) -> - io:format("~p~p: Unhandled event: ~p ~n", [?MODULE, self(), What]), +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> + case observer_traceoptions_wx:process_trace(Parent, TraceOpts) of + {ok, Opts} -> + POpts = [#tpid{pid=Pid, opts=Opts} || Pid <- Pids], + {noreply, do_add_processes(POpts, State#state{def_trace_opts=Opts})}; + cancel -> + {noreply, State} + end; +handle_cast(Msg, State) -> + io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), {noreply, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -handle_info({updated_traceopts, - TraceOpts, - MatchSpecs, - TracedFuncs}, State) -> - {noreply, State#state{trace_options = TraceOpts, - match_specs = MatchSpecs, - traced_funcs = TracedFuncs, - traceoptions_open = false}}; - -handle_info(traceopts_closed, State) -> - {noreply, State#state{traceoptions_open = false}}; - -handle_info(Tuple, #state{text_ctrl = TxtCtrl} = State) when is_tuple(Tuple) -> - Text = textformat(Tuple), - wxTextCtrl:appendText(TxtCtrl, lists:flatten(Text)), +handle_info({active, _Node}, State=#state{parent=Parent}) -> + create_menues(Parent), + {noreply, State}; + +handle_info(not_active, State) -> {noreply, State}; +handle_info({update_ms, NewMs}, State) -> + {noreply, State#state{match_specs=NewMs}}; + handle_info(Any, State) -> io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]), {noreply, State}. - -terminate(Reason, #state{node = Node, - frame = Frame}) -> - try - case observer_wx:try_rpc(Node, erlang, whereis, [dbg]) of - undefined -> fine; - Pid -> exit(Pid, kill) - end, - io:format("~p terminating tracemenu. Reason: ~p~n", [?MODULE, Reason]), - wxFrame:destroy(Frame), - ok - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - wxFrame:destroy(Frame) - end. +terminate(Reason, #state{nodes=Nodes}) -> + %% case observer_wx:try_rpc(Node, erlang, whereis, [dbg]) of + %% undefined -> fine; + %% Pid -> exit(Pid, kill) + %% end, + ok. code_change(_, _, State) -> {stop, not_yet_implemented, State}. -handle_call(Msg, _From, State) -> - io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -handle_cast(Msg, State) -> - io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), - {noreply, State}. +do_add_processes(POpts, S0=#state{p_view=LCtrl, tpids=OldPids, nodes=Ns0}) -> + case merge_pids(POpts, OldPids) of + {OldPids, [], []} -> + S0; + {Pids, New, Changed} -> + update_process_view(Pids, LCtrl), + Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]), + Nodes = case ordsets:subtract(Ns1, Ns0) of + [] -> Ns0; %% No new Nodes + NewNs -> + %% Handle new nodes + %% BUGBUG add trace patterns for new nodes + ordsets:union(NewNs, Ns0) + end, + S0#state{tpids=Pids, nodes=Nodes} + end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +update_process_view(Pids, LCtrl) -> + wxListCtrl:deleteAllItems(LCtrl), + wx:foldl(fun(#tpid{pid=Pid, opts=Opts}, Row) -> + _Item = wxListCtrl:insertItem(LCtrl, Row, ""), + ?EVEN(Row) andalso + wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), + wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Pid)), + wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)), + Row+1 + end, 0, Pids). + +do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) -> + Old = case dict:find(Module, TPs0) of + {ok, Prev} -> Prev; + error -> [] + end, + case merge_patterns(NewPs, Old) of + {Old, [], []} -> + State; + {MPatterns, New, Changed} -> + TPs = dict:store(Module, MPatterns, TPs0), + update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview), + update_functions_view(dict:fetch(Module, TPs), Fview), + State#state{tpatterns=TPs} + end. + +update_modules_view(Mods, Module, LCtrl) -> + wxListCtrl:deleteAllItems(LCtrl), + wx:foldl(fun(Mod, Row) -> + _Item = wxListCtrl:insertItem(LCtrl, Row, ""), + ?EVEN(Row) andalso + wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), + wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Mod)), + (Mod =:= Module) andalso + wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, ?wxLIST_STATE_SELECTED), + Row+1 + end, 0, Mods). + +update_functions_view(Funcs, LCtrl) -> + wxListCtrl:deleteAllItems(LCtrl), + wx:foldl(fun(#tpattern{fa=FA, ms=#match_spec{str=Ms}}, Row) -> + _Item = wxListCtrl:insertItem(LCtrl, Row, ""), + ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), + wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({func,FA})), + wxListCtrl:setItem(LCtrl, Row, 1, Ms), + Row+1 + end, 0, Funcs). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +merge_pids([N1=#tpid{pid=new}|Ns], [N2=#tpid{pid=new}|Old]) -> + {Pids, New, Changed} = merge_pids_1(Ns,Old), + {[N1|Pids], New, [{N2,N2}|Changed]}; +merge_pids([N1=#tpid{pid=new}|Ns], Old) -> + {Pids, New, Changed} = merge_pids_1(Ns,Old), + {[N1|Pids], [N1|New], Changed}; +merge_pids(Ns, [N2=#tpid{pid=new}|Old]) -> + {Pids, New, Changed} = merge_pids_1(Ns,Old), + {[N2|Pids], New, Changed}; +merge_pids(New, Old) -> + merge_pids_1(New, Old). + +merge_pids_1(New, Old) -> + merge(lists:sort(New), Old, #tpid.pid, [], [], []). + +merge_patterns(New, Old) -> + merge(lists:sort(New), Old, #tpattern.fa, [], [], []). + + +merge([N|Ns], [N|Os], El, New, Ch, All) -> + merge(Ns, Os, El, New, Ch, [N|All]); +merge([N|Ns], [O|Os], El, New, Ch, All) + when element(El, N) == element(El, O) -> + merge(Ns, Os, El, New, [{O,N}|Ch], [N|All]); +merge([N|Ns], Os=[O|_], El, New, Ch, All) + when element(El, N) < element(El, O) -> + merge(Ns, Os, El, [N|New], Ch, [N|All]); +merge(Ns=[N|_], [O|Os], El, New, Ch, All) + when element(El, N) > element(El, O) -> + merge(Ns, Os, El, New, Ch, [O|All]); +merge([], Os, _El, New, Ch, All) -> + {lists:reverse(All, Os), New, Ch}; +merge(Ns, [], _El, New, Ch, All) -> + {lists:reverse(All, Ns), Ns++New, Ch}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_trace(Node, TracedProcs, TracedDict, #trace_options{send = Send, treceive = Receive, functions = Functions, @@ -385,10 +533,9 @@ print(Num, X, Buff) -> trace_functions(TracedDict) -> Trace = fun(KeyAtom, RecordList, acc_in) -> - lists:foreach(fun(#traced_func{func_name = Function, arity = Arity, - match_spec = #match_spec{term_ms = MS}}) -> + match_spec = #match_spec{term = MS}}) -> dbg:tpl({KeyAtom, Function, Arity}, MS) end, RecordList), @@ -396,37 +543,23 @@ trace_functions(TracedDict) -> end, dict:fold(Trace, acc_in, TracedDict). - -write_file(Frame, Filename, #trace_options{send = Send, - treceive = Receive, - functions = Functions, - events = Events, - on_1st_spawn = On1stSpawn, - on_all_spawn = OnAllSpawn, - on_1st_link = On1stLink, - on_all_link = OnAllLink}, - MatchSpecs) -> - - FormattedMatchSpecs = lists:flatten(lists:foldl( - fun(#match_spec{alias = A, term_ms = T, fun2ms = F}, Acc) -> - [io_lib:format("{alias, ~p, term_ms, ~p, fun2ms, ~p}.\n", - [A, T, F]) | Acc] - end, [], MatchSpecs)), - - Binary = - list_to_binary("%%%\n%%% This file is generated by Observer\n" - "%%%\n%%% DO NOT EDIT!\n%%%\n" - "{send, " ++ atom_to_list(Send) ++ "}.\n" - "{treceive, " ++ atom_to_list(Receive) ++ "}.\n" - "{functions, " ++ atom_to_list(Functions) ++ "}.\n" - "{events, " ++ atom_to_list(Events) ++ "}.\n" - "{on_1st_spawn, " ++ atom_to_list(On1stSpawn) ++ "}.\n" - "{on_all_spawn, " ++ atom_to_list(OnAllSpawn) ++ "}.\n" - "{on_1st_link, " ++ atom_to_list(On1stLink) ++ "}.\n" - "{on_all_link, " ++ atom_to_list(OnAllLink) ++ "}.\n" - ++ FormattedMatchSpecs), - - case file:write_file(Filename, Binary) of +write_file(Frame, Filename, TraceOps, MatchSpecs, TPs) -> + FormatMS = fun(#match_spec{name=Id, term=T, func=F}) -> + io_lib:format("[{name,\"~s\"}, {term, ~w}, {func, \"~s\"}]", + [Id, T, F]) + end, + FormatTP = fun({Module, FTPs}) -> + List = format_ftp(FTPs, FormatMS), + io_lib:format("{tp, ~w, [~s]}.~n",[Module, List]) + end, + Str = + ["%%%\n%%% This file is generated by Observer\n", + "%%%\n%%% DO NOT EDIT!\n%%%\n", + [["{ms, ", FormatMS(Ms), "}.\n"] || Ms <- MatchSpecs], + "{traceopts, ", io_lib:format("~w",[TraceOps]) ,"}.\n", + [FormatTP(TP) || TP <- TPs] + ], + case file:write_file(Filename, list_to_binary(Str)) of ok -> success; {error, Reason} -> @@ -434,47 +567,38 @@ write_file(Frame, Filename, #trace_options{send = Send, observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR) end. +format_ftp([#tpattern{fa={F,A}, ms=Ms}], FormatMS) -> + io_lib:format("{~w, ~w, ~s}", [F,A,FormatMS(Ms)]); +format_ftp([#tpattern{fa={F,A}, ms=Ms}|Rest], FormatMS) -> + [io_lib:format("{~w, ~w, ~s},~n ", [F,A,FormatMS(Ms)]), + format_ftp(Rest, FormatMS)]. -read_settings(Filename, #state{frame = Frame} = State) -> +read_settings(Filename, #state{match_specs=Ms0, def_trace_opts=TO0} = State) -> case file:consult(Filename) of {ok, Terms} -> - {TraceOpts, MatchSpecs} = parse_settings(Terms, {#trace_options{}, []}), - State#state{trace_options = TraceOpts, match_specs = MatchSpecs}; + Ms = lists:usort(Ms0 ++ [parse_ms(MsList) || {ms, MsList} <- Terms]), + TOs = lists:usort(TO0 ++ proplists:get_value(traceopts, Terms, [])), + lists:foldl(fun parse_tp/2, + State#state{match_specs=Ms, def_trace_opts=TOs}, + Terms); {error, _} -> - observer_wx:create_txt_dialog(Frame, "Could not load settings", "Error", ?wxICON_ERROR), + observer_wx:create_txt_dialog(State#state.panel, "Could not load settings", + "Error", ?wxICON_ERROR), State end. - -parse_settings([], {TraceOpts, MatchSpecs}) -> - {TraceOpts, MatchSpecs}; -parse_settings([{send, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{send = Bool}, MS}); -parse_settings([{treceive, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{treceive = Bool}, MS}); -parse_settings([{functions, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{functions = Bool}, MS}); -parse_settings([{events, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{events = Bool}, MS}); -parse_settings([{on_1st_spawn, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{on_1st_spawn = Bool}, MS}); -parse_settings([{on_all_spawn, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{on_all_spawn = Bool}, MS}); -parse_settings([{on_1st_link, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{on_1st_link = Bool}, MS}); -parse_settings([{on_all_link, Bool} | T], {Opts, MS}) -> - parse_settings(T, {Opts#trace_options{on_all_link = Bool}, MS}); -parse_settings([{alias, A, term_ms, TermMS, fun2ms, F} | T], {Opts, MatchSpecs}) -> - Alias = case A of - undefined -> A; - _ -> lists:flatten(io_lib:format("~s", [A])) - end, - Fun2MS = case F of - undefined -> F; - _ -> lists:flatten(io_lib:format("~s", [F])) - end, - parse_settings(T, {Opts, [#match_spec{alias = Alias, - term_ms = TermMS, - str_ms = lists:flatten(io_lib:format("~p", [TermMS])), - fun2ms = Fun2MS} - | MatchSpecs]}). +parse_ms(Opts) -> + Name = proplists:get_value(name, Opts, "TracePattern"), + Term = proplists:get_value(term, Opts, [{'_',[],[ok]}]), + FunStr = proplists:get_value(term, Opts, "fun(_) -> ok end"), + make_ms(Name, Term, FunStr). + +make_ms(Name, Term, FunStr) -> + #match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}. + +parse_tp({tp, Mod, FAs}, State) -> + Patterns = [#tpattern{m=Mod,fa={F,A}, ms=parse_ms(List)} || + {F,A,List} <- FAs], + do_add_patterns({Mod, Patterns}, State); +parse_tp(_, State) -> + State. diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 7244efdc50..7570610b97 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -21,210 +21,420 @@ -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). --export([start/6]). --export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, - handle_event/2, handle_cast/2]). - --behaviour(wx_object). - --record(traceopts_state, { - parent, - frame, - tree, - boxes, - functionpage_listbox, - matchpage_styled_txtctrl, - matchpage_listbox, - popup_open = false, - module_popup_dialog, - module_popup_checklistbox, - matchspec_popup_dialog, - matchspec_popup_listbox, - matchspec_popup_styled_txtctrl, - match_specs, % [ #match_spec{} ] - checked_funcs = [], - traced_funcs, % Key =:= Module::atom, Value =:= [ #traced_func{} ] - trace_options}). - - --record(boxes, {send, 'receive', functions, events, - on_spawn, on_link, all_spawn, all_link}). - --define(TRACEOPTS_FRAME, 501). - --define(MATCHPAGE_ADDFUN, 502). --define(MATCHPAGE_ADDMS, 503). --define(MATCHPAGE_ADDMS_ALIAS, 504). --define(MATCHPAGE_LISTBOX, 505). - --define(MATCH_POPUP_DIALOG, 506). - --define(MODULEPOPUP_SELECT, 507). --define(MODULEPOPUP_SELALL, 508). --define(MODULEPOPUP_CHECKLISTBOX, 509). --define(MODULEPOPUP_TXTCTRL, 510). --define(MODULEPOPUP_DIALOG, 511). - --define(FUNCTIONPAGE_LISTBOX, 512). --define(FUNCTIONPAGE_TXTCTRL, 513). - - -start(ParentFrame, ParentPid, Node, TraceOpts, TracedFuncs, MatchSpecs) -> - wx_object:start(?MODULE, [ParentFrame, ParentPid, Node, TraceOpts, - TracedFuncs, MatchSpecs], []). - -init([ParentFrame, ParentPid, Node, TraceOpts, TracedFuncs, MatchSpecs]) -> +-export([process_trace/2, trace_pattern/4]). + +-compile(export_all). + +process_trace(Parent, Default) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Process Options", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]), + Panel = wxPanel:new(Dialog), + MainSz = wxBoxSizer:new(?wxVERTICAL), + PanelSz = wxBoxSizer:new(?wxHORIZONTAL), + LeftSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Tracing options"}]), + RightSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Inheritance options:"}]), + + FuncBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace function call", []), + check_box(FuncBox, lists:member(functions, Default)), + SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []), + check_box(SendBox, lists:member(send, Default)), + RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []), + check_box(RecBox, lists:member('receive', Default)), + EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace process events", []), + check_box(EventBox, lists:member(events, Default)), + + {SpawnBox, SpwnAllRadio, SpwnFirstRadio} = + optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"), + {LinkBox, LinkAllRadio, LinkFirstRadio} = + optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "link"), + SpawnBool = lists:member(on_spawn, Default) orelse lists:member(on_first_spawn, Default), + LinkBool = lists:member(on_link, Default) orelse lists:member(on_first_link, Default), + check_box(SpawnBox, SpawnBool), + check_box(LinkBox, LinkBool), + enable(SpawnBox, [SpwnAllRadio, SpwnFirstRadio]), + enable(LinkBox, [LinkAllRadio, LinkFirstRadio]), + wxRadioButton:setValue(SpwnAllRadio, lists:member(on_spawn, Default)), + wxRadioButton:setValue(SpwnFirstRadio, lists:member(on_first_spawn, Default)), + wxRadioButton:setValue(LinkAllRadio, lists:member(on_link, Default)), + wxRadioButton:setValue(LinkFirstRadio, lists:member(on_first_link, Default)), + + wxSizer:add(LeftSz, FuncBox, []), + wxSizer:add(LeftSz, SendBox, []), + wxSizer:add(LeftSz, RecBox, []), + wxSizer:add(LeftSz, EventBox, []), + wxSizer:add(LeftSz, 150, -1), + + wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}]), + wxSizer:add(PanelSz, RightSz,[{flag, ?wxEXPAND}]), + wxPanel:setSizer(Panel, PanelSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND}, {proportion,1}]), + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]), + wxWindow:setSizerAndFit(Dialog, MainSz), + wxSizer:setSizeHints(MainSz, Dialog), + wxCheckBox:connect(SpawnBox, command_checkbox_clicked, + [{callback, fun(#wx{event=#wxCommand{}},_) -> + enable(SpawnBox, [SpwnAllRadio, SpwnFirstRadio]) + end}]), + wxCheckBox:connect(LinkBox, command_checkbox_clicked, + [{callback, fun(#wx{event=#wxCommand{}},_) -> + enable(LinkBox, [LinkAllRadio, LinkFirstRadio]) + end}]), + + Res = case wxDialog:showModal(Dialog) of + ?wxID_OK -> + All = [{SendBox, send}, {RecBox, 'receive'}, + {FuncBox, functions}, {EventBox, events}, + {{SpawnBox, SpwnAllRadio}, on_spawn}, + {{SpawnBox,SpwnFirstRadio}, on_first_spawn}, + {{LinkBox, LinkAllRadio}, on_link}, + {{LinkBox, LinkFirstRadio}, on_first_link}], + Check = fun({Box, Radio}) -> + wxCheckBox:getValue(Box) andalso wxRadioButton:getValue(Radio); + (Box) -> + wxCheckBox:getValue(Box) + end, + Opts = [Id || {Tick, Id} <- All, Check(Tick)], + {ok, lists:reverse(Opts)}; + ?wxID_CANCEL -> + cancel + end, + wxDialog:destroy(Dialog), + Res. + +trace_pattern(ParentPid, Parent, Node, MatchSpecs) -> try - {Frame, Tree, Boxes, ModuleListBox, MatchTxtCtrl, MatchListBox} - = setup(ParentFrame, Node, TraceOpts, TracedFuncs, MatchSpecs), - - {Frame, - #traceopts_state{parent = ParentPid, - frame = Frame, - tree = Tree, - functionpage_listbox = ModuleListBox, - matchpage_styled_txtctrl = MatchTxtCtrl, - matchpage_listbox = MatchListBox, - boxes = Boxes, - match_specs = MatchSpecs, - traced_funcs = TracedFuncs, - trace_options = TraceOpts}} - - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(ParentFrame, Node), - {stop, badrpc, #traceopts_state{}} + Module = module_selector(Parent, Node), + MFAs = function_selector(Parent, Node, Module), + MatchSpec = select_matchspec(ParentPid, Parent, MatchSpecs), + {Module, [#tpattern{m=M,fa={F,A},ms=MatchSpec} || {M,F,A} <- MFAs]} + catch cancel -> cancel end. +module_selector(Parent, Node) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, + {size, {400, 400}}]), + Panel = wxPanel:new(Dialog), + PanelSz = wxBoxSizer:new(?wxVERTICAL), + MainSz = wxBoxSizer:new(?wxVERTICAL), + + TxtCtrl = wxTextCtrl:new(Panel, ?wxID_ANY), + ListBox = wxListBox:new(Panel, ?wxID_ANY, [{style, ?wxLB_SINGLE}]), + wxSizer:add(PanelSz, TxtCtrl, [{flag, ?wxEXPAND}]), + wxSizer:add(PanelSz, ListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxPanel:setSizer(Panel, PanelSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 1}]), + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 0}]), + wxWindow:setSizer(Dialog, MainSz), + OkId = wxDialog:getAffirmativeId(Dialog), + OkButt = wxWindow:findWindowById(OkId), + wxWindow:disable(OkButt), + wxWindow:setFocus(TxtCtrl), + %% init data + Modules = get_modules(Node), + AllModules = [{atom_to_list(X), X} || X <- Modules], + filter_listbox_data("", AllModules, ListBox), + wxTextCtrl:connect(TxtCtrl, command_text_updated, + [{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) -> + filter_listbox_data(Input, AllModules, ListBox) + end}]), + wxListBox:connect(ListBox, command_listbox_doubleclicked, + [{callback, fun(_, _) -> wxDialog:endModal(Dialog, ?wxID_OK) end}]), + wxListBox:connect(ListBox, command_listbox_selected, + [{callback, fun(_, _) -> wxWindow:enable(OkButt) end}]), + + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + SelId = wxListBox:getSelection(ListBox), + Module = wxListBox:getClientData(ListBox, SelId), + wxDialog:destroy(Dialog), + Module; + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + throw(cancel) + end. -setup(ParentFrame, Node, TraceOpts, TracedFuncs, MatchSpecs) -> +function_selector(Parent, Node, Module) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Functions", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, + {size, {400, 400}}]), -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Setup main window + Panel = wxPanel:new(Dialog), + PanelSz = wxBoxSizer:new(?wxVERTICAL), + MainSz = wxBoxSizer:new(?wxVERTICAL), + + TxtCtrl = wxTextCtrl:new(Panel, ?wxID_ANY), + ListBox = wxCheckListBox:new(Panel, ?wxID_ANY, [{style, ?wxLB_EXTENDED}]), + wxSizer:add(PanelSz, TxtCtrl, [{flag, ?wxEXPAND}]), + wxSizer:add(PanelSz, ListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), + SelAllBtn = wxButton:new(Panel, ?wxID_ANY, [{label, "Check Visible"}]), + DeSelAllBtn = wxButton:new(Panel, ?wxID_ANY, [{label, "Uncheck Visible"}]), + ButtonSz = wxBoxSizer:new(?wxHORIZONTAL), + [wxSizer:add(ButtonSz, Button, []) || Button <- [SelAllBtn, DeSelAllBtn]], + wxSizer:add(PanelSz, ButtonSz, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 0}]), + wxPanel:setSizer(Panel, PanelSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 1}]), + + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 0}]), + wxWindow:setSizer(Dialog, MainSz), + wxWindow:setFocus(TxtCtrl), + %% Init + Functions = observer_wx:try_rpc(Node, Module, module_info, [functions]), + Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, + not(erl_internal:guard_bif(Name, Arity))]), + ParsedChoices = parse_function_names(Choices), + filter_listbox_data("", ParsedChoices, ListBox), + %% Setup Event handling + wxTextCtrl:connect(TxtCtrl, command_text_updated, + [{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) -> + filter_listbox_data(Input, ParsedChoices, ListBox) + end}]), + Self = self(), + wxCheckListBox:connect(ListBox, command_checklistbox_toggled, + [{callback, fun(#wx{event=#wxCommand{commandInt=N}}, _) -> + Self ! {ListBox, wxCheckListBox:isChecked(ListBox, N), + wxListBox:getClientData(ListBox, N)} + end}]), + Check = fun(Id, Bool) -> + wxCheckListBox:check(ListBox, Id, [{check, Bool}]), + Self ! {ListBox, Bool, wxListBox:getClientData(ListBox, Id)} + end, + wxButton:connect(SelAllBtn, command_button_clicked, + [{callback, fun(#wx{}, _) -> + Count = wxListBox:getCount(ListBox), + [Check(SelId, true) || + SelId <- lists:seq(0, Count-1), + not wxCheckListBox:isChecked(ListBox, SelId)] + end}]), + wxButton:connect(DeSelAllBtn, command_button_clicked, + [{callback, fun(#wx{}, _) -> + Count = wxListBox:getCount(ListBox), + [Check(SelId, false) || + SelId <- lists:seq(0, Count-1), + wxCheckListBox:isChecked(ListBox, SelId)] + end}]), + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + wxDialog:destroy(Dialog), + case get_checked_funcs(ListBox, []) of + [] -> [{Module, '_', '_'}]; + FAs -> + [{Module, F, A} || {F,A} <- FAs] + end; + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + throw(cancel) + end. + +get_checked_funcs(ListBox, Acc) -> + receive + {ListBox, true, FA} -> + get_checked_funcs(ListBox, [FA|lists:delete(FA,Acc)]); + {ListBox, false, FA} -> + get_checked_funcs(ListBox, lists:delete(FA,Acc)) + after 0 -> + lists:reverse(Acc) + end. + +select_matchspec(Pid, Parent, MatchSpecs) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, + {size, {400, 400}}]), - Frame = wxFrame:new(ParentFrame, ?TRACEOPTS_FRAME, "Trace options", - [{style, ?wxRESIZE_BORDER bor ?wxCLOSE_BOX}, - {size, {400, 500}}]), - Panel = wxPanel:new(Frame, []), + Panel = wxPanel:new(Dialog), + PanelSz = wxBoxSizer:new(?wxVERTICAL), MainSz = wxBoxSizer:new(?wxVERTICAL), - Notebook = wxNotebook:new(Panel, ?wxID_ANY), - Modules = get_modules(Node), + TxtSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Match specification:"}]), + BtnSz = wxBoxSizer:new(?wxHORIZONTAL), + SavedSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Saved match specifications:"}]), -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Setup tracing page - - OptPanel = wxPanel:new(Notebook), - OptMainSz = wxBoxSizer:new(?wxVERTICAL), - TopSz = wxBoxSizer:new(?wxHORIZONTAL), - TopLeftSz = wxStaticBoxSizer:new(?wxVERTICAL, OptPanel, - [{label, "Tracing options"}]), - TopRightSz = wxStaticBoxSizer:new(?wxVERTICAL, OptPanel, - [{label, "Inheritance options:"}]), - - SendBox = wxCheckBox:new(OptPanel, ?wxID_ANY, "Trace send", []), - check_box(SendBox, TraceOpts#trace_options.send), - RecBox = wxCheckBox:new(OptPanel, ?wxID_ANY, "Trace receive", []), - check_box(RecBox, TraceOpts#trace_options.treceive), - FuncBox = wxCheckBox:new(OptPanel, ?wxID_ANY, "Trace functions", []), - check_box(FuncBox, TraceOpts#trace_options.functions), - EventBox = wxCheckBox:new(OptPanel, ?wxID_ANY, "Trace events", []), - check_box(EventBox, TraceOpts#trace_options.events), + TextCtrl = create_styled_txtctrl(Panel), + wxSizer:add(TxtSz, TextCtrl, [{flag, ?wxEXPAND}, {proportion, 1}]), - {SpawnBox, SpwnAllRadio, SpwnFirstRadio} = - optionpage_top_right(OptPanel, TopRightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"), - {LinkBox, LinkAllRadio, LinkFirstRadio} = - optionpage_top_right(OptPanel, TopRightSz, [{flag, ?wxBOTTOM},{border, 5}], "link"), - SpawnBool = TraceOpts#trace_options.on_all_spawn or TraceOpts#trace_options.on_1st_spawn, - LinkBool = TraceOpts#trace_options.on_all_link or TraceOpts#trace_options.on_1st_link, - check_box(SpawnBox, SpawnBool), - check_box(LinkBox, LinkBool), - enable({SpawnBox, SpwnAllRadio, SpwnFirstRadio}), - enable({LinkBox, LinkAllRadio, LinkFirstRadio}), - wxRadioButton:setValue(SpwnAllRadio, TraceOpts#trace_options.on_all_spawn), - wxRadioButton:setValue(SpwnFirstRadio, TraceOpts#trace_options.on_1st_spawn), - wxRadioButton:setValue(LinkAllRadio, TraceOpts#trace_options.on_all_link), - wxRadioButton:setValue(LinkFirstRadio, TraceOpts#trace_options.on_1st_link), - - wxSizer:add(TopLeftSz, SendBox, []), - wxSizer:add(TopLeftSz, RecBox, []), - wxSizer:add(TopLeftSz, FuncBox, []), - wxSizer:add(TopLeftSz, EventBox, []), - wxSizer:add(TopLeftSz, 150, -1), - - wxSizer:add(TopSz, TopLeftSz, [{flag, ?wxEXPAND}]), - wxSizer:add(TopSz, TopRightSz,[{flag, ?wxEXPAND}]), - wxSizer:add(OptMainSz, TopSz, []), - wxWindow:setSizer(OptPanel, OptMainSz), - wxNotebook:addPage(Notebook, OptPanel, "Tracing"), - -%%%%%%%%%%%%%%%%%%%%%%%% Setup functions page - - FuncPanel = wxPanel:new(Notebook), - FuncMainSz = wxBoxSizer:new(?wxVERTICAL), - ModuleSz = wxStaticBoxSizer:new(?wxVERTICAL, FuncPanel, [{label, "Select module"}]), - TreeSz = wxStaticBoxSizer:new(?wxVERTICAL, FuncPanel, [{label, "Selected functions"}]), - - AllModules = atomlist_to_stringlist(Modules), - ModuleTxtCtrl = wxTextCtrl:new(FuncPanel, ?FUNCTIONPAGE_TXTCTRL), - ModuleListBox = wxListBox:new(FuncPanel, ?FUNCTIONPAGE_LISTBOX, [{choices, AllModules}, {style, ?wxLB_SINGLE}]), - TreeCtrl = wxTreeCtrl:new(FuncPanel), - wxTreeCtrl:addRoot(TreeCtrl, atom_to_list(Node)), - update_tree(TreeCtrl, TracedFuncs), - - wxTextCtrl:connect(ModuleTxtCtrl, command_text_updated, - [{userData, AllModules}]), - wxListBox:connect(ModuleListBox, command_listbox_doubleclicked), - wxTreeCtrl:connect(TreeCtrl, command_tree_item_activated), - - wxSizer:add(ModuleSz, ModuleTxtCtrl, [{flag, ?wxEXPAND}]), - wxSizer:add(ModuleSz, ModuleListBox, [{flag, ?wxEXPAND}]), - wxSizer:add(TreeSz, TreeCtrl, [{flag, ?wxEXPAND},{proportion, 1}]), - wxSizer:add(FuncMainSz, ModuleSz, [{flag, ?wxEXPAND}]), - wxSizer:add(FuncMainSz, TreeSz, [{flag, ?wxEXPAND}, {proportion, 1}]), - wxWindow:setSizer(FuncPanel, FuncMainSz), - wxNotebook:addPage(Notebook, FuncPanel, "Functions"), - - -%%%%%%%%%%%%%%%%%%% Setup match specification page - - {MatchPanel, _, MatchTxtCtrl, MatchListBox} = create_matchspec_page(Notebook, MatchSpecs, matchpage), - wxNotebook:addPage(Notebook, MatchPanel, "Match Specs"), - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Setup Dialog - - wxSizer:add(MainSz, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]), - OKBtn = wxButton:new(Panel, ?wxID_OK, []), - CancelBtn = wxButton:new(Panel, ?wxID_CANCEL, []), - DialogBtnSz = wxStdDialogButtonSizer:new(), - wxStdDialogButtonSizer:addButton(DialogBtnSz, OKBtn), - wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), - wxStdDialogButtonSizer:realize(DialogBtnSz), - wxSizer:add(MainSz, DialogBtnSz), - wxWindow:setSizer(Panel, MainSz), - - Boxes = #boxes{send = SendBox, - 'receive' = RecBox, - functions = FuncBox, - events = EventBox, - on_spawn = #on_spawn{checkbox = SpawnBox, - all_spawn = SpwnAllRadio, - first_spawn = SpwnFirstRadio}, - all_spawn = SpwnAllRadio, - on_link = #on_link{checkbox = LinkBox, - all_link = LinkAllRadio, - first_link = LinkFirstRadio}, - all_link = LinkAllRadio}, - - - wxButton:connect(OKBtn, command_button_clicked, [{userData, trace_options}]), - wxButton:connect(CancelBtn, command_button_clicked, [{userData, trace_options}]), - wxFrame:connect(Frame, close_window, []), - wxFrame:show(Frame), - {Frame, TreeCtrl, Boxes, ModuleListBox, MatchTxtCtrl, MatchListBox}. + AddMsBtn = wxButton:new(Panel, ?wxID_ANY, [{label, "New"}]), + EditMsBtn = wxButton:new(Panel, ?wxID_ANY, [{label, "Edit"}]), + DelMsBtn = wxButton:new(Panel, ?wxID_ANY, [{label, "Delete"}]), + wxSizer:add(BtnSz, AddMsBtn), + wxSizer:add(BtnSz, EditMsBtn), + wxSizer:add(BtnSz, DelMsBtn), + + ListBox = wxListBox:new(Panel, ?wxID_ANY, []), + wxSizer:add(SavedSz, ListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(PanelSz, TxtSz, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(PanelSz, BtnSz), + wxSizer:add(PanelSz, SavedSz, [{flag, ?wxEXPAND}, {proportion, 1}]), + + wxWindow:setSizer(Panel, PanelSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 1}]), + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, + {border, 5}, {proportion, 0}]), + wxWindow:setSizer(Dialog, MainSz), + OkId = wxDialog:getAffirmativeId(Dialog), + OkButt = wxWindow:findWindowById(OkId), + wxWindow:disable(OkButt), + wxWindow:disable(EditMsBtn), + wxWindow:disable(DelMsBtn), + + Choices = ms_names(MatchSpecs), + filter_listbox_data("", Choices, ListBox), + + Add = fun(_,_) -> + case edit_ms(TextCtrl, new, Parent) of + Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox); + Else -> Else + end + end, + Edit = fun(_,_) -> + SelId = wxListBox:getSelection(ListBox), + case SelId >= 0 of + true -> + #match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId), + case edit_ms(TextCtrl, Name, Parent) of + Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox); + Else -> Else + end; + false -> + ok + end + end, + Del = fun(_,_) -> + SelId = wxListBox:getSelection(ListBox), + case SelId >= 0 of + true -> + wxListBox:delete(ListBox, SelId); + false -> + ok + end + end, + Sel = fun(#wx{event=#wxCommand{commandInt=Id}}, _) -> + case Id >= 0 of + true -> + wxWindow:enable(OkButt), + wxWindow:enable(EditMsBtn), + wxWindow:enable(DelMsBtn), + #match_spec{func=Str} = wxListBox:getClientData(ListBox,Id), + wxStyledTextCtrl:setText(TextCtrl, Str); + false -> + try + wxWindow:disable(OkButt), + wxWindow:disable(EditMsBtn), + wxWindow:disable(DelMsBtn) + catch _:_ -> ok + end + end + end, + wxButton:connect(AddMsBtn, command_button_clicked, [{callback,Add}]), + wxButton:connect(EditMsBtn, command_button_clicked, [{callback,Edit}]), + wxButton:connect(DelMsBtn, command_button_clicked, [{callback,Del}]), + wxListBox:connect(ListBox, command_listbox_selected, [{callback, Sel}]), + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + SelId = wxListBox:getSelection(ListBox), + Count = wxListBox:getCount(ListBox), + MSs = [wxListBox:getClientData(ListBox, Id) || + Id <- lists:seq(0, Count-1)], + Pid ! {update_ms, MSs}, + MS = lists:nth(SelId+1, MSs), + wxDialog:destroy(Dialog), + MS; + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + throw(cancel) + end. + +edit_ms(TextCtrl, Label0, Parent) -> + Str = ensure_last_is_dot(wxStyledTextCtrl:getText(TextCtrl)), + try + MatchSpec = ms_from_string(Str), + Label = case Label0 == new of + true -> get_label(Parent); + _ -> Label0 + end, + #match_spec{name=Label, term=MatchSpec, + str=io_lib:format("~w",[MatchSpec]), + func=Str} + catch + throw:cancel -> + ok; + throw:Error -> + observer_wx:create_txt_dialog(Parent, Error, "Error", ?wxICON_ERROR), + ok + end. + +get_label(Frame) -> + Dialog = wxTextEntryDialog:new(Frame, "Enter alias: "), + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + wxTextEntryDialog:getValue(Dialog); + ?wxID_CANCEL -> + throw(cancel) + end. + +ms_from_string(Str) -> + try + Tokens = case erl_scan:string(Str) of + {ok, Ts, _} -> Ts; + {error, {SLine, SMod, SError}, _} -> + throw(io_lib:format("~w: ~s", [SLine,SMod:format_error(SError)])) + end, + Exprs = case erl_parse:parse_exprs(Tokens) of + {ok, T} -> T; + {error, {PLine, PMod, PError}} -> + throw(io_lib:format("~w: ~s", [PLine,PMod:format_error(PError)])) + end, + Term = case Exprs of + [{'fun', _, {clauses, Clauses}}|_] -> + case ms_transform:transform_from_shell(dbg,Clauses,orddict:new()) of + {error, [{_,[{MSLine,Mod,MSInfo}]}],_} -> + throw(io_lib:format("~w: ~p", [MSLine,Mod:format_error(MSInfo)])); + {error, _} -> + throw("Could not convert fun() to match spec"); + Ms -> + Ms + end; + [Expr|_] -> + erl_parse:normalise(Expr) + end, + case erlang:match_spec_test([], Term, trace) of + {ok, _, _, _} -> Term; + {error, List} -> throw([[Error, $\n] || {_, Error} <- List]) + end + catch error:_Reason -> + %% io:format("Bad term: ~s~n ~p in ~p~n", [Str, _Reason, erlang:get_stacktrace()]), + throw("Invalid term") + end. + +add_and_select(Id, MS0, ListBox) -> + [{Str,User}] = ms_names([MS0]), + Sel = case Id >= 0 of + true -> + wxListBox:setString(ListBox, Id, Str), + wxListBox:setClientData(ListBox, Id, User), + Id; + false -> + wxListBox:append(ListBox, Str, User) + end, + wxListBox:setSelection(ListBox, Sel). filter_listbox_data(Input, Data, ListBox) -> - FilteredData = [X || X <- Data, re:run(X, Input) =/= nomatch], + FilteredData = [X || X = {Str, _} <- Data, re:run(Str, Input) =/= nomatch], wxListBox:clear(ListBox), - wxListBox:appendStrings(ListBox, FilteredData), + wxListBox:appendStrings(ListBox, [Str || {Str,_} <- FilteredData]), + wx:foldl(fun({_, Term}, N) -> + wxListBox:setClientData(ListBox, N, Term), + N+1 + end, 0, FilteredData), FilteredData. get_modules(Node) -> @@ -241,41 +451,11 @@ optionpage_top_right(Panel, TopRightSz, Options, Text) -> wxSizer:add(RadioSz, Radio2, []), wxSizer:add(Sizer, RadioSz, [{flag, ?wxLEFT},{border, 20}]), wxSizer:add(TopRightSz, Sizer, Options), - wxCheckBox:connect(ChkBox, command_checkbox_clicked, [{userData, {ChkBox, Radio1, Radio2}}]), {ChkBox, Radio1, Radio2}. -read_trace_boxes(ChkBoxes = #boxes{on_spawn = OnSpawn, on_link = OnLink}, Options) -> - {On1stSpawn2, OnAllSpawn2} = - case wxCheckBox:isChecked(OnSpawn#on_spawn.checkbox) of - true -> - OnAllSpawn = wxRadioButton:getValue(OnSpawn#on_spawn.all_spawn), - On1stSpawn = wxRadioButton:getValue(OnSpawn#on_spawn.first_spawn), - {On1stSpawn, OnAllSpawn}; - false -> - {false, false} - end, - {On1stLink2, OnAllLink2} = - case wxCheckBox:isChecked(OnLink#on_link.checkbox) of - true -> - OnAllLink = wxRadioButton:getValue(OnLink#on_link.all_link), - On1stLink = wxRadioButton:getValue(OnLink#on_link.first_link), - {On1stLink, OnAllLink}; - false -> - {false, false} - end, - Options#trace_options{send = wxCheckBox:isChecked(ChkBoxes#boxes.send), - treceive = wxCheckBox:isChecked(ChkBoxes#boxes.'receive'), - functions = wxCheckBox:isChecked(ChkBoxes#boxes.functions), - events = wxCheckBox:isChecked(ChkBoxes#boxes.events), - on_all_spawn = OnAllSpawn2, - on_1st_spawn = On1stSpawn2, - on_all_link = OnAllLink2, - on_1st_link = On1stLink2}. - - create_styled_txtctrl(Parent) -> - FixedFont = wxFont:new(12, ?wxFONTFAMILY_TELETYPE, ?wxFONTSTYLE_NORMAL, ?wxNORMAL,[]), + FixedFont = observer_wx:get_attrib({font, modern}), Ed = wxStyledTextCtrl:new(Parent), wxStyledTextCtrl:styleClearAll(Ed), wxStyledTextCtrl:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont), @@ -314,14 +494,12 @@ keyWords() -> lists:flatten([K ++ " " || K <- L] ++ [0]). -enable({CheckBox, AllRadio, FirstRadio}) -> +enable(CheckBox, Radio) -> case wxCheckBox:isChecked(CheckBox) of false -> - wxWindow:disable(AllRadio), - wxWindow:disable(FirstRadio); + [wxWindow:disable(R) || R <- Radio]; true -> - wxWindow:enable(AllRadio), - wxWindow:enable(FirstRadio) + [wxWindow:enable(R) || R <- Radio] end. @@ -333,18 +511,14 @@ check_box(ChkBox, Bool) -> ignore end. -parse_record_function_names(RecordList) -> - StrList = [atom_to_list(FName) ++ "/" ++ integer_to_list(Arity) - || #traced_func{func_name = FName, arity = Arity} <- RecordList], - parse_function_names(StrList, []). - parse_function_names(Choices) -> - StrList = [atom_to_list(Name) ++ "/" ++ integer_to_list(Arity) || {Name, Arity} <- Choices], + StrList = [{atom_to_list(Name) ++ "/" ++ integer_to_list(Arity), Term} + || Term = {Name, Arity} <- Choices], parse_function_names(StrList, []). parse_function_names([], Acc) -> lists:reverse(Acc); -parse_function_names([H|T], Acc) -> +parse_function_names([{H, Term}|T], Acc) -> IsFun = re:run(H, ".*-fun-\\d*?-"), IsLc = re:run(H, ".*-lc\\$\\^\\d*?/\\d*?-\\d*?-"), IsLbc = re:run(H, ".*-lbc\\$\\^\\d*?/\\d*?-\\d*?-"), @@ -355,209 +529,204 @@ parse_function_names([H|T], Acc) -> true -> H end, - parse_function_names(T, [Parsed | Acc]). + parse_function_names(T, [{Parsed, Term} | Acc]). -show_ms_in_savedlistbox(MatchSpecList) -> - MsOrAlias = fun(#match_spec{alias = A, str_ms = M, fun2ms = F}) -> +ms_names(MatchSpecList) -> + MsOrAlias = fun(#match_spec{name = A, str = M}) -> case A of - undefined -> - if - F =:= undefined -> M; - true -> F - end; - _ -> - A + "" -> M; + _ -> A ++ " " ++ M end end, - [MsOrAlias(X) || X <- MatchSpecList]. - - -find_and_format_ms(Selection, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} | T ]) -> - case ((Selection =:= Spec) or (Selection =:= Alias)) or (Selection =:= Fun) of - true -> - if Selection =:= Alias -> - Spec; - true -> - Selection - end; - false -> - find_and_format_ms(Selection, T) - end. - -find_ms(_, []) -> - {nomatch, #match_spec{}}; -find_ms(Str, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} = MS | T ]) -> - case ((Str =:= Spec) or (Str =:= Alias)) or (Str =:= Fun) of - true -> - {match, MS}; - false -> - find_ms(Str, T) - end. - -apply_matchspec(MatchSpec, TracedDict, root) -> - UpdateMS = fun(_Key, RecordList) -> - [X#traced_func{match_spec = MatchSpec} || X <- RecordList] - end, - {ok, dict:map(UpdateMS, TracedDict)}; -apply_matchspec(MatchSpec, TracedDict, {module, Module}) -> - RecordList = dict:fetch(Module, TracedDict), - RecordList2 = [X#traced_func{match_spec = MatchSpec} || X <- RecordList], - {ok, dict:store(Module, RecordList2, TracedDict)}; -apply_matchspec(MatchSpec, TracedDict, {function, Module, TracedFuncRec}) -> - RecordList = dict:fetch(Module, TracedDict), - NewFunc = TracedFuncRec#traced_func{match_spec = MatchSpec}, - RecordList2 = [NewFunc | [X || X <- RecordList, X =/= TracedFuncRec]], - {NewFunc, dict:store(Module, RecordList2, TracedDict)}. - -create_matchspec_page(Parent, MatchSpecs, UserData) -> - Panel = wxPanel:new(Parent), - MainSz = wxBoxSizer:new(?wxVERTICAL), - TxtSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Match specification:"}]), - BtnSz = wxBoxSizer:new(?wxHORIZONTAL), - SavedSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Saved match specifications:"}]), - - TxtCtrl = create_styled_txtctrl(Panel), - wxSizer:add(TxtSz, TxtCtrl, [{flag, ?wxEXPAND}, {proportion, 1}]), - - AddMsBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS, [{label, "Add"}]), - AddMsAliasBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS_ALIAS, [{label, "Add with alias"}]), - Fun2MSBtn = wxButton:new(Panel, ?MATCHPAGE_ADDFUN, [{label, "Add fun"}]), - wxSizer:add(BtnSz, AddMsBtn), - wxSizer:add(BtnSz, AddMsAliasBtn), - wxSizer:add(BtnSz, Fun2MSBtn), - - Choices = show_ms_in_savedlistbox(MatchSpecs), - SavedMSListBox = wxListBox:new(Panel, ?MATCHPAGE_LISTBOX, [{choices, Choices}]), - wxSizer:add(SavedSz, SavedMSListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), - - wxButton:connect(AddMsBtn, command_button_clicked, [{userData, UserData}]), - wxButton:connect(AddMsAliasBtn, command_button_clicked, [{userData, UserData}] ), - wxButton:connect(Fun2MSBtn, command_button_clicked, [{userData, UserData}] ), - wxListBox:connect(SavedMSListBox, command_listbox_selected, [{userData, UserData}] ), - wxSizer:add(MainSz, TxtSz, [{flag, ?wxEXPAND}, {proportion, 1}]), - wxSizer:add(MainSz, BtnSz), - wxSizer:add(MainSz, SavedSz, [{flag, ?wxEXPAND}, {proportion, 1}]), - - wxWindow:setSizer(Panel, MainSz), - {Panel, MainSz, TxtCtrl, SavedMSListBox}. - - - -update_tree(Tree, Dict) -> - RootId = wxTreeCtrl:getRootItem(Tree), - wxTreeCtrl:deleteChildren(Tree, RootId), - - FillTree = fun(KeyAtom, RecordList, acc_in) -> - ParsedList = parse_record_function_names(RecordList), - Module = wxTreeCtrl:appendItem(Tree, RootId, atom_to_list(KeyAtom)), - lists:foldl(fun(TracedFuncRecord, N) -> - FNameStr = lists:nth(N, ParsedList), - wxTreeCtrl:appendItem(Tree, Module, FNameStr, - [{data, TracedFuncRecord}]), - N+1 - end, - 1, RecordList), - wxTreeCtrl:sortChildren(Tree, Module), - acc_in - end, - dict:fold(FillTree, acc_in, Dict), - wxTreeCtrl:sortChildren(Tree, RootId), - wxTreeCtrl:expand(Tree, RootId). - - - - -create_module_popup(Parent, ModuleName, TracedDict) -> - Module = list_to_atom(ModuleName), - Value = dict:find(Module, TracedDict), - TracedModRecs = - case Value of - {ok, V} -> - V; - error -> - [] - end, - Functions = Module:module_info(functions), - Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, not(erl_internal:guard_bif(Name, Arity))]), - ParsedChoices = parse_function_names(Choices), - - Dialog = wxDialog:new(Parent, ?MODULEPOPUP_DIALOG, ModuleName, - [{style, ?wxDEFAULT_FRAME_STYLE}]), - Panel = wxPanel:new(Dialog), - MainSz = wxBoxSizer:new(?wxVERTICAL), - - SelBtnSz = wxBoxSizer:new(?wxHORIZONTAL), - TxtCtrl = wxTextCtrl:new(Panel, ?MODULEPOPUP_TXTCTRL), - SelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Select"}]), - DeSelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Deselect"}]), - SelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Select all"}]), - DeSelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Deselect all"}]), - CheckListBox = wxCheckListBox:new(Panel, ?MODULEPOPUP_CHECKLISTBOX, [{choices, ParsedChoices}, {style, ?wxLB_EXTENDED}]), - Indices = find_index(TracedModRecs, Choices), - lists:foreach(fun(X) -> wxCheckListBox:check(CheckListBox, X) end, Indices), - Selections = [wxControlWithItems:getString(CheckListBox, I) || I <- Indices], - - OKBtn = wxButton:new(Panel, ?wxID_OK, []), - CancelBtn = wxButton:new(Panel, ?wxID_CANCEL, []), - DialogBtnSz = wxStdDialogButtonSizer:new(), - wxStdDialogButtonSizer:addButton(DialogBtnSz, OKBtn), - wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), - wxStdDialogButtonSizer:realize(DialogBtnSz), - - wxSizer:add(SelBtnSz, SelBtn), - wxSizer:add(SelBtnSz, DeSelBtn), - wxSizer:add(SelBtnSz, SelAllBtn), - wxSizer:add(SelBtnSz, DeSelAllBtn), - wxSizer:add(MainSz, TxtCtrl, [{flag, ?wxEXPAND}]), - wxSizer:add(MainSz, CheckListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), - wxSizer:add(MainSz, SelBtnSz, [{flag, ?wxEXPAND}]), - wxSizer:add(MainSz, DialogBtnSz), - wxWindow:setSizer(Panel, MainSz), - - wxButton:connect(SelBtn, command_button_clicked, [{userData, true}]), - wxButton:connect(DeSelBtn, command_button_clicked, [{userData, false}]), - wxButton:connect(SelAllBtn, command_button_clicked, [{userData, true}]), - wxButton:connect(DeSelAllBtn, command_button_clicked, [{userData, false}]), - wxButton:connect(OKBtn, command_button_clicked, [{userData, {module_popup, Module, ParsedChoices, Choices}}]), - wxButton:connect(CancelBtn, command_button_clicked, [{userData, module_popup}]), - wxTextCtrl:connect(TxtCtrl, command_text_updated, [{userData, ParsedChoices}]), - wxCheckListBox:connect(CheckListBox, command_checklistbox_toggled), - wxDialog:connect(Dialog, close_window), - wxDialog:show(Dialog), - {Dialog, CheckListBox, Selections}. - -get_selections(Selections, FunctionList) -> - get_selections(Selections, FunctionList, []). -get_selections([], _, Acc) -> - Acc; -get_selections([Int|T], FuncList, Acc) -> - get_selections(T, FuncList, [lists:nth(Int, FuncList) | Acc]). - -find_index(Selections, FunctionList) -> - find_index(Selections, FunctionList, 1, []). -find_index(Selections, FunctionList, N, Acc) when N > length(FunctionList); Selections =:= [] -> - Acc; - -find_index([#traced_func{func_name = Name, arity = Arity} |STail] = Selections, - FunctionList, N, Acc) -> - {Fname, A} = lists:nth(N, FunctionList), - case (Fname =:= Name) and (A =:= Arity) of - true -> - find_index(STail, FunctionList, 1, [N-1|Acc]); - false -> - find_index(Selections, FunctionList, N+1, Acc) - end; - -find_index([Sel|STail] = Selections, FunctionList, N, Acc) when is_list(Sel) -> - case lists:nth(N, FunctionList) =:= Sel of - true -> - find_index(STail, FunctionList, 1, [N-1|Acc]); - false -> - find_index(Selections, FunctionList, N+1, Acc) - end. - -atomlist_to_stringlist(Modules) -> - [atom_to_list(X) || X <- Modules]. + [{MsOrAlias(X), X} || X <- MatchSpecList]. + + +%% find_and_format_ms(Selection, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} | T ]) -> +%% case ((Selection =:= Spec) or (Selection =:= Alias)) or (Selection =:= Fun) of +%% true -> +%% if Selection =:= Alias -> +%% Spec; +%% true -> +%% Selection +%% end; +%% false -> +%% find_and_format_ms(Selection, T) +%% end. + +%% find_ms(_, []) -> +%% {nomatch, #match_spec{}}; +%% find_ms(Str, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} = MS | T ]) -> +%% case ((Str =:= Spec) or (Str =:= Alias)) or (Str =:= Fun) of +%% true -> +%% {match, MS}; +%% false -> +%% find_ms(Str, T) +%% end. + +%% apply_matchspec(MatchSpec, TracedDict, root) -> +%% UpdateMS = fun(_Key, RecordList) -> +%% [X#traced_func{match_spec = MatchSpec} || X <- RecordList] +%% end, +%% {ok, dict:map(UpdateMS, TracedDict)}; +%% apply_matchspec(MatchSpec, TracedDict, {module, Module}) -> +%% RecordList = dict:fetch(Module, TracedDict), +%% RecordList2 = [X#traced_func{match_spec = MatchSpec} || X <- RecordList], +%% {ok, dict:store(Module, RecordList2, TracedDict)}; +%% apply_matchspec(MatchSpec, TracedDict, {function, Module, TracedFuncRec}) -> +%% RecordList = dict:fetch(Module, TracedDict), +%% NewFunc = TracedFuncRec#traced_func{match_spec = MatchSpec}, +%% RecordList2 = [NewFunc | [X || X <- RecordList, X =/= TracedFuncRec]], +%% {NewFunc, dict:store(Module, RecordList2, TracedDict)}. + +%% create_matchspec_page(Parent, MatchSpecs, UserData) -> +%% Panel = wxPanel:new(Parent), +%% MainSz = wxBoxSizer:new(?wxVERTICAL), +%% TxtSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Match specification:"}]), +%% BtnSz = wxBoxSizer:new(?wxHORIZONTAL), +%% SavedSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Saved match specifications:"}]), + +%% TxtCtrl = create_styled_txtctrl(Panel), +%% wxSizer:add(TxtSz, TxtCtrl, [{flag, ?wxEXPAND}, {proportion, 1}]), + +%% AddMsBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS, [{label, "Add"}]), +%% AddMsAliasBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS_ALIAS, [{label, "Add with alias"}]), +%% Fun2MSBtn = wxButton:new(Panel, ?MATCHPAGE_ADDFUN, [{label, "Add fun"}]), +%% wxSizer:add(BtnSz, AddMsBtn), +%% wxSizer:add(BtnSz, AddMsAliasBtn), +%% wxSizer:add(BtnSz, Fun2MSBtn), + +%% Choices = show_ms_in_savedlistbox(MatchSpecs), +%% SavedMSListBox = wxListBox:new(Panel, ?MATCHPAGE_LISTBOX, [{choices, Choices}]), +%% wxSizer:add(SavedSz, SavedMSListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), + +%% wxButton:connect(AddMsBtn, command_button_clicked, [{userData, UserData}]), +%% wxButton:connect(AddMsAliasBtn, command_button_clicked, [{userData, UserData}] ), +%% wxButton:connect(Fun2MSBtn, command_button_clicked, [{userData, UserData}] ), +%% wxListBox:connect(SavedMSListBox, command_listbox_selected, [{userData, UserData}] ), +%% wxSizer:add(MainSz, TxtSz, [{flag, ?wxEXPAND}, {proportion, 1}]), +%% wxSizer:add(MainSz, BtnSz), +%% wxSizer:add(MainSz, SavedSz, [{flag, ?wxEXPAND}, {proportion, 1}]), + +%% wxWindow:setSizer(Panel, MainSz), +%% {Panel, MainSz, TxtCtrl, SavedMSListBox}. + + + +%% update_tree(Tree, Dict) -> +%% RootId = wxTreeCtrl:getRootItem(Tree), +%% wxTreeCtrl:deleteChildren(Tree, RootId), + +%% FillTree = fun(KeyAtom, RecordList, acc_in) -> +%% ParsedList = parse_record_function_names(RecordList), +%% Module = wxTreeCtrl:appendItem(Tree, RootId, atom_to_list(KeyAtom)), +%% lists:foldl(fun(TracedFuncRecord, N) -> +%% FNameStr = lists:nth(N, ParsedList), +%% wxTreeCtrl:appendItem(Tree, Module, FNameStr, +%% [{data, TracedFuncRecord}]), +%% N+1 +%% end, +%% 1, RecordList), +%% wxTreeCtrl:sortChildren(Tree, Module), +%% acc_in +%% end, +%% dict:fold(FillTree, acc_in, Dict), +%% wxTreeCtrl:sortChildren(Tree, RootId), +%% wxTreeCtrl:expand(Tree, RootId). + + + + +%% create_module_popup(Parent, ModuleName, TracedDict) -> +%% Module = list_to_atom(ModuleName), +%% Value = dict:find(Module, TracedDict), +%% TracedModRecs = +%% case Value of +%% {ok, V} -> +%% V; +%% error -> +%% [] +%% end, +%% Functions = Module:module_info(functions), +%% Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, not(erl_internal:guard_bif(Name, Arity))]), +%% ParsedChoices = parse_function_names(Choices), + +%% Dialog = wxDialog:new(Parent, ?MODULEPOPUP_DIALOG, ModuleName, +%% [{style, ?wxDEFAULT_FRAME_STYLE}]), +%% Panel = wxPanel:new(Dialog), +%% MainSz = wxBoxSizer:new(?wxVERTICAL), + +%% SelBtnSz = wxBoxSizer:new(?wxHORIZONTAL), +%% TxtCtrl = wxTextCtrl:new(Panel, ?MODULEPOPUP_TXTCTRL), +%% SelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Select"}]), +%% DeSelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Deselect"}]), +%% SelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Select all"}]), +%% DeSelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Deselect all"}]), +%% CheckListBox = wxCheckListBox:new(Panel, ?MODULEPOPUP_CHECKLISTBOX, [{choices, ParsedChoices}, {style, ?wxLB_EXTENDED}]), +%% Indices = find_index(TracedModRecs, Choices), +%% lists:foreach(fun(X) -> wxCheckListBox:check(CheckListBox, X) end, Indices), +%% Selections = [wxControlWithItems:getString(CheckListBox, I) || I <- Indices], + +%% OKBtn = wxButton:new(Panel, ?wxID_OK, []), +%% CancelBtn = wxButton:new(Panel, ?wxID_CANCEL, []), +%% DialogBtnSz = wxStdDialogButtonSizer:new(), +%% wxStdDialogButtonSizer:addButton(DialogBtnSz, OKBtn), +%% wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), +%% wxStdDialogButtonSizer:realize(DialogBtnSz), + +%% wxSizer:add(SelBtnSz, SelBtn), +%% wxSizer:add(SelBtnSz, DeSelBtn), +%% wxSizer:add(SelBtnSz, SelAllBtn), +%% wxSizer:add(SelBtnSz, DeSelAllBtn), +%% wxSizer:add(MainSz, TxtCtrl, [{flag, ?wxEXPAND}]), +%% wxSizer:add(MainSz, CheckListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), +%% wxSizer:add(MainSz, SelBtnSz, [{flag, ?wxEXPAND}]), +%% wxSizer:add(MainSz, DialogBtnSz), +%% wxWindow:setSizer(Panel, MainSz), + +%% wxButton:connect(SelBtn, command_button_clicked, [{userData, true}]), +%% wxButton:connect(DeSelBtn, command_button_clicked, [{userData, false}]), +%% wxButton:connect(SelAllBtn, command_button_clicked, [{userData, true}]), +%% wxButton:connect(DeSelAllBtn, command_button_clicked, [{userData, false}]), +%% wxButton:connect(OKBtn, command_button_clicked, [{userData, {module_popup, Module, ParsedChoices, Choices}}]), +%% wxButton:connect(CancelBtn, command_button_clicked, [{userData, module_popup}]), +%% wxTextCtrl:connect(TxtCtrl, command_text_updated, [{userData, ParsedChoices}]), +%% wxCheckListBox:connect(CheckListBox, command_checklistbox_toggled), +%% wxDialog:connect(Dialog, close_window), +%% wxDialog:show(Dialog), +%% {Dialog, CheckListBox, Selections}. + +%% get_selections(Selections, FunctionList) -> +%% get_selections(Selections, FunctionList, []). +%% get_selections([], _, Acc) -> +%% Acc; +%% get_selections([Int|T], FuncList, Acc) -> +%% get_selections(T, FuncList, [lists:nth(Int, FuncList) | Acc]). + +%% find_index(Selections, FunctionList) -> +%% find_index(Selections, FunctionList, 1, []). +%% find_index(Selections, FunctionList, N, Acc) when N > length(FunctionList); Selections =:= [] -> +%% Acc; + +%% find_index([#traced_func{func_name = Name, arity = Arity} |STail] = Selections, +%% FunctionList, N, Acc) -> +%% {Fname, A} = lists:nth(N, FunctionList), +%% case (Fname =:= Name) and (A =:= Arity) of +%% true -> +%% find_index(STail, FunctionList, 1, [N-1|Acc]); +%% false -> +%% find_index(Selections, FunctionList, N+1, Acc) +%% end; + +%% find_index([Sel|STail] = Selections, FunctionList, N, Acc) when is_list(Sel) -> +%% case lists:nth(N, FunctionList) =:= Sel of +%% true -> +%% find_index(STail, FunctionList, 1, [N-1|Acc]); +%% false -> +%% find_index(Selections, FunctionList, N+1, Acc) +%% end. + +%% atomlist_to_stringlist(Modules) -> +%% [atom_to_list(X) || X <- Modules]. ensure_last_is_dot([]) -> "."; @@ -569,498 +738,446 @@ ensure_last_is_dot(String) -> String ++ "." end. -check_correct_MS(String) -> - Tokens = try_scan(String), - case try_parse(Tokens) of - {ok, Term} -> - case erlang:match_spec_test([], Term, trace) of - {ok, _, _, _} -> - {true, Term}; - {error, List} -> - Reason = unparse_error_msg(List, []), - {false, Reason} - end; - error -> - {false, "Invalid term"} - end. - -unparse_error_msg([], Acc) -> - lists:reverse(Acc); -unparse_error_msg([{_, Reason} | T], Acc) -> - unparse_error_msg(T, [Reason ++ "\n" | Acc]). - -try_scan(String) -> - try - erl_scan:string(String) of - {ok, T, _} -> - T; - _ -> - error - catch - _:_ -> error - end. - -try_parse(Tokens) -> - try - erl_parse:parse_term(Tokens) of - {ok, Term} -> - {ok, Term}; - _ -> - error - catch - _:_ -> - error - end. - -update_matchspec_listbox(Str, {PopupBox, PageBox}, From) -> - case From of - matchpopup -> - wxControlWithItems:append(PageBox, Str), - wxControlWithItems:append(PopupBox, Str); - matchpage -> - wxControlWithItems:append(PageBox, Str) - end. - - -dbg_from_string(Str0) -> - Str = unicode:characters_to_list(Str0), - case erl_scan:string(Str) of - {ok, Tokens,_} -> - case erl_parse:parse_exprs(Tokens) of - {ok,[{'fun',_,{clauses, Cl}}]} -> - case ms_transform: - transform_from_shell(dbg,Cl,orddict:new()) of - {error, [{_,[{Line,ms_transform,Info}]}],_} -> - {error,{Line,ms_transform,Info}}; - {error, _} = ET1 -> - ET1; - Else -> - {ok, Else, "[" ++ lists:flatten(io_lib:format("~p", Else)) ++ "]"} - end; - {ok,_} -> - {error, {1,ms_transform,1}}; - {error,Reason} -> - {error,Reason} - end; - {error,Reason2,_} -> - {error,Reason2} - end. - -get_correct_matchspec_components(From, State) -> - case From of - matchpage -> - {State#traceopts_state.matchpage_styled_txtctrl, - State#traceopts_state.frame}; - matchpopup -> - {State#traceopts_state.matchspec_popup_styled_txtctrl, - State#traceopts_state.matchspec_popup_dialog} - end. - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %Trace option window - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% All pages - -handle_event(#wx{id = ?wxID_OK, - event = #wxCommand{type = command_button_clicked}, - userData = trace_options}, - #traceopts_state{boxes = Boxes, - trace_options = TraceOpts, - match_specs = MatchSpecs, - traced_funcs = TracedFuncs, - parent = Parent} = State) -> - UpdTraceOpts = wx:batch(fun() -> - read_trace_boxes(Boxes, TraceOpts) - end), - Parent ! {updated_traceopts, - UpdTraceOpts, - MatchSpecs, - TracedFuncs}, - {stop, shutdown, State}; - -handle_event(#wx{id = ?wxID_CANCEL, - event = #wxCommand{type = command_button_clicked}, - userData = trace_options}, - #traceopts_state{parent = Parent} = State) -> - Parent ! traceopts_closed, - {stop, shutdown, State}; - -handle_event(#wx{id = ?TRACEOPTS_FRAME, - event = #wxClose{type = close_window}}, - #traceopts_state{parent = Parent} = State) -> - Parent ! traceopts_closed, - {stop, shutdown, State}; - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Tracing - -handle_event(#wx{event = #wxCommand{type = command_checkbox_clicked}, userData = Boxgroup}, - State) -> - enable(Boxgroup), - {noreply, State}; - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Functions - -handle_event(#wx{id = ?FUNCTIONPAGE_LISTBOX, - event = #wxCommand{type = command_listbox_doubleclicked, - cmdString = ChosenModule}}, - #traceopts_state{frame = Frame, - traced_funcs = TracedDict, - popup_open = false} = State) -> - {Dialog, CheckListBox, CheckedFuncs} = create_module_popup(Frame, ChosenModule, TracedDict), - {noreply, State#traceopts_state{popup_open = true, - module_popup_dialog = Dialog, - module_popup_checklistbox = CheckListBox, - checked_funcs = CheckedFuncs}}; - -handle_event(#wx{id = ?FUNCTIONPAGE_TXTCTRL, - event = #wxCommand{type = command_text_updated, - cmdString = Input}, - userData = Data}, - #traceopts_state{functionpage_listbox = ListBox} = State) -> - filter_listbox_data(Input, Data, ListBox), - {noreply, State}; - -handle_event(#wx{event = #wxTree{type = command_tree_item_activated, - item = Item}}, - #traceopts_state{frame = Frame, - match_specs = MatchSpecs, - popup_open = false} = State) -> - - Dialog = wxDialog:new(Frame, ?MATCH_POPUP_DIALOG, "Match specification", - [{style, ?wxDEFAULT_FRAME_STYLE}]), - {MatchPanel, MatchSz, StyledTxtCtrl, ListBox} = create_matchspec_page(Dialog, MatchSpecs, matchpopup), - ApplyBtn = wxButton:new(MatchPanel, ?wxID_APPLY), - CancelBtn = wxButton:new(MatchPanel, ?wxID_CANCEL, []), - wxButton:connect(ApplyBtn, command_button_clicked, [{userData, Item}]), - wxButton:connect(CancelBtn, command_button_clicked, [{userData, matchspec_popup}]), - DialogBtnSz = wxStdDialogButtonSizer:new(), - wxStdDialogButtonSizer:addButton(DialogBtnSz, ApplyBtn), - wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), - wxStdDialogButtonSizer:realize(DialogBtnSz), - wxSizer:add(MatchSz, DialogBtnSz), - - wxDialog:connect(Dialog, close_window), - wxDialog:show(Dialog), - {noreply, State#traceopts_state{matchspec_popup_dialog = Dialog, - matchspec_popup_listbox = ListBox, - matchspec_popup_styled_txtctrl = StyledTxtCtrl, - popup_open = true}}; - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Match specs - -handle_event(#wx{event = #wxCommand{type = command_listbox_selected, - cmdString = Txt}}, - State) when Txt =:= [] -> - {noreply, State}; - -handle_event(#wx{id = ?MATCHPAGE_LISTBOX, - event = #wxCommand{type = command_listbox_selected, - cmdString = SavedTxt}, - userData = From}, - #traceopts_state{match_specs = MatchSpecs} = State) -> - {StyledTxtCtrl, _} = get_correct_matchspec_components(From, State), - MsOrFun = find_and_format_ms(SavedTxt, MatchSpecs), - wxStyledTextCtrl:setText(StyledTxtCtrl, MsOrFun), - {noreply, State}; - -handle_event(#wx{id = ?MATCHPAGE_ADDFUN, - event = #wxCommand{type = command_button_clicked}, - userData = From}, - #traceopts_state{match_specs = MatchSpecs, - matchpage_listbox = PageListBox, - matchspec_popup_listbox = PopupListBox} = State) -> - - {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), - StrFun = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), - - MatchSpecs2 = case dbg_from_string(StrFun) of - {ok, TermMS, StrMS} -> - FunMS = #match_spec{str_ms = StrMS, term_ms = TermMS, fun2ms = StrFun}, - case lists:member(FunMS, MatchSpecs) of - true -> - observer_wx:create_txt_dialog(Frame, StrFun ++ "\nalready exists", - "Error", ?wxICON_ERROR), - MatchSpecs; - false -> - wxStyledTextCtrl:setText(StyledTxtCtrl, StrMS), - update_matchspec_listbox(StrFun, {PopupListBox, PageListBox}, From), - lists:reverse([FunMS | MatchSpecs]) - end; - {error, {_, Module, What}} -> - FailMsg = Module:format_error(What), - observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR), - MatchSpecs - end, - {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - -handle_event(#wx{id = ?MATCHPAGE_ADDMS, - event = #wxCommand{type = command_button_clicked}, - userData = From}, - #traceopts_state{match_specs = MatchSpecs, - matchpage_listbox = PageListBox, - matchspec_popup_listbox = PopupListBox} = State) -> - - {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), - StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), - MatchSpecs2 = case check_correct_MS(StrMS) of - {true, TermMS} -> - MS = #match_spec{str_ms = StrMS, term_ms = TermMS}, - case lists:member(MS, MatchSpecs) of - true -> - observer_wx:create_txt_dialog(Frame, StrMS ++ "\nalready exists", - "Error", ?wxICON_ERROR), - MatchSpecs; - false -> - update_matchspec_listbox(StrMS, {PopupListBox, PageListBox}, From), - lists:reverse([MS | MatchSpecs]) - end; - {false, Reason} -> - observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), - MatchSpecs - end, - {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - - -handle_event(#wx{id = ?MATCHPAGE_ADDMS_ALIAS, - event = #wxCommand{type = command_button_clicked}, - userData = From}, - #traceopts_state{match_specs = MatchSpecs, - matchpage_listbox = PageListBox, - matchspec_popup_listbox = PopupListBox} = State) -> - - {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), - StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), - - MatchSpecs2 = case check_correct_MS(StrMS) of - {true, TermMS} -> - Dialog = wxTextEntryDialog:new(Frame, "Enter ms alias: "), - Alias = case wxDialog:showModal(Dialog) of - ?wxID_OK -> - wxTextEntryDialog:getValue(Dialog); - ?wxID_CANCEL -> - "" - end, - wxDialog:destroy(Dialog), - - case Alias of - "" -> - observer_wx:create_txt_dialog(Frame, "Bad alias", "Syntax error", - ?wxICON_ERROR), - MatchSpecs; - - _ -> - MS = #match_spec{alias = Alias, str_ms = StrMS, - term_ms = TermMS}, - {OccupiedAlias, _} = find_ms(Alias, MatchSpecs), - - if - OccupiedAlias =:= match -> - observer_wx:create_txt_dialog(Frame, "Alias " ++ Alias ++ " already exists", - "Error", ?wxICON_ERROR), - MatchSpecs; - true -> - update_matchspec_listbox(Alias, {PopupListBox, PageListBox}, From), - lists:reverse([MS | MatchSpecs]) - end - end; - {false, Reason} -> - observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), - MatchSpecs - end, - {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - - -handle_event(#wx{id = ?wxID_APPLY, - event = #wxCommand{type = command_button_clicked}, - userData = Item}, - #traceopts_state{matchspec_popup_dialog = Dialog, - matchspec_popup_listbox = ListBox, - tree = Tree, - match_specs = MatchSpecs, - traced_funcs = TracedDict} = State) -> - IntSelection = wxListBox:getSelection(ListBox), - StrSelection = - case IntSelection >= 0 of - true -> - wxControlWithItems:getString(ListBox, IntSelection); - false -> - [] - end, - {_, MS} = find_ms(StrSelection, MatchSpecs), - RootId = wxTreeCtrl:getRootItem(Tree), - ItemParent = wxTreeCtrl:getItemParent(Tree, Item), - - TracedDict2 = - if (Item =:= RootId) -> - {ok, NewDict} = apply_matchspec(MS, TracedDict, root), - NewDict; - (ItemParent =:= RootId) -> - Module = list_to_atom(wxTreeCtrl:getItemText(Tree, Item)), - {ok, NewDict} = apply_matchspec(MS, TracedDict, {module, Module}), - NewDict; - true -> - TracedFuncRec = wxTreeCtrl:getItemData(Tree, Item), - Module = list_to_atom(wxTreeCtrl:getItemText(Tree, ItemParent)), - {NewTracedFuncRecord, NewDict} = - apply_matchspec(MS, - TracedDict, - {function, - Module, - TracedFuncRec}), - wxTreeCtrl:setItemData(Tree, Item, NewTracedFuncRecord), - NewDict - end, - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{traced_funcs = TracedDict2, - popup_open = false}}; - -handle_event(#wx{id = ?wxID_CANCEL, - event = #wxCommand{type = command_button_clicked}, - userData = matchspec_popup}, - #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{popup_open = false}}; - -handle_event(#wx{id = ?MATCH_POPUP_DIALOG, - event = #wxClose{type = close_window}}, - #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{popup_open = false}}; - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %Module Popup - -handle_event(#wx{id = ?wxID_OK, - event = #wxCommand{type = command_button_clicked}, - userData = {module_popup, Module, - ParsedChoices, Choices}}, - #traceopts_state{ - module_popup_dialog = Dialog, - traced_funcs = TracedDict, - tree = Tree, - checked_funcs = CheckedFuncs} = State) -> - - Indices = [I+1 || I <- find_index(CheckedFuncs, ParsedChoices)], - Selections = get_selections(Indices, Choices), - TracedDict2 = case Selections of - [] -> - dict:erase(Module, TracedDict); - _ -> - Traced = [#traced_func{arity = Arity, - func_name = Function} - || {Function, Arity} <- Selections], - dict:store(Module, Traced, TracedDict) - end, - - update_tree(Tree, TracedDict2), - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{traced_funcs = TracedDict2, - checked_funcs = [], - popup_open = false}}; - -handle_event(#wx{id = ?wxID_CANCEL, - event = #wxCommand{type = command_button_clicked}, - userData = module_popup}, - #traceopts_state{module_popup_dialog = Dialog} = State) -> - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{popup_open = false, - checked_funcs = []}}; - -handle_event(#wx{id = ?MODULEPOPUP_SELECT, - event = #wxCommand{type = command_button_clicked}, - userData = Bool}, - #traceopts_state{module_popup_checklistbox = CheckListBox, - checked_funcs = CheckedFuncs} = State) -> - {_, Selections} = wxListBox:getSelections(CheckListBox), - lists:foreach(fun(Index) -> wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) end, Selections), - StrSelections = [wxControlWithItems:getString(CheckListBox, N) || N <- Selections], - CheckedFuncs2 = case Bool of - true -> - [X || X <- StrSelections, - not(lists:member(X, CheckedFuncs))] ++ CheckedFuncs; - false -> - CheckedFuncs -- StrSelections - end, - {noreply, State#traceopts_state{checked_funcs = CheckedFuncs2}}; - -handle_event(#wx{id = ?MODULEPOPUP_SELALL, - event = #wxCommand{type = command_button_clicked}, - userData = Bool}, - #traceopts_state{module_popup_checklistbox = CheckListBox} = State) -> - lists:foreach(fun(Index) -> - wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) - end, - lists:seq(0, wxControlWithItems:getCount(CheckListBox))), - CheckedFuncs = case Bool of - true -> - [wxControlWithItems:getString(CheckListBox, N) - || N <- lists:seq(0, wxControlWithItems:getCount(CheckListBox))]; - false -> - [] - end, - {noreply, State#traceopts_state{checked_funcs = CheckedFuncs}}; - -handle_event(#wx{id = ?MODULEPOPUP_CHECKLISTBOX, - obj = CheckListBox, - event = #wxCommand{type = command_checklistbox_toggled, - commandInt = Index}}, - #traceopts_state{checked_funcs = CheckedFuncs} = State) -> - - UpdCheckedFuncs = case - wxCheckListBox:isChecked(CheckListBox, Index) of - true -> - [wxControlWithItems:getString(CheckListBox, Index) | CheckedFuncs]; - false -> - lists:delete(wxControlWithItems:getString(CheckListBox, Index), CheckedFuncs) - end, - {noreply, State#traceopts_state{checked_funcs = UpdCheckedFuncs}}; - -handle_event(#wx{id = ?MODULEPOPUP_TXTCTRL, - event = #wxCommand{type = command_text_updated, - cmdString = Input}, - userData = Data}, - #traceopts_state{module_popup_checklistbox = CListBox, - checked_funcs = CheckedFuncs} = State) -> - FilteredData = filter_listbox_data(Input, Data, CListBox), - lists:foreach(fun(Index) -> - wxCheckListBox:check(CListBox, Index, [{check, true}]) - end, - [wxControlWithItems:findString(CListBox, X) || X <- CheckedFuncs, lists:member(X, FilteredData)]), - {noreply, State}; - -handle_event(#wx{id = ?MODULEPOPUP_DIALOG, - event = #wxClose{type = close_window}}, - #traceopts_state{module_popup_dialog = Dialog} = State) -> - wxDialog:destroy(Dialog), - {noreply, State#traceopts_state{popup_open = false, - checked_funcs = []}}; - -handle_event(#wx{event = What}, State) -> - io:format("~p~p: Unhandled event: ~p ~n", [?MODULE, self(), What]), - {noreply, State}. - - - -terminate(Reason, #traceopts_state{frame = Frame}) -> - io:format("~p terminating traceopts. Reason: ~p~n", [?MODULE, Reason]), - wxFrame:destroy(Frame), - ok. - -code_change(_, _, State) -> - {stop, not_yet_implemented, State}. - -handle_info(Any, State) -> - io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]), - {noreply, State}. - -handle_call(Msg, _From, State) -> - io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), - {reply, ok, State}. -handle_cast(Msg, State) -> - io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), - {noreply, State}. +%% dbg_from_string(Str0) -> +%% Str = unicode:characters_to_list(Str0), +%% case erl_scan:string(Str) of +%% {ok, Tokens,_} -> +%% case erl_parse:parse_exprs(Tokens) of +%% {ok,[{'fun',_,{clauses, Cl}}]} -> +%% case ms_transform: +%% transform_from_shell(dbg,Cl,orddict:new()) of +%% {error, [{_,[{Line,ms_transform,Info}]}],_} -> +%% {error,{Line,ms_transform,Info}}; +%% {error, _} = ET1 -> +%% ET1; +%% Else -> +%% {ok, Else, "[" ++ lists:flatten(io_lib:format("~p", Else)) ++ "]"} +%% end; +%% {ok,_} -> +%% {error, {1,ms_transform,1}}; +%% {error,Reason} -> +%% {error,Reason} +%% end; +%% {error,Reason2,_} -> +%% {error,Reason2} +%% end. + +%% get_correct_matchspec_components(From, State) -> +%% case From of +%% matchpage -> +%% {State#traceopts_state.matchpage_styled_txtctrl, +%% State#traceopts_state.frame}; +%% matchpopup -> +%% {State#traceopts_state.matchspec_popup_styled_txtctrl, +%% State#traceopts_state.matchspec_popup_dialog} +%% end. + + + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %Trace option window + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% All pages + +%% handle_event(#wx{id = ?wxID_OK, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = trace_options}, +%% #traceopts_state{boxes = Boxes, +%% trace_options = TraceOpts, +%% match_specs = MatchSpecs, +%% traced_funcs = TracedFuncs, +%% parent = Parent} = State) -> +%% UpdTraceOpts = wx:batch(fun() -> +%% read_trace_boxes(Boxes, TraceOpts) +%% end), +%% Parent ! {updated_traceopts, +%% UpdTraceOpts, +%% MatchSpecs, +%% TracedFuncs}, +%% {stop, shutdown, State}; + +%% handle_event(#wx{id = ?wxID_CANCEL, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = trace_options}, +%% #traceopts_state{parent = Parent} = State) -> +%% Parent ! traceopts_closed, +%% {stop, shutdown, State}; + +%% handle_event(#wx{id = ?TRACEOPTS_FRAME, +%% event = #wxClose{type = close_window}}, +%% #traceopts_state{parent = Parent} = State) -> +%% Parent ! traceopts_closed, +%% {stop, shutdown, State}; + + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Tracing + +%% handle_event(#wx{event = #wxCommand{type = command_checkbox_clicked}, userData = Boxgroup}, +%% State) -> +%% enable(Boxgroup), +%% {noreply, State}; + + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Functions + +%% handle_event(#wx{id = ?FUNCTIONPAGE_LISTBOX, +%% event = #wxCommand{type = command_listbox_doubleclicked, +%% cmdString = ChosenModule}}, +%% #traceopts_state{frame = Frame, +%% traced_funcs = TracedDict, +%% popup_open = false} = State) -> +%% {Dialog, CheckListBox, CheckedFuncs} = create_module_popup(Frame, ChosenModule, TracedDict), +%% {noreply, State#traceopts_state{popup_open = true, +%% module_popup_dialog = Dialog, +%% module_popup_checklistbox = CheckListBox, +%% checked_funcs = CheckedFuncs}}; + +%% handle_event(#wx{id = ?FUNCTIONPAGE_TXTCTRL, +%% event = #wxCommand{type = command_text_updated, +%% cmdString = Input}, +%% userData = Data}, +%% #traceopts_state{functionpage_listbox = ListBox} = State) -> +%% filter_listbox_data(Input, Data, ListBox), +%% {noreply, State}; + +%% handle_event(#wx{event = #wxTree{type = command_tree_item_activated, +%% item = Item}}, +%% #traceopts_state{frame = Frame, +%% match_specs = MatchSpecs, +%% popup_open = false} = State) -> + +%% Dialog = wxDialog:new(Frame, ?MATCH_POPUP_DIALOG, "Match specification", +%% [{style, ?wxDEFAULT_FRAME_STYLE}]), +%% {MatchPanel, MatchSz, StyledTxtCtrl, ListBox} = create_matchspec_page(Dialog, MatchSpecs, matchpopup), +%% ApplyBtn = wxButton:new(MatchPanel, ?wxID_APPLY), +%% CancelBtn = wxButton:new(MatchPanel, ?wxID_CANCEL, []), +%% wxButton:connect(ApplyBtn, command_button_clicked, [{userData, Item}]), +%% wxButton:connect(CancelBtn, command_button_clicked, [{userData, matchspec_popup}]), +%% DialogBtnSz = wxStdDialogButtonSizer:new(), +%% wxStdDialogButtonSizer:addButton(DialogBtnSz, ApplyBtn), +%% wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), +%% wxStdDialogButtonSizer:realize(DialogBtnSz), +%% wxSizer:add(MatchSz, DialogBtnSz), + +%% wxDialog:connect(Dialog, close_window), +%% wxDialog:show(Dialog), +%% {noreply, State#traceopts_state{matchspec_popup_dialog = Dialog, +%% matchspec_popup_listbox = ListBox, +%% matchspec_popup_styled_txtctrl = StyledTxtCtrl, +%% popup_open = true}}; + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Match specs + +%% handle_event(#wx{event = #wxCommand{type = command_listbox_selected, +%% cmdString = Txt}}, +%% State) when Txt =:= [] -> +%% {noreply, State}; + +%% handle_event(#wx{id = ?MATCHPAGE_LISTBOX, +%% event = #wxCommand{type = command_listbox_selected, +%% cmdString = SavedTxt}, +%% userData = From}, +%% #traceopts_state{match_specs = MatchSpecs} = State) -> +%% {StyledTxtCtrl, _} = get_correct_matchspec_components(From, State), +%% MsOrFun = find_and_format_ms(SavedTxt, MatchSpecs), +%% wxStyledTextCtrl:setText(StyledTxtCtrl, MsOrFun), +%% {noreply, State}; + +%% handle_event(#wx{id = ?MATCHPAGE_ADDFUN, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = From}, +%% #traceopts_state{match_specs = MatchSpecs, +%% matchpage_listbox = PageListBox, +%% matchspec_popup_listbox = PopupListBox} = State) -> + +%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), +%% StrFun = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), + +%% MatchSpecs2 = case dbg_from_string(StrFun) of +%% {ok, TermMS, StrMS} -> +%% FunMS = #match_spec{str_ms = StrMS, term_ms = TermMS, fun2ms = StrFun}, +%% case lists:member(FunMS, MatchSpecs) of +%% true -> +%% observer_wx:create_txt_dialog(Frame, StrFun ++ "\nalready exists", +%% "Error", ?wxICON_ERROR), +%% MatchSpecs; +%% false -> +%% wxStyledTextCtrl:setText(StyledTxtCtrl, StrMS), +%% update_matchspec_listbox(StrFun, {PopupListBox, PageListBox}, From), +%% lists:reverse([FunMS | MatchSpecs]) +%% end; +%% {error, {_, Module, What}} -> +%% FailMsg = Module:format_error(What), +%% observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR), +%% MatchSpecs +%% end, +%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; + +%% handle_event(#wx{id = ?MATCHPAGE_ADDMS, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = From}, +%% #traceopts_state{match_specs = MatchSpecs, +%% matchpage_listbox = PageListBox, +%% matchspec_popup_listbox = PopupListBox} = State) -> + +%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), +%% StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), +%% MatchSpecs2 = case check_correct_MS(StrMS) of +%% {true, TermMS} -> +%% MS = #match_spec{str_ms = StrMS, term_ms = TermMS}, +%% case lists:member(MS, MatchSpecs) of +%% true -> +%% observer_wx:create_txt_dialog(Frame, StrMS ++ "\nalready exists", +%% "Error", ?wxICON_ERROR), +%% MatchSpecs; +%% false -> +%% update_matchspec_listbox(StrMS, {PopupListBox, PageListBox}, From), +%% lists:reverse([MS | MatchSpecs]) +%% end; +%% {false, Reason} -> +%% observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), +%% MatchSpecs +%% end, +%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; + + +%% handle_event(#wx{id = ?MATCHPAGE_ADDMS_ALIAS, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = From}, +%% #traceopts_state{match_specs = MatchSpecs, +%% matchpage_listbox = PageListBox, +%% matchspec_popup_listbox = PopupListBox} = State) -> + +%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), +%% StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), + +%% MatchSpecs2 = case check_correct_MS(StrMS) of +%% {true, TermMS} -> +%% Dialog = wxTextEntryDialog:new(Frame, "Enter ms alias: "), +%% Alias = case wxDialog:showModal(Dialog) of +%% ?wxID_OK -> +%% wxTextEntryDialog:getValue(Dialog); +%% ?wxID_CANCEL -> +%% "" +%% end, +%% wxDialog:destroy(Dialog), + +%% case Alias of +%% "" -> +%% observer_wx:create_txt_dialog(Frame, "Bad alias", "Syntax error", +%% ?wxICON_ERROR), +%% MatchSpecs; + +%% _ -> +%% MS = #match_spec{alias = Alias, str_ms = StrMS, +%% term_ms = TermMS}, +%% {OccupiedAlias, _} = find_ms(Alias, MatchSpecs), + +%% if +%% OccupiedAlias =:= match -> +%% observer_wx:create_txt_dialog(Frame, "Alias " ++ Alias ++ " already exists", +%% "Error", ?wxICON_ERROR), +%% MatchSpecs; +%% true -> +%% update_matchspec_listbox(Alias, {PopupListBox, PageListBox}, From), +%% lists:reverse([MS | MatchSpecs]) +%% end +%% end; +%% {false, Reason} -> +%% observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), +%% MatchSpecs +%% end, +%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; + + +%% handle_event(#wx{id = ?wxID_APPLY, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = Item}, +%% #traceopts_state{matchspec_popup_dialog = Dialog, +%% matchspec_popup_listbox = ListBox, +%% tree = Tree, +%% match_specs = MatchSpecs, +%% traced_funcs = TracedDict} = State) -> +%% IntSelection = wxListBox:getSelection(ListBox), +%% StrSelection = +%% case IntSelection >= 0 of +%% true -> +%% wxControlWithItems:getString(ListBox, IntSelection); +%% false -> +%% [] +%% end, +%% {_, MS} = find_ms(StrSelection, MatchSpecs), +%% RootId = wxTreeCtrl:getRootItem(Tree), +%% ItemParent = wxTreeCtrl:getItemParent(Tree, Item), + +%% TracedDict2 = +%% if (Item =:= RootId) -> +%% {ok, NewDict} = apply_matchspec(MS, TracedDict, root), +%% NewDict; +%% (ItemParent =:= RootId) -> +%% Module = list_to_atom(wxTreeCtrl:getItemText(Tree, Item)), +%% {ok, NewDict} = apply_matchspec(MS, TracedDict, {module, Module}), +%% NewDict; +%% true -> +%% TracedFuncRec = wxTreeCtrl:getItemData(Tree, Item), +%% Module = list_to_atom(wxTreeCtrl:getItemText(Tree, ItemParent)), +%% {NewTracedFuncRecord, NewDict} = +%% apply_matchspec(MS, +%% TracedDict, +%% {function, +%% Module, +%% TracedFuncRec}), +%% wxTreeCtrl:setItemData(Tree, Item, NewTracedFuncRecord), +%% NewDict +%% end, +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{traced_funcs = TracedDict2, +%% popup_open = false}}; + +%% handle_event(#wx{id = ?wxID_CANCEL, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = matchspec_popup}, +%% #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{popup_open = false}}; + +%% handle_event(#wx{id = ?MATCH_POPUP_DIALOG, +%% event = #wxClose{type = close_window}}, +%% #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{popup_open = false}}; + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% %Module Popup + +%% handle_event(#wx{id = ?wxID_OK, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = {module_popup, Module, +%% ParsedChoices, Choices}}, +%% #traceopts_state{ +%% module_popup_dialog = Dialog, +%% traced_funcs = TracedDict, +%% tree = Tree, +%% checked_funcs = CheckedFuncs} = State) -> + +%% Indices = [I+1 || I <- find_index(CheckedFuncs, ParsedChoices)], +%% Selections = get_selections(Indices, Choices), +%% TracedDict2 = case Selections of +%% [] -> +%% dict:erase(Module, TracedDict); +%% _ -> +%% Traced = [#traced_func{arity = Arity, +%% func_name = Function} +%% || {Function, Arity} <- Selections], +%% dict:store(Module, Traced, TracedDict) +%% end, + +%% update_tree(Tree, TracedDict2), +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{traced_funcs = TracedDict2, +%% checked_funcs = [], +%% popup_open = false}}; + +%% handle_event(#wx{id = ?wxID_CANCEL, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = module_popup}, +%% #traceopts_state{module_popup_dialog = Dialog} = State) -> +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{popup_open = false, +%% checked_funcs = []}}; + +%% handle_event(#wx{id = ?MODULEPOPUP_SELECT, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = Bool}, +%% #traceopts_state{module_popup_checklistbox = CheckListBox, +%% checked_funcs = CheckedFuncs} = State) -> +%% {_, Selections} = wxListBox:getSelections(CheckListBox), +%% lists:foreach(fun(Index) -> wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) end, Selections), +%% StrSelections = [wxControlWithItems:getString(CheckListBox, N) || N <- Selections], +%% CheckedFuncs2 = case Bool of +%% true -> +%% [X || X <- StrSelections, +%% not(lists:member(X, CheckedFuncs))] ++ CheckedFuncs; +%% false -> +%% CheckedFuncs -- StrSelections +%% end, +%% {noreply, State#traceopts_state{checked_funcs = CheckedFuncs2}}; + +%% handle_event(#wx{id = ?MODULEPOPUP_SELALL, +%% event = #wxCommand{type = command_button_clicked}, +%% userData = Bool}, +%% #traceopts_state{module_popup_checklistbox = CheckListBox} = State) -> +%% lists:foreach(fun(Index) -> +%% wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) +%% end, +%% lists:seq(0, wxControlWithItems:getCount(CheckListBox))), +%% CheckedFuncs = case Bool of +%% true -> +%% [wxControlWithItems:getString(CheckListBox, N) +%% || N <- lists:seq(0, wxControlWithItems:getCount(CheckListBox))]; +%% false -> +%% [] +%% end, +%% {noreply, State#traceopts_state{checked_funcs = CheckedFuncs}}; + +%% handle_event(#wx{id = ?MODULEPOPUP_CHECKLISTBOX, +%% obj = CheckListBox, +%% event = #wxCommand{type = command_checklistbox_toggled, +%% commandInt = Index}}, +%% #traceopts_state{checked_funcs = CheckedFuncs} = State) -> + +%% UpdCheckedFuncs = case +%% wxCheckListBox:isChecked(CheckListBox, Index) of +%% true -> +%% [wxControlWithItems:getString(CheckListBox, Index) | CheckedFuncs]; +%% false -> +%% lists:delete(wxControlWithItems:getString(CheckListBox, Index), CheckedFuncs) +%% end, +%% {noreply, State#traceopts_state{checked_funcs = UpdCheckedFuncs}}; + +%% handle_event(#wx{id = ?MODULEPOPUP_TXTCTRL, +%% event = #wxCommand{type = command_text_updated, +%% cmdString = Input}, +%% userData = Data}, +%% #traceopts_state{module_popup_checklistbox = CListBox, +%% checked_funcs = CheckedFuncs} = State) -> +%% FilteredData = filter_listbox_data(Input, Data, CListBox), +%% lists:foreach(fun(Index) -> +%% wxCheckListBox:check(CListBox, Index, [{check, true}]) +%% end, +%% [wxControlWithItems:findString(CListBox, X) || X <- CheckedFuncs, lists:member(X, FilteredData)]), +%% {noreply, State}; + +%% handle_event(#wx{id = ?MODULEPOPUP_DIALOG, +%% event = #wxClose{type = close_window}}, +%% #traceopts_state{module_popup_dialog = Dialog} = State) -> +%% wxDialog:destroy(Dialog), +%% {noreply, State#traceopts_state{popup_open = false, +%% checked_funcs = []}}; + +%% handle_event(#wx{event = What}, State) -> +%% io:format("~p~p: Unhandled event: ~p ~n", [?MODULE, self(), What]), +%% {noreply, State}. + + + +%% terminate(Reason, #traceopts_state{frame = Frame}) -> +%% io:format("~p terminating traceopts. Reason: ~p~n", [?MODULE, Reason]), +%% wxFrame:destroy(Frame), +%% ok. + +%% code_change(_, _, State) -> +%% {stop, not_yet_implemented, State}. + +%% handle_info(Any, State) -> +%% io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]), +%% {noreply, State}. + +%% handle_call(Msg, _From, State) -> +%% io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), +%% {reply, ok, State}. + +%% handle_cast(Msg, State) -> +%% io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), +%% {noreply, State}. diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl index f744623960..7b5cdb44b9 100644 --- a/lib/observer/src/observer_tv_table.erl +++ b/lib/observer/src/observer_tv_table.erl @@ -264,7 +264,7 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) -> Cols = wxListCtrl:getColumnCount(Grid), Last = lists:foldl(fun(I, Last) -> Last - wxListCtrl:getColumnWidth(Grid, I) - end, W-2, lists:seq(0, Cols - 2)), + end, W-?LCTRL_WDECR, lists:seq(0, Cols - 2)), Size = max(?DEFAULT_COL_WIDTH, Last), wxListCtrl:setColumnWidth(Grid, Cols-1, Size) end), diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 191faf73ff..ded03fadb1 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -131,7 +131,7 @@ handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) -> Cols = wxListCtrl:getColumnCount(Grid), Last = lists:foldl(fun(I, Last) -> Last - wxListCtrl:getColumnWidth(Grid, I) - end, W-2, lists:seq(0, Cols - 2)), + end, W-?LCTRL_WDECR, lists:seq(0, Cols - 2)), Size = max(200, Last), wxListCtrl:setColumnWidth(Grid, Cols-1, Size) end), diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 45725f03b5..d737faed32 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -20,7 +20,7 @@ -behaviour(wx_object). -export([start/0]). --export([create_menus/2, get_attrib/1, +-export([create_menus/2, get_attrib/1, get_tracer/0, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, @@ -40,6 +40,8 @@ -define(FIRST_NODES_MENU_ID, 1000). -define(LAST_NODES_MENU_ID, 2000). +-define(TRACE_STR, "Trace Overview"). + %% Records -record(state, {frame, @@ -51,6 +53,7 @@ pro_panel, tv_panel, sys_panel, + trace_panel, active_tab, node, nodes @@ -65,10 +68,13 @@ create_menus(Object, Menus) when is_list(Menus) -> get_attrib(What) -> wx_object:call(observer, {get_attrib, What}). +get_tracer() -> + wx_object:call(observer, get_tracer). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(_Args) -> + register(observer, self()), wx:new(), catch wxSystemOptions:setOption("mac.listctrl.always_use_generic", 1), Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Observer", @@ -80,10 +86,8 @@ init(_Args) -> State = #state{frame = Frame}, UpdState = setup(State), - wxFrame:show(Frame), net_kernel:monitor_nodes(true), process_flag(trap_exit, true), - register(observer, self()), {Frame, UpdState}. setup(#state{frame = Frame} = State) -> @@ -103,13 +107,24 @@ setup(#state{frame = Frame} = State) -> Panel = wxPanel:new(Frame, []), Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), - %% Setup sizer - MainSizer = wxBoxSizer:new(?wxVERTICAL), - %% System Panel SysPanel = observer_sys_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, SysPanel, "System", []), + %% Setup sizer create early to get it when window shows + MainSizer = wxBoxSizer:new(?wxVERTICAL), + + wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]), + wxPanel:setSizer(Panel, MainSizer), + + wxNotebook:connect(Notebook, command_notebook_page_changing), + wxFrame:connect(Frame, close_window, [{skip, true}]), + wxMenu:connect(Frame, command_menu_selected), + wxFrame:show(Frame), + + %% I postpone the creation of the other tabs so they can query/use + %% the window size + %% Process Panel ProPanel = observer_pro_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, ProPanel, "Processes", []), @@ -118,12 +133,9 @@ setup(#state{frame = Frame} = State) -> TVPanel = observer_tv_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []), - wxSizer:add(MainSizer, Notebook, [{proportion, 1}, {flag, ?wxEXPAND}]), - wxPanel:setSizer(Panel, MainSizer), - - wxNotebook:connect(Notebook, command_notebook_page_changing), - wxFrame:connect(Frame, close_window, [{skip, true}]), - wxMenu:connect(Frame, command_menu_selected), + %% Trace Viewer Panel + TracePanel = observer_trace_wx:start_link(Notebook, self()), + wxNotebook:addPage(Notebook, TracePanel, ?TRACE_STR, []), SysPid = wx_object:get_pid(SysPanel), SysPid ! {active, node()}, @@ -134,6 +146,7 @@ setup(#state{frame = Frame} = State) -> sys_panel = SysPanel, pro_panel = ProPanel, tv_panel = TVPanel, + trace_panel = TracePanel, active_tab = SysPid, node = node(), nodes = Nodes @@ -143,6 +156,7 @@ setup(#state{frame = Frame} = State) -> SysFontSize = wxFont:getPointSize(SysFont), Modern = wxFont:new(SysFontSize, ?wxFONTFAMILY_MODERN, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), put({font, modern}, Modern), + put({font, fixed}, Modern), UpdState. @@ -262,6 +276,9 @@ handle_call({create_menus, TabMenus}, _From, handle_call({get_attrib, Attrib}, _From, State) -> {reply, get(Attrib), State}; +handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) -> + {reply, TraceP, State}; + handle_call(_Msg, _From, State) -> {reply, ok, State}. @@ -360,11 +377,12 @@ check_page_title(Notebook) -> Selection = wxNotebook:getSelection(Notebook), wxNotebook:getPageText(Notebook, Selection). -get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, tv_panel=Tv}) -> +get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, tv_panel=Tv, trace_panel=Trace}) -> Panel = case check_page_title(Notebook) of "Processes" -> Pro; "System" -> Sys; - "Table Viewer" -> Tv + "Table Viewer" -> Tv; + ?TRACE_STR -> Trace end, wx_object:get_pid(Panel). -- cgit v1.2.3