aboutsummaryrefslogtreecommitdiffstats
path: root/lib/reltool/src/reltool_mod_win.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/reltool/src/reltool_mod_win.erl')
-rw-r--r--lib/reltool/src/reltool_mod_win.erl773
1 files changed, 773 insertions, 0 deletions
diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl
new file mode 100644
index 0000000000..c05f73cde8
--- /dev/null
+++ b/lib/reltool/src/reltool_mod_win.erl
@@ -0,0 +1,773 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. 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(reltool_mod_win).
+
+%% Public
+-export([start_link/5, raise/1, refresh/1]).
+
+%% Internal
+-export([init/6, loop/1]).
+
+%% sys callback functions
+-export([
+ system_continue/3,
+ system_terminate/4,
+ system_code_change/4
+ ]).
+
+-include_lib("wx/include/wx.hrl").
+-include("reltool.hrl").
+
+-record(state,
+ {parent_pid,
+ xref_pid,
+ rel_pid,
+ mod_wins,
+ sys,
+ common,
+ mod,
+ name,
+ frame,
+ panel,
+ book,
+ status_bar,
+ deps_used_by_ctrl,
+ deps_uses_ctrl,
+ popup_menu,
+ active_page,
+ code_pages}).
+
+-record(code_page,
+ {name,
+ editor,
+ find_objs,
+ find_data}).
+
+-record(find_objs,
+ {search, % Search input ctrl
+ goto, % Goto input ctrl
+ radio}). % Radio buttons
+
+-record(find_data,
+ {start, % start pos
+ found, % status
+ history}). % list of recent positions
+
+-define(WIN_WIDTH, 800).
+-define(WIN_HEIGHT, 600).
+
+-define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available
+-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific
+-define(CONTENTS_ITEM, 300).
+-define(SEARCH_ENTRY, 413).
+-define(GOTO_ENTRY, 414).
+
+-define(MODS_MOD_COL, 0).
+-define(MODS_APP_COL, 1).
+
+-define(INITIAL_CODE_PAGE_NAME, "Code").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Client
+
+start_link(WxEnv, Xref, RelPid, Common, ModName) ->
+ proc_lib:start_link(?MODULE, init, [self(), WxEnv, Xref, RelPid, Common, ModName], infinity, []).
+
+raise(Pid) ->
+ reltool_utils:cast(Pid, raise).
+
+refresh(Pid) ->
+ reltool_utils:cast(Pid, refresh).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Server
+
+init(Parent, WxEnv, Xref, RelPid, C, ModName) ->
+ try
+ do_init(Parent, WxEnv, Xref, RelPid, C, ModName)
+ catch
+ error:Reason ->
+ exit({Reason, erlang:get_stacktrace()})
+ end.
+
+do_init(Parent, WxEnv, Xref, RelPid, C, ModName) ->
+ process_flag(trap_exit, C#common.trap_exit),
+ {ok, Mod} = reltool_server:get_mod(Xref, ModName),
+ {ok, Sys} = reltool_server:get_sys(Xref),
+ S = #state{parent_pid = Parent,
+ xref_pid = Xref,
+ rel_pid = RelPid,
+ sys = Sys,
+ mod = Mod,
+ name = atom_to_list(Mod#mod.name),
+ common = C},
+ proc_lib:init_ack(Parent, {ok, self()}),
+ wx:set_env(WxEnv),
+ wx:debug(C#common.wx_debug),
+ S2 = wx:batch(fun() -> create_window(S) end),
+ loop(S2).
+
+loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) ->
+ receive
+ Msg ->
+ %% io:format("~s~p -> ~p\n", [S#state.name, self(), Msg]),
+ case Msg of
+ {system, From, SysMsg} ->
+ Dbg = C#common.sys_debug,
+ sys:handle_system_msg(SysMsg, From, S#state.parent_pid, ?MODULE, Dbg, S);
+ {cast, _From, raise} ->
+ wxFrame:raise(S#state.frame),
+ wxFrame:setFocus(S#state.frame),
+ ?MODULE:loop(S);
+ {cast, _From, refresh} ->
+ %% wx_misc:beginBusyCursor(),
+ case reltool_server:get_mod(Xref, Mod#mod.name) of
+ {ok, Mod2} ->
+ {ok, Sys} = reltool_server:get_sys(Xref),
+ S2 = redraw_window(S#state{sys = Sys, mod = Mod2}),
+ %% wx_misc:endBusyCursor(),
+ ?MODULE:loop(S2);
+ {error, _} ->
+ wxFrame:destroy(S#state.frame),
+ exit(shutdown)
+ end;
+ {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid ->
+ exit(Reason);
+ #wx{event = #wxSize{}} = Wx ->
+ Wx2 = reltool_utils:get_latest_resize(Wx),
+ S2 = handle_event(S, Wx2),
+ ?MODULE:loop(S2);
+ #wx{obj = ObjRef,
+ event = #wxClose{type = close_window}} ->
+ wxFrame:destroy(ObjRef),
+ exit(shutdown);
+ #wx{} = Wx ->
+ S2 = handle_event(S, Wx),
+ ?MODULE:loop(S2);
+ _ ->
+ error_logger:format("~p~p got unexpected message:\n\t~p\n",
+ [?MODULE, self(), Msg]),
+ ?MODULE:loop(S)
+ end
+ end.
+
+create_window(#state{mod = Mod, name = ModStr} = S) ->
+ Title = atom_to_list(?APPLICATION) ++ " - " ++
+ atom_to_list(Mod#mod.app_name) ++ " - " ++
+ ModStr ++ ".erl",
+ Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, []),
+ %% wxFrame:setSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}),
+ Panel = wxPanel:new(Frame, []),
+ StatusBar = wxFrame:createStatusBar(Frame,[]),
+
+ Book = wxNotebook:new(Panel, ?wxID_ANY, []),
+
+ S2 = S#state{frame = Frame,
+ panel = Panel,
+ book = Book,
+ status_bar = StatusBar,
+ code_pages = []},
+ S3 = create_deps_page(S2),
+ S4 = create_code_page(S3, ?INITIAL_CODE_PAGE_NAME),
+ S5 = create_config_page(S4),
+ wxNotebook:setSelection(Book, 0),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]),
+
+ wxPanel:setSizer(Panel, Sizer),
+ wxSizer:fit(Sizer, Frame),
+ wxSizer:setSizeHints(Sizer, Frame),
+
+ wxEvtHandler:connect(Book, command_notebook_page_changed, [{skip, true}]),
+ wxFrame:connect(Frame, close_window),
+ wxFrame:show(Frame),
+
+ S5.
+
+create_deps_page(S) ->
+ Panel = wxPanel:new(S#state.book, []),
+ Main = wxBoxSizer:new(?wxHORIZONTAL),
+
+ UsedByCtrl = create_mods_list_ctrl(Panel, Main, "Modules used by others", " and their applications"),
+ wxSizer:add(Main,
+ wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]),
+ [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]),
+ UsesCtrl = create_mods_list_ctrl(Panel, Main, "Used modules", " and their applications"),
+ S2 = S#state{deps_used_by_ctrl = UsedByCtrl,
+ deps_uses_ctrl = UsesCtrl},
+ redraw_mods(S2),
+ wxPanel:setSizer(Panel, Main),
+ wxNotebook:addPage(S2#state.book, Panel, "Dependencies", []),
+ S2.
+
+create_mods_list_ctrl(Panel, Sizer, ModText, AppText) ->
+ Width = lists:max([100, ?WIN_WIDTH - 40]) div 2,
+ Height = lists:max([100, ?WIN_HEIGHT - 100]),
+ ListCtrl = wxListCtrl:new(Panel,
+ [{style,
+ ?wxLC_REPORT bor
+ %% ?wxLC_SORT_ASCENDING bor
+ ?wxLC_SINGLE_SEL bor
+ ?wxHSCROLL bor
+ ?wxVSCROLL},
+ {size, {Width, Height}}]),
+ %% Prep images
+ reltool_utils:assign_image_list(ListCtrl),
+
+ %% Prep column label
+ ListItem = wxListItem:new(),
+ wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT),
+ wxListItem:setText(ListItem, ModText),
+ wxListCtrl:insertColumn(ListCtrl, ?MODS_MOD_COL, ListItem),
+ %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, ?MODS_MOD_COL_WIDTH),
+
+ wxListItem:setText(ListItem, AppText),
+ wxListCtrl:insertColumn(ListCtrl, ?MODS_APP_COL, ListItem),
+ %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, ?MODS_APP_COL_WIDTH),
+ wxListItem:destroy(ListItem),
+
+ wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, mods_list_ctrl}]),
+ wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_app}]),
+ wxWindow:connect(ListCtrl, enter_window),
+
+ wxSizer:add(Sizer, ListCtrl,
+ [{border, 2},
+ {flag, ?wxALL bor ?wxEXPAND},
+ {proportion, 1}]),
+ ListCtrl.
+
+create_code_page(#state{book = Book, code_pages = Pages, name = ModStr} = S, PageName) ->
+ case find_page(S, PageName) of
+ not_found ->
+ Page = do_create_code_page(S, PageName),
+ Pages2 = Pages ++ [Page],
+ Pos = length(Pages2),
+ wxNotebook:setSelection(Book, Pos),
+ case find_page(S, ?INITIAL_CODE_PAGE_NAME) of
+ not_found ->
+ ignore;
+ {found, _, CodePos} ->
+ %% Rename initial code page
+ wxNotebook:setPageText(Book, CodePos, ModStr)
+ end,
+ S#state{active_page = Page, code_pages = Pages2};
+ {found, Page, Pos} ->
+ wxNotebook:setSelection(Book, Pos),
+ S#state{active_page = Page}
+ end.
+
+find_page(S, PageName) ->
+ find_page(S#state.code_pages, PageName, 1).
+
+find_page([Page | Pages], PageName, Pos) ->
+ case Page#code_page.name =:= PageName of
+ true ->
+ {found, Page, Pos};
+ false ->
+ find_page(Pages, PageName, Pos + 1)
+ end;
+find_page([], _PageName, _Pos) ->
+ not_found.
+
+do_create_code_page(#state{xref_pid = Xref, mod = M} = S, PageName) ->
+ Panel = wxPanel:new(S#state.book, []),
+ Editor = create_editor(Panel),
+ ToolTip = "Double click on a function call to search the function definition.",
+ wxBitmapButton:setToolTip(Editor, ToolTip),
+ {Objs, Data, SearchSz} = create_search_area(Panel),
+
+ {ok, App} = reltool_server:get_app(Xref, M#mod.app_name),
+ ErlBin =
+ case App#app.is_escript of
+ true -> find_escript_bin(App, M);
+ false -> find_regular_bin(App, M)
+ end,
+
+ load_code(Editor, ErlBin),
+
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxSizer:add(Sizer, SearchSz, [{flag, ?wxEXPAND}]),
+ wxPanel:setSizer(Panel, Sizer),
+ wxNotebook:addPage(S#state.book, Panel, PageName, []),
+ #code_page{name = PageName, editor = Editor, find_objs = Objs, find_data = Data}.
+
+find_regular_bin(App, Mod) ->
+ ActiveDir = App#app.active_dir,
+ SrcDir = filename:join([ActiveDir, "src"]),
+ ModStr = atom_to_list(Mod#mod.name),
+ Base = ModStr ++ ".erl",
+ Find = fun(F, _Acc) -> file:read_file(F) end,
+ case filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of
+ {ok, Bin} ->
+ Bin;
+ {error, enoent} ->
+ %% Reconstructing the source code from debug info if possible
+ BeamFile = filename:join([ActiveDir, "ebin", ModStr ++ ".beam"]),
+ case beam_lib:chunks(BeamFile, [abstract_code]) of
+ {ok,{_,[{abstract_code,{_,AC}}]}} ->
+ list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC)));
+ _ ->
+ list_to_binary(["%% Bad luck, cannot find any debug info in the file \"", BeamFile])
+ end
+ end.
+
+find_escript_bin(#app{active_dir = ActiveDir}, Mod) ->
+ NotFound = false,
+ ModName = Mod#mod.name,
+ {Fun, Escript} =
+ case filelib:is_regular(ActiveDir) of
+ true ->
+ %% File is on top level in the escript
+ {fun(FullName, _GetInfo, GetBin, Acc) ->
+ case filename:split(FullName) of
+ [_] ->
+ Bin = GetBin(),
+ case beam_lib:version(Bin) of
+ {ok,{M, _}} when M =:= ModName; FullName =:= "." ->
+ case beam_lib:chunks(Bin, [abstract_code]) of
+ {ok,{_,[{abstract_code,{_,AC}}]}} ->
+ {obj, list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC)))};
+ _ ->
+ Acc
+ end;
+ _ ->
+ Acc
+ end;
+ _ ->
+ Acc
+ end
+ end,
+ ActiveDir};
+ false ->
+ %% File is in an archive
+ Ext = code:objfile_extension(),
+ SrcFile = lists:concat([ModName, ".erl"]),
+ ObjFile = lists:concat([ModName, Ext]),
+ {fun(FullName, _GetInfo, GetBin, Acc) ->
+ io:format("", []),
+ case filename:split(FullName) of
+ [_AppName, "ebin", F] when F =:= ObjFile, Acc =:= NotFound ->
+ case beam_lib:chunks(GetBin(), [abstract_code]) of
+ {ok,{_,[{abstract_code,{_,AC}}]}} ->
+ {obj, list_to_binary(erl_prettypr:format(erl_syntax:form_list(AC)))};
+ _ ->
+ Acc
+ end;
+ [_AppName, "src", F] when F =:= SrcFile ->
+ {text, GetBin()};
+ _ ->
+ Acc
+ end
+ end,
+ filename:dirname(ActiveDir)}
+ end,
+ try
+ case escript:foldl(Fun, NotFound, Escript) of
+ {ok, {text, Bin}} ->
+ Bin;
+ {ok, {obj, Bin}} ->
+ Bin;
+ _ ->
+ list_to_binary(["%% Bad luck, cannot find the code in the escript ", Escript, "."])
+ end
+ catch
+ throw:Reason when is_list(Reason) ->
+ list_to_binary(["%% Bad luck, cannot find the code in the escript ", Escript, ": ", Reason])
+ end.
+
+create_config_page(S) ->
+ S.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+handle_event(#state{xref_pid = Xref} = S, Wx) ->
+ %% io:format("wx: ~p\n", [Wx]),
+ case Wx of
+ #wx{obj= ListCtrl, userData = mods_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} ->
+ wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, (2 * W) div 3),
+ wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, W div 3),
+ S;
+ #wx{userData = open_app,
+ obj = ListCtrl,
+ event = #wxList{type = command_list_item_activated, itemIndex = Pos}} ->
+ ModStr = wxListCtrl:getItemText(ListCtrl, Pos),
+ ModName = list_to_atom(ModStr),
+ {ok, Mod} = reltool_server:get_mod(Xref, ModName),
+ S2 = create_code_page(S#state{mod = Mod}, ModStr),
+ find_regexp_forward(S2, S2#state.name ++ ":");
+ %% ok = reltool_sys_win:open_app(S#state.rel_pid, Mod#mod.app_name),
+ %% S;
+ #wx{obj = Editor,
+ event = #wxStyledText{type = stc_doubleclick}} ->
+ goto_function(S, Editor);
+ #wx{id = ?SEARCH_ENTRY,
+ event = #wxCommand{type = command_text_enter, cmdString = Str}} ->
+ find_string(S, Str);
+ #wx{id = ?GOTO_ENTRY,
+ event = #wxCommand{type = command_text_enter, cmdString = Str}} ->
+ goto_line(S, Str);
+ #wx{event = #wxNotebook{type = command_notebook_page_changed}} ->
+ case wxNotebook:getSelection(S#state.book) of
+ 0 -> % Deps page
+ S;
+ N -> % Code page
+ Page = lists:nth(N, S#state.code_pages),
+ S#state{active_page = Page}
+ end;
+ #wx{event = #wxCommand{type = command_button_clicked}, userData = history_back} ->
+ goto_back(S);
+ #wx{obj = ObjRef, event = #wxMouse{type = enter_window}} ->
+ wxWindow:setFocus(ObjRef),
+ S;
+ _ ->
+ error_logger:format("~p~p got unexpected mod event from wx:\n\t~p\n",
+ [?MODULE, self(), Wx]),
+ S
+ end.
+
+redraw_mods(#state{xref_pid = Xref,
+ deps_used_by_ctrl = UsedByCtrl,
+ deps_uses_ctrl = UsesCtrl,
+ mod = #mod{is_pre_included = IsPre,
+ is_included = IsIncl,
+ uses_mods = UsesModNames,
+ used_by_mods = UsedByModNames},
+ status_bar = Bar}) ->
+ InclStatus =
+ case IsIncl of
+ true when IsPre =:= true -> "Whitelist - ";
+ true -> "Derived - ";
+ false -> "Blacklist - ";
+ undefined -> "Source - "
+ end,
+ Status = lists:concat([InclStatus,
+ " uses ", length(UsesModNames), " modules and ",
+ " is used by ", length(UsedByModNames), " modules."]),
+ wxStatusBar:setStatusText(Bar, Status),
+ UsesMods = [select_image(Xref, M) || M <- UsesModNames],
+ UsedByMods = [select_image(Xref, M) || M <- UsedByModNames],
+ redraw_mods(UsedByCtrl, UsedByMods),
+ redraw_mods(UsesCtrl, UsesMods).
+
+select_image(Xref, ModName) ->
+ {ok, M} = reltool_server:get_mod(Xref, ModName),
+ Image =
+ case M#mod.is_included of
+ _ when M#mod.app_name =:= ?MISSING_APP -> ?ERR_IMAGE;
+ true -> ?TICK_IMAGE;
+ false -> ?WARN_IMAGE;
+ undefined -> ?ERR_IMAGE
+ end,
+ {Image, M#mod.app_name, M}.
+
+redraw_mods(ListCtrl, []) ->
+ wxListCtrl:deleteAllItems(ListCtrl);
+redraw_mods(ListCtrl, ImageMods) ->
+ wxListCtrl:deleteAllItems(ListCtrl),
+ Add =
+ fun({ImageId, AppName, #mod{name = ModName}}, Row) ->
+ wxListCtrl:insertItem(ListCtrl, Row, ""),
+ if (Row rem 2) =:= 0 ->
+ wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255});
+ true ->
+ ignore
+ end,
+ wxListCtrl:setItem(ListCtrl, Row, ?MODS_MOD_COL,
+ atom_to_list(ModName), [{imageId, ImageId}]),
+ wxListCtrl:setItem(ListCtrl, Row, ?MODS_APP_COL,
+ atom_to_list(AppName), [{imageId, ImageId}]),
+ Row + 1
+ end,
+ wx:foldl(Add, 0, lists:sort(ImageMods)).
+
+redraw_config(S) ->
+ S.
+
+redraw_window(S) ->
+ redraw_config(S),
+ redraw_mods(S),
+ S.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+goto_line(#state{active_page = P} = S, LineNo) when is_integer(LineNo) ->
+ Editor = P#code_page.editor,
+ wxStyledTextCtrl:gotoLine(Editor, LineNo),
+ Left = wxStyledTextCtrl:getCurrentPos(Editor),
+ Right = wxStyledTextCtrl:getLineEndPosition(Editor, LineNo),
+ wxStyledTextCtrl:setSelection(Editor, Left, Right),
+ S;
+goto_line(#state{active_page = P} =S, Str) when is_list(Str) ->
+ try
+ LineNo = list_to_integer(Str),
+ CurrentPos = wxStyledTextCtrl:getCurrentPos(P#code_page.editor),
+ S2 = add_pos_to_history(S, CurrentPos),
+ goto_line(S2, LineNo - 1)
+ catch
+ _:_ ->
+ wxStatusBar:setStatusText(S#state.status_bar, "Not a line number"),
+ S
+ end.
+
+find_string(S, Str) ->
+ find_string(S, Str, 0).
+
+find_regexp_forward(S, Str) ->
+ S2 = find_string(S, Str, ?wxSTC_FIND_REGEXP),
+ TextCtrl = ((S2#state.active_page)#code_page.find_objs)#find_objs.search,
+ wxTextCtrl:setValue(TextCtrl, Str),
+ S2.
+
+find_string(#state{active_page = #code_page{editor = Editor,
+ find_objs = #find_objs{radio={NextO,_,CaseO}},
+ find_data = #find_data{found = Found} = Data} = P} = S,
+ Str,
+ Flag) ->
+ wxStyledTextCtrl:hideSelection(Editor, true),
+ Dir = wxRadioButton:getValue(NextO) xor wx_misc:getKeyState(?WXK_SHIFT),
+ Case = wxCheckBox:getValue(CaseO),
+ Pos =
+ if
+ Found, Dir -> %% Forward Continuation
+ wxStyledTextCtrl:getAnchor(Editor);
+ Found -> %% Backward Continuation
+ wxStyledTextCtrl:getCurrentPos(Editor);
+ Dir -> %% Forward wrap
+ 0;
+ true -> %% Backward wrap
+ wxStyledTextCtrl:getLength(Editor)
+ end,
+ wxStyledTextCtrl:gotoPos(Editor,Pos),
+ wxStyledTextCtrl:searchAnchor(Editor),
+ Flag2 =
+ if Case -> Flag bor ?wxSTC_FIND_MATCHCASE;
+ true -> Flag
+ end,
+ Res =
+ if
+ Dir -> wxStyledTextCtrl:searchNext(Editor, Flag2, Str);
+ true -> wxStyledTextCtrl:searchPrev(Editor, Flag2, Str)
+ end,
+ Found2 =
+ case Res >= 0 of
+ true ->
+ wxStyledTextCtrl:hideSelection(Editor, false),
+ %% io:format("Found ~p ~n",[Res]),
+ LineNo = wxStyledTextCtrl:lineFromPosition(Editor,Res),
+ wxStyledTextCtrl:scrollToLine(Editor, LineNo - 3),
+ wxStatusBar:setStatusText(S#state.status_bar, ""),
+ true;
+ false ->
+ wxStatusBar:setStatusText(S#state.status_bar,
+ "Not found (Hit Enter to wrap search)"),
+ false
+ end,
+ P2 = P#code_page{find_data = Data#find_data{found = Found2}},
+ Pages = lists:keystore(P#code_page.name, #code_page.name, S#state.code_pages, P2),
+ S#state{active_page = P2, code_pages = Pages}.
+
+goto_function(S, Editor) ->
+ wxStyledTextCtrl:hideSelection(Editor, false),
+ CurrentPos = wxStyledTextCtrl:getCurrentPos(Editor),
+ Left = wxStyledTextCtrl:wordStartPosition(Editor, CurrentPos, true),
+ Right = wxStyledTextCtrl:wordEndPosition(Editor, CurrentPos, true),
+ ColonPos = Left - 1,
+ Left2 =
+ case wxStyledTextCtrl:getCharAt(Editor, ColonPos) of
+ $: ->
+ wxStyledTextCtrl:wordStartPosition(Editor, ColonPos, true);
+ _ ->
+ Left
+ end,
+ Right2 =
+ case wxStyledTextCtrl:getCharAt(Editor, Right) of
+ $: ->
+ wxStyledTextCtrl:wordEndPosition(Editor, Right + 1, true);
+ _ ->
+ Right
+ end,
+ case [wxStyledTextCtrl:getCharAt(Editor, Right2)] of
+ "(" ->
+ wxStyledTextCtrl:setSelection(Editor, Left2, Right2),
+ Text = wxStyledTextCtrl:getSelectedText(Editor),
+ S2 = add_pos_to_history(S, CurrentPos),
+ do_goto_function(S2, string:tokens(Text, ":"));
+ _ ->
+ %% No function call
+ wxStyledTextCtrl:hideSelection(Editor, false),
+ wxStyledTextCtrl:setSelection(Editor, Left2, Right2),
+ S
+ end.
+
+do_goto_function(S, []) ->
+ S;
+do_goto_function(#state{active_page = P} = S, [FunName]) ->
+ wxStyledTextCtrl:gotoPos(P#code_page.editor, 1),
+ find_regexp_forward(S, "^" ++ FunName ++ "(");
+do_goto_function(S, [ModStr, FunStr]) ->
+ case reltool_server:get_mod(S#state.xref_pid, list_to_atom(ModStr)) of
+ {ok, Mod} when Mod#mod.app_name =/= ?MISSING_APP ->
+ S2 = create_code_page(S#state{mod = Mod}, ModStr),
+ find_regexp_forward(S2, "^" ++ FunStr ++ "(");
+ {ok, _} ->
+ wxStatusBar:setStatusText(S#state.status_bar, "No such module: " ++ ModStr),
+ S
+ end.
+
+goto_back(#state{active_page = #code_page{editor = Editor, find_data = Data} = Page,
+ code_pages = Pages} = S) ->
+ case Data#find_data.history of
+ [PrevPos | History] ->
+ LineNo = wxStyledTextCtrl:lineFromPosition(Editor, PrevPos),
+ Data2 = Data#find_data{history = History},
+ Page2 = Page#code_page{find_data = Data2},
+ Pages2 = lists:keystore(Page2#code_page.name, #code_page.name, Pages, Page2),
+ goto_line(S#state{active_page = Page2, code_pages = Pages2}, LineNo);
+ [] ->
+ wxStatusBar:setStatusText(S#state.status_bar, "No history"),
+ S
+ end.
+
+add_pos_to_history(#state{active_page = Page, code_pages = Pages} = S, CurrentPos) ->
+ Data = Page#code_page.find_data,
+ Data2 = Data#find_data{history = [CurrentPos | Data#find_data.history]},
+ Page2 = Page#code_page{find_data = Data2},
+ Pages2 = lists:keystore(Page2#code_page.name, #code_page.name, Pages, Page2),
+ S#state{active_page = Page2, code_pages = Pages2}.
+
+create_editor(Parent) ->
+ FixedFont = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]),
+ %%Ed = wxStyledTextCtrl:new(Parent, [{size, {700, 500}}]),
+ Ed = wxStyledTextCtrl:new(Parent),
+
+ wxStyledTextCtrl:styleClearAll(Ed),
+ wxStyledTextCtrl:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont),
+ wxStyledTextCtrl:setLexer(Ed, ?wxSTC_LEX_ERLANG),
+ wxStyledTextCtrl:setMarginType(Ed, 0, ?wxSTC_MARGIN_NUMBER),
+ LW = wxStyledTextCtrl:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, "9"),
+ wxStyledTextCtrl:setMarginWidth(Ed, 0, LW),
+
+ wxStyledTextCtrl:setSelectionMode(Ed, ?wxSTC_SEL_LINES),
+ %%wxStyledTextCtrl:hideSelection(Ed, true),
+
+ 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()),
+
+ %% Margins Markers
+ %% Breakpoint Should be a pixmap?
+ wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, [{foreground, {170,20,20}}]),
+ wxStyledTextCtrl:markerDefine(Ed, 0, ?wxSTC_MARK_CIRCLE, [{background, {200,120,120}}]),
+ %% Disabled Breakpoint
+ wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, [{foreground, {20,20,170}}]),
+ wxStyledTextCtrl:markerDefine(Ed, 1, ?wxSTC_MARK_CIRCLE, [{background, {120,120,200}}]),
+
+ %% Current Line
+ wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, [{foreground, {20,170,20}}]),
+ wxStyledTextCtrl:markerDefine(Ed, 2, ?wxSTC_MARK_ARROW, [{background, {200,255,200}}]),
+ wxStyledTextCtrl:markerDefine(Ed, 3, ?wxSTC_MARK_BACKGROUND, [{background, {200,255,200}}]),
+
+ %% Scrolling
+ Policy = ?wxSTC_CARET_SLOP bor ?wxSTC_CARET_JUMPS bor ?wxSTC_CARET_EVEN,
+ wxStyledTextCtrl:setYCaretPolicy(Ed, Policy, 3),
+ wxStyledTextCtrl:setVisiblePolicy(Ed, Policy, 3),
+
+ wxStyledTextCtrl:connect(Ed, stc_doubleclick),
+ wxWindow:connect(Ed, enter_window),
+
+ wxStyledTextCtrl:setReadOnly(Ed, true),
+ Ed.
+
+create_search_area(Parent) ->
+ Sizer = wxBoxSizer:new(?wxHORIZONTAL),
+ wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Find:"),
+ [{flag,?wxALIGN_CENTER_VERTICAL}]),
+ TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
+ wxSizer:add(Sizer, TC1, [{proportion,3}, {flag, ?wxEXPAND}]),
+ Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"),
+ wxRadioButton:setValue(Nbtn, true),
+ wxSizer:add(Sizer,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ Pbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Previous"),
+ wxSizer:add(Sizer,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"),
+ wxSizer:add(Sizer,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ wxSizer:add(Sizer, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]),
+ wxSizer:add(Sizer, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"),
+ [{flag,?wxALIGN_CENTER_VERTICAL}]),
+ TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
+ wxSizer:add(Sizer, TC2, [{proportion,0}, {flag, ?wxEXPAND}]),
+ Button = wxButton:new(Parent, ?wxID_ANY, [{label, "Back"}]),
+ wxSizer:add(Sizer, Button, []),
+
+ wxEvtHandler:connect(Button, command_button_clicked, [{userData, history_back}]),
+ %% wxTextCtrl:connect(TC1, command_text_updated),
+ wxTextCtrl:connect(TC1, command_text_enter),
+ %% wxTextCtrl:connect(TC1, kill_focus),
+ wxTextCtrl:connect(TC2, command_text_enter),
+ wxWindow:connect(Parent, command_button_clicked),
+ {#find_objs{search = TC1,goto = TC2,radio = {Nbtn,Pbtn,Cbtn}},
+ #find_data{start = 0, found = false, history = []},
+ Sizer}.
+
+load_code(Ed, Code) when is_binary(Code) ->
+ wxStyledTextCtrl:setReadOnly(Ed, false),
+ wxStyledTextCtrl:setTextRaw(Ed, <<Code/binary, 0:8>>),
+ Lines = wxStyledTextCtrl:getLineCount(Ed),
+ Sz = trunc(math:log10(Lines))+1,
+ LW = wxStyledTextCtrl:textWidth(Ed, ?wxSTC_STYLE_LINENUMBER, lists:duplicate(Sz, $9)),
+ %%io:format("~p ~p ~p~n", [Lines, Sz, LW]),
+ wxStyledTextCtrl:setMarginWidth(Ed, 0, LW+5),
+ wxStyledTextCtrl:setReadOnly(Ed, true),
+ 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]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sys callbacks
+
+system_continue(_Parent, _Debug, S) ->
+ ?MODULE:loop(S).
+
+system_terminate(Reason, _Parent, _Debug, _S) ->
+ exit(Reason).
+
+system_code_change(S,_Module,_OldVsn,_Extra) ->
+ {ok, S}.