aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_traceoptions_wx.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/src/observer_traceoptions_wx.erl')
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl1066
1 files changed, 1066 insertions, 0 deletions
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
new file mode 100644
index 0000000000..7244efdc50
--- /dev/null
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -0,0 +1,1066 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(observer_traceoptions_wx).
+
+-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]) ->
+ 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{}}
+ end.
+
+
+setup(ParentFrame, Node, TraceOpts, TracedFuncs, MatchSpecs) ->
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Setup main window
+
+ Frame = wxFrame:new(ParentFrame, ?TRACEOPTS_FRAME, "Trace options",
+ [{style, ?wxRESIZE_BORDER bor ?wxCLOSE_BOX},
+ {size, {400, 500}}]),
+ Panel = wxPanel:new(Frame, []),
+ MainSz = wxBoxSizer:new(?wxVERTICAL),
+ Notebook = wxNotebook:new(Panel, ?wxID_ANY),
+ Modules = get_modules(Node),
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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),
+
+ {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}.
+
+
+filter_listbox_data(Input, Data, ListBox) ->
+ FilteredData = [X || X <- Data, re:run(X, Input) =/= nomatch],
+ wxListBox:clear(ListBox),
+ wxListBox:appendStrings(ListBox, FilteredData),
+ FilteredData.
+
+get_modules(Node) ->
+ lists:sort([Module || {Module, _} <- observer_wx:try_rpc(Node, code, all_loaded, [])]).
+
+optionpage_top_right(Panel, TopRightSz, Options, Text) ->
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ ChkBox = wxCheckBox:new(Panel, ?wxID_ANY, "Inherit on " ++ Text, []),
+ RadioSz = wxBoxSizer:new(?wxVERTICAL),
+ Radio1 = wxRadioButton:new(Panel, ?wxID_ANY, "All " ++ Text, [{style, ?wxRB_GROUP}]),
+ Radio2 = wxRadioButton:new(Panel, ?wxID_ANY, "First " ++ Text ++ " only", []),
+ wxSizer:add(Sizer, ChkBox, []),
+ wxSizer:add(RadioSz, Radio1, []),
+ 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,[]),
+ Ed = wxStyledTextCtrl:new(Parent),
+ wxStyledTextCtrl:styleClearAll(Ed),
+ wxStyledTextCtrl:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont),
+ wxStyledTextCtrl:setLexer(Ed, ?wxSTC_LEX_ERLANG),
+ wxStyledTextCtrl:setMarginType(Ed, 1, ?wxSTC_MARGIN_NUMBER),
+ wxStyledTextCtrl:setSelectionMode(Ed, ?wxSTC_SEL_LINES),
+ wxStyledTextCtrl:setUseHorizontalScrollBar(Ed, false),
+
+ Styles = [{?wxSTC_ERLANG_DEFAULT, {0,0,0}},
+ {?wxSTC_ERLANG_COMMENT, {160,53,35}},
+ {?wxSTC_ERLANG_VARIABLE, {150,100,40}},
+ {?wxSTC_ERLANG_NUMBER, {5,5,100}},
+ {?wxSTC_ERLANG_KEYWORD, {130,40,172}},
+ {?wxSTC_ERLANG_STRING, {170,45,132}},
+ {?wxSTC_ERLANG_OPERATOR, {30,0,0}},
+ {?wxSTC_ERLANG_ATOM, {0,0,0}},
+ {?wxSTC_ERLANG_FUNCTION_NAME, {64,102,244}},
+ {?wxSTC_ERLANG_CHARACTER,{236,155,172}},
+ {?wxSTC_ERLANG_MACRO, {40,144,170}},
+ {?wxSTC_ERLANG_RECORD, {40,100,20}},
+ {?wxSTC_ERLANG_SEPARATOR,{0,0,0}},
+ {?wxSTC_ERLANG_NODE_NAME,{0,0,0}}],
+ SetStyle = fun({Style, Color}) ->
+ wxStyledTextCtrl:styleSetFont(Ed, Style, FixedFont),
+ wxStyledTextCtrl:styleSetForeground(Ed, Style, Color)
+ end,
+ [SetStyle(Style) || Style <- Styles],
+ wxStyledTextCtrl:setKeyWords(Ed, 0, keyWords()),
+ Ed.
+
+
+keyWords() ->
+ L = ["after","begin","case","try","cond","catch","andalso","orelse",
+ "end","fun","if","let","of","query","receive","when","bnot","not",
+ "div","rem","band","and","bor","bxor","bsl","bsr","or","xor"],
+ lists:flatten([K ++ " " || K <- L] ++ [0]).
+
+
+enable({CheckBox, AllRadio, FirstRadio}) ->
+ case wxCheckBox:isChecked(CheckBox) of
+ false ->
+ wxWindow:disable(AllRadio),
+ wxWindow:disable(FirstRadio);
+ true ->
+ wxWindow:enable(AllRadio),
+ wxWindow:enable(FirstRadio)
+ end.
+
+
+check_box(ChkBox, Bool) ->
+ case Bool of
+ true ->
+ wxCheckBox:set3StateValue(ChkBox, ?wxCHK_CHECKED);
+ false ->
+ 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],
+ parse_function_names(StrList, []).
+
+parse_function_names([], Acc) ->
+ lists:reverse(Acc);
+parse_function_names([H|T], Acc) ->
+ IsFun = re:run(H, ".*-fun-\\d*?-"),
+ IsLc = re:run(H, ".*-lc\\$\\^\\d*?/\\d*?-\\d*?-"),
+ IsLbc = re:run(H, ".*-lbc\\$\\^\\d*?/\\d*?-\\d*?-"),
+ Parsed =
+ if IsFun =/= nomatch -> "Fun: " ++ H;
+ IsLc =/= nomatch -> "List comprehension: " ++ H;
+ IsLbc =/= nomatch -> "Bit comprehension: " ++ H;
+ true ->
+ H
+ end,
+ parse_function_names(T, [Parsed | Acc]).
+
+show_ms_in_savedlistbox(MatchSpecList) ->
+ MsOrAlias = fun(#match_spec{alias = A, str_ms = M, fun2ms = F}) ->
+ case A of
+ undefined ->
+ if
+ F =:= undefined -> M;
+ true -> F
+ end;
+ _ ->
+ A
+ 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].
+
+ensure_last_is_dot([]) ->
+ ".";
+ensure_last_is_dot(String) ->
+ case lists:last(String) =:= $. of
+ true ->
+ String;
+ false ->
+ 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}.