%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions 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, select_nodes/2,
output/2, select_matchspec/4]).
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(Radio, lists:member(Opt, Default)) ||
{Radio, Opt} <- [{SpwnAllRadio, on_spawn}, {SpwnFirstRadio, on_first_spawn},
{LinkAllRadio, on_link}, {LinkFirstRadio, on_first_link}]],
[wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,SendBox,RecBox,EventBox]],
wxSizer:add(LeftSz, 150, -1),
wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}, {proportion,1}]),
wxSizer:add(PanelSz, RightSz,[{flag, ?wxEXPAND}, {proportion,1}]),
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}]),
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)],
wxDialog:destroy(Dialog),
lists:reverse(Opts);
?wxID_CANCEL ->
wxDialog:destroy(Dialog),
throw(cancel)
end.
trace_pattern(ParentPid, Parent, Node, MatchSpecs) ->
try
{Module,MFAs,MatchSpec} =
case module_selector(Parent, Node) of
{'$trace_event',Event} ->
MS = select_matchspec(ParentPid, Parent, MatchSpecs, Event),
{'Events',[{'Events',Event}],MS};
Mod ->
MFAs0 = function_selector(Parent, Node, Mod),
MS = select_matchspec(ParentPid, Parent, MatchSpecs, funcs),
{Mod,MFAs0,MS}
end,
{Module, [#tpattern{m=M,fa=FA,ms=MatchSpec} || {M,FA} <- MFAs]}
catch cancel -> cancel
end.
select_nodes(Parent, Nodes) ->
Choices = [{atom_to_list(X), X} || X <- Nodes],
check_selector(Parent, Choices).
module_selector(Parent, Node) ->
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event",
[{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),
Events = [{"Messages sent",{'$trace_event',send}},
{"Messages received",{'$trace_event','receive'}}],
AllModules = Events ++ [{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(#wx{event=#wxCommand{commandInt=Id}}, _) ->
Id >= 0 andalso wxWindow:enable(OkButt)
end}]),
case wxDialog:showModal(Dialog) of
?wxID_OK ->
SelId = wxListBox:getSelection(ListBox),
case SelId >= 0 of
true ->
Module = wxListBox:getClientData(ListBox, SelId),
wxDialog:destroy(Dialog),
Module;
false ->
wxDialog:destroy(Dialog),
throw(cancel)
end;
?wxID_CANCEL ->
wxDialog:destroy(Dialog),
throw(cancel)
end.
function_selector(Parent, Node, Module) ->
Functions = observer_wx:try_rpc(Node, Module, module_info, [functions]),
Externals = observer_wx:try_rpc(Node, Module, module_info, [exports]),
Choices = lists:usort([{Name, Arity} || {Name, Arity} <- Externals ++ Functions,
not(erl_internal:guard_bif(Name, Arity))]),
ParsedChoices = parse_function_names(Choices),
case check_selector(Parent, ParsedChoices) of
[] -> [{Module, {'_', '_'}}];
FAs ->
[{Module, {F, A}} || {F,A} <- FAs]
end.
check_selector(Parent, ParsedChoices) ->
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
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),
get_checked(ListBox, []);
?wxID_CANCEL ->
wxDialog:destroy(Dialog),
get_checked(ListBox, []),
throw(cancel)
end.
get_checked(ListBox, Acc) ->
receive
{ListBox, true, FA} ->
get_checked(ListBox, [FA|lists:delete(FA,Acc)]);
{ListBox, false, FA} ->
get_checked(ListBox, lists:delete(FA,Acc))
after 0 ->
lists:reverse(Acc)
end.
select_matchspec(Pid, Parent, AllMatchSpecs, Key) ->
{MatchSpecs,RestMS} =
case lists:keytake(Key,1,AllMatchSpecs) of
{value,{Key,MSs0},Rest} -> {MSs0,Rest};
false -> {[],AllMatchSpecs}
end,
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, Dialog) of
Ms = #match_spec{} ->
add_and_select(-1, Ms, ListBox),
wxWindow:enable(OkButt),
wxWindow:enable(EditMsBtn),
wxWindow:enable(DelMsBtn);
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, Dialog) of
Ms = #match_spec{} ->
add_and_select(SelId, Ms, ListBox),
wxWindow:enable(OkButt),
wxWindow:enable(EditMsBtn),
wxWindow:enable(DelMsBtn);
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, [{Key,MSs}|RestMS]},
MS = lists:nth(SelId+1, MSs),
wxDialog:destroy(Dialog),
MS;
?wxID_CANCEL ->
wxDialog:destroy(Dialog),
throw(cancel)
end.
output(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 = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Output"}]),
%% Output select
WinB = wxCheckBox:new(Panel, ?wxID_ANY, "Window", []),
check_box(WinB, proplists:get_value(window, Default, true)),
ShellB = wxCheckBox:new(Panel, ?wxID_ANY, "Shell", []),
check_box(ShellB, proplists:get_value(shell, Default, false)),
[wxSizer:add(PanelSz, CheckBox, []) || CheckBox <- [WinB, ShellB]],
GetFileOpts = ttb_file_options(Panel, PanelSz, Default),
%% Set sizer and show dialog
wxPanel:setSizer(Panel, PanelSz),
wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND bor ?wxALL}, {proportion,1}, {border, 3}]),
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),
case wxDialog:showModal(Dialog) of
?wxID_OK ->
Res = [{window, wxCheckBox:getValue(WinB)},
{shell, wxCheckBox:getValue(ShellB)} | GetFileOpts()],
wxDialog:destroy(Dialog),
Res;
?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, fixed}),
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].
ensure_last_is_dot([]) ->
".";
ensure_last_is_dot(String) ->
case lists:last(String) =:= $. of
true ->
String;
false ->
String ++ "."
end.
ttb_file_options(Panel, Sizer, Default) ->
Top = wxBoxSizer:new(?wxVERTICAL),
NameS = wxBoxSizer:new(?wxHORIZONTAL),
FileBox = wxCheckBox:new(Panel, ?wxID_ANY, "File (Using ttb file tracer)", []),
check_box(FileBox, proplists:get_value(file, Default, false)),
wxSizer:add(Sizer, FileBox),
Desc = wxStaticText:new(Panel, ?wxID_ANY, "File"),
FileName = proplists:get_value(filename, Default, "ttb"),
FileT = wxTextCtrl:new(Panel, ?wxID_ANY, [{size, {150,-1}}, {value, FileName}]),
FileB = wxButton:new(Panel, ?wxID_ANY, [{label, "Browse"}]),
wxSizer:add(NameS, Desc, [{proportion, 0}, {flag, ?wxALIGN_CENTER_VERTICAL}]),
wxSizer:add(NameS, FileT, [{proportion, 1}, {flag, ?wxEXPAND bor ?wxALIGN_CENTER_VERTICAL}]),
wxSizer:add(NameS, FileB, [{proportion, 0}, {flag, ?wxALIGN_CENTER_VERTICAL}]),
WrapB = wxCheckBox:new(Panel, ?wxID_ANY, "Wrap logs"),
WrapSz = wxSlider:new(Panel, ?wxID_ANY, proplists:get_value(wrap_sz, Default, 128),
64, 10*1024, [{style, ?wxSL_HORIZONTAL bor ?wxSL_LABELS}]),
WrapC = wxSlider:new(Panel, ?wxID_ANY, proplists:get_value(wrap_c, Default, 8),
2, 100, [{style, ?wxSL_HORIZONTAL bor ?wxSL_LABELS}]),
wxSizer:add(Top, NameS, [{flag, ?wxEXPAND}]),
wxSizer:add(Top, WrapB, []),
wxSizer:add(Top, WrapSz,[{flag, ?wxEXPAND}]),
wxSizer:add(Top, WrapC, [{flag, ?wxEXPAND}]),
wxSizer:add(Sizer, Top, [{flag, ?wxEXPAND bor ?wxLEFT},{border, 20}]),
Enable = fun(UseFile, UseWrap0) ->
UseWrap = UseFile andalso UseWrap0,
[wxWindow:enable(W, [{enable, UseFile}]) || W <- [Desc,FileT,FileB,WrapB]],
[wxWindow:enable(W, [{enable, UseWrap}]) || W <- [WrapSz, WrapC]],
check_box(WrapB, UseWrap0)
end,
Enable(proplists:get_value(file, Default, false), proplists:get_value(wrap, Default, false)),
wxPanel:connect(FileBox, command_checkbox_clicked,
[{callback, fun(_,_) ->
Enable(wxCheckBox:getValue(FileBox),
wxCheckBox:getValue(WrapB))
end}]),
wxPanel:connect(WrapB, command_checkbox_clicked,
[{callback, fun(_,_) ->
Enable(true, wxCheckBox:getValue(WrapB))
end}]),
wxPanel:connect(FileB, command_button_clicked,
[{callback, fun(_,_) -> get_file(FileT) end}]),
fun() ->
[{file, wxCheckBox:getValue(FileBox)},
{filename, wxTextCtrl:getValue(FileT)},
{wrap, wxCheckBox:getValue(WrapB)},
{wrap_sz, wxSlider:getValue(WrapSz)},
{wrap_c, wxSlider:getValue(WrapC)}]
end.
get_file(Text) ->
Str = wxTextCtrl:getValue(Text),
Dialog = wxFileDialog:new(Text,
[{message, "Select a file"},
{defaultFile, Str}]),
case wxDialog:showModal(Dialog) of
?wxID_OK ->
Dir = wxFileDialog:getDirectory(Dialog),
File = wxFileDialog:getFilename(Dialog),
wxTextCtrl:setValue(Text, filename:join(Dir, File));
_ -> ok
end,
wxFileDialog:destroy(Dialog).