diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/reltool/src/reltool_mod_win.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/reltool/src/reltool_mod_win.erl')
-rw-r--r-- | lib/reltool/src/reltool_mod_win.erl | 773 |
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}. |