aboutsummaryrefslogblamecommitdiffstats
path: root/lib/observer/src/observer_traceoptions_wx.erl
blob: 043126d85f9aee656032a41ced406284f5b0f513 (plain) (tree)
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196






















                                                                         

















































































                                                                                                 
       




                                                                           

        













































                                                                                          
 



                                                                                 
 



























                                                                                      
                                                           

                                                     



                                                                                          
                  







                                                                                
                                                                 




                                                                                           
                            


                                                                       
                                                       





                                                                                          
                                                         





                                                                                      


























                                                                                 
 

                                          
                                         


                                                                                                 
 

                                                                       
 





































































































































































                                                                                                 
 
                                            


                                                           
                                                                               
                             
                                                                       




                                                                   















                                                                                           


                             
                                
                                                       





































                                                                            
                          

                                          
                                                
               
                                              










                                                              
                                

                                                                          



                                      
                                           









                                                           
                                                    
 

                                                      
                                 

                                                 

                           




























































































































































































                                                                                                                                










                                     
 

























































































































































































































































































































































































































































                                                                                                                      
%%
%% %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([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
	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.

function_selector(Parent, Node, Module) ->
    Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Functions",
			  [{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 = 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, false),
    %% Setup Event handling
    wxTextCtrl:connect(TxtCtrl, command_text_updated,
    		       [{callback, 
			 fun(#wx{event=#wxCommand{cmdString=Input}}, _) ->
				 filter_listbox_data(Input, ParsedChoices, ListBox, false)
			 end}]),
    Self = self(),

    %% Sigh clientdata in checklistbox crashes on windows, wx-bug I presume.
    %% Don't have time to investigate now, workaround file bug report later
    GetClientData = fun(LB, N) ->
			    String = wxListBox:getString(LB, N),
			    {_, Data} = lists:keyfind(String, 1, ParsedChoices),
			    Data
		    end,
    wxCheckListBox:connect(ListBox, command_checklistbox_toggled,
    			   [{callback, 
			     fun(#wx{event=#wxCommand{commandInt=N}}, _) ->
				     Self ! {ListBox, wxCheckListBox:isChecked(ListBox, N),
					     GetClientData(ListBox, N)}
			     end}]),
    Check = fun(Id, Bool) ->
    		    wxCheckListBox:check(ListBox, Id, [{check, Bool}]),
    		    Self ! {ListBox, Bool, 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}}]),

    Panel = wxPanel:new(Dialog),
    PanelSz = wxBoxSizer:new(?wxVERTICAL),
    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:"}]),

    TextCtrl = create_styled_txtctrl(Panel),
    wxSizer:add(TxtSz, TextCtrl, [{flag, ?wxEXPAND}, {proportion, 1}]),

    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) ->
    filter_listbox_data(Input, Data, ListBox, true).

filter_listbox_data(Input, Data, ListBox, AddClientData) ->
    FilteredData = [X || X = {Str, _} <- Data, re:run(Str, Input) =/= nomatch],
    wxListBox:clear(ListBox),
    wxListBox:appendStrings(ListBox, [Str || {Str,_} <- FilteredData]),
    AddClientData andalso 
	wx:foldl(fun({_, Term}, N) ->
			 wxListBox:setClientData(ListBox, N, Term),
			 N+1
		 end, 0, 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),
    {ChkBox, Radio1, Radio2}.


create_styled_txtctrl(Parent) ->
    FixedFont = observer_wx:get_attrib({font, modern}),
    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, Radio) ->
    case wxCheckBox:isChecked(CheckBox) of
	false ->
	    [wxWindow:disable(R) || R <- Radio];
	true ->
	    [wxWindow:enable(R) || R <- Radio]
    end.


check_box(ChkBox, Bool) ->
    case Bool of
	true ->
	    wxCheckBox:set3StateValue(ChkBox, ?wxCHK_CHECKED);
	false ->
	    ignore
    end.

parse_function_names(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, 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*?-"),
    Parsed =
	if IsFun =/= nomatch -> "Fun: " ++ H;
	   IsLc =/= nomatch -> "List comprehension: " ++ H;
	   IsLbc =/= nomatch -> "Bit comprehension: " ++ H;
	   true ->
		H
	end,
    parse_function_names(T, [{Parsed, Term} | Acc]).

ms_names(MatchSpecList) ->
    MsOrAlias = fun(#match_spec{name = A, str = M}) ->
			case A of
			    "" -> M;
			    _ -> A ++ "    " ++ M
			end
		end,
    [{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([]) ->
    ".";
ensure_last_is_dot(String) ->
    case lists:last(String) =:= $. of
	true ->
	    String;
	false ->
	    String ++ "."
    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}.