aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2011-11-11 15:00:11 +0100
committerDan Gudmundsson <[email protected]>2011-11-17 14:51:28 +0100
commitb65b0c4bf4896021cbd327a4bc1acd426645a5f3 (patch)
tree5a5449038c82f1a57d688fb8e6cfa51aebb91b66
parent3caa688c5522b8f0f039b9375cf6377b1bd88f0e (diff)
downloadotp-b65b0c4bf4896021cbd327a4bc1acd426645a5f3.tar.gz
otp-b65b0c4bf4896021cbd327a4bc1acd426645a5f3.tar.bz2
otp-b65b0c4bf4896021cbd327a4bc1acd426645a5f3.zip
[observer] Rework tracing part of the gui
No tracing is implemented yet.
-rw-r--r--lib/observer/src/observer_defs.hrl13
-rw-r--r--lib/observer/src/observer_lib.erl4
-rw-r--r--lib/observer/src/observer_pro_wx.erl110
-rw-r--r--lib/observer/src/observer_trace_wx.erl668
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl1971
-rw-r--r--lib/observer/src/observer_tv_table.erl2
-rw-r--r--lib/observer/src/observer_tv_wx.erl2
-rw-r--r--lib/observer/src/observer_wx.erl46
8 files changed, 1506 insertions, 1310 deletions
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).