diff options
Diffstat (limited to 'lib/reltool/src/reltool_app_win.erl')
-rw-r--r-- | lib/reltool/src/reltool_app_win.erl | 886 |
1 files changed, 886 insertions, 0 deletions
diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl new file mode 100644 index 0000000000..6083493c02 --- /dev/null +++ b/lib/reltool/src/reltool_app_win.erl @@ -0,0 +1,886 @@ +%% +%% %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_app_win). + +%% Public +-export([start_link/4, raise/1, refresh/1, open_mod/2]). + +%% Internal +-export([init/5, 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, + mod_wins, + sys, + common, + app, + frame, + panel, + book, + status_bar, + %% page, % apps | source | config + config_app_global, config_app_local, config_app_local_box, + config_mod_global, config_mod_local, config_mod_local_box, + config_latest, config_selected, config_source_box, + + app_used_by_ctrl, app_required_ctrl, app_incl_ctrl, app_uses_ctrl, + mods_source_ctrl, mods_white_ctrl, mods_black_ctrl, mods_derived_ctrl, + deps_used_by_ctrl, deps_uses_ctrl, + popup_menu}). +-record(mod_win, {name, pid}). + +-define(WIN_WIDTH, 800). +-define(WIN_HEIGHT, 600). +%% -define(MODS_MOD_COL_WIDTH, 250). +%% -define(MODS_APP_COL_WIDTH, 250). +%% -define(APPS_APP_COL_WIDTH, 250). + +-define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available +-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific +-define(CONTENTS_ITEM, 300). + +-define(MODS_MOD_COL, 0). +-define(MODS_APP_COL, 1). +-define(APPS_APP_COL, 0). + +-define(source, "Available"). +-define(whitelist, "Included"). +-define(blacklist, "Excluded"). +-define(derived, "Derived"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Client + +start_link(WxEnv, Xref, Common, AppName) -> + proc_lib:start_link(?MODULE, init, [self(), WxEnv, Xref, Common, AppName], infinity, []). + +raise(Pid) -> + reltool_utils:cast(Pid, raise). + +refresh(Pid) -> + reltool_utils:cast(Pid, refresh). + +open_mod(Pid, ModName) -> + reltool_utils:call(Pid, {open_mod, ModName}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Server + +init(Parent, WxEnv, Xref, C, AppName) -> + try + do_init(Parent, WxEnv, Xref, C, AppName) + catch + error:Reason -> + exit({Reason, erlang:get_stacktrace()}) + end. + +do_init(Parent, WxEnv, Xref, C, AppName) -> + process_flag(trap_exit, C#common.trap_exit), + {ok, App} = reltool_server:get_app(Xref, AppName), + {ok, Sys} = reltool_server:get_sys(Xref), + S = #state{parent_pid = Parent, + xref_pid = Xref, + mod_wins = [], + sys = Sys, + common = C, + app = App}, + 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, app = App} = S) -> + receive + {system, From, Msg} -> + Dbg = C#common.sys_debug, + sys:handle_system_msg(Msg, 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} -> + case reltool_server:get_app(Xref, App#app.name) of + {ok, App2} -> + {ok, Sys} = reltool_server:get_sys(Xref), + S2 = redraw_window(S#state{sys = Sys, app = App2}), + [ok = reltool_mod_win:refresh(MW#mod_win.pid) || MW <- S2#state.mod_wins], + ?MODULE:loop(S2); + {error, _Reason} -> + wxFrame:destroy(S#state.frame), + exit(shutdown) + end; + {call, ReplyTo, Ref, {open_mod, ModName}} -> + S2 = create_mod_window(S, ModName), + {value, #mod_win{pid = ModPid}} = lists:keysearch(ModName, #mod_win.name, S2#state.mod_wins), + reltool_utils:reply(ReplyTo, Ref, {ok, ModPid}), + ?MODULE:loop(S2); + #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); + {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> + exit(Reason); + {'EXIT', Pid, _Reason} = Exit -> + exit_warning(Exit), + S2 = S#state{mod_wins = lists:keydelete(Pid, #mod_win.pid, S#state.mod_wins)}, + ?MODULE:loop(S2); + Msg -> + error_logger:format("~p~p got unexpected message:\n\t~p\n", + [?MODULE, self(), Msg]), + ?MODULE:loop(S) + end. + +exit_warning({'EXIT', _Pid, shutdown}) -> + ok; +exit_warning({'EXIT', _Pid, _Reason} = Msg) -> + error_logger:format("~p~p got unexpected message:\n\t~p\n", + [?MODULE, self(), Msg]). + +create_window(#state{app = App} = S) -> + Title = app_title(App), + 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}, + Derived = app_to_mods(S2), + S3 = create_mods_page(S2, Derived), + S4 = create_apps_page(S3, Derived), + S5 = create_deps_page(S4, Derived), + S6 = create_config_page(S5), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]), + + wxPanel:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Frame), + wxSizer:setSizeHints(Sizer, Frame), + wxFrame:show(Frame), + + wxFrame:connect(Frame, close_window), + S6. + +app_title(App) -> + lists:concat([?APPLICATION, " - ", App#app.label]). + +create_apps_page(S, Derived) -> + Panel = wxPanel:new(S#state.book, []), + Main = wxBoxSizer:new(?wxVERTICAL), + Upper = wxBoxSizer:new(?wxHORIZONTAL), + Lower = wxBoxSizer:new(?wxHORIZONTAL), + + UsedByCtrl = create_apps_list_ctrl(Panel, Upper, "Used by"), + wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + + RequiredCtrl = create_apps_list_ctrl(Panel, Upper, "Required"), + wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + InclCtrl = create_apps_list_ctrl(Panel, Upper, "Included"), + wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + UsesCtrl = create_apps_list_ctrl(Panel, Upper, "Uses"), + S2 = S#state{app_required_ctrl = RequiredCtrl, + app_used_by_ctrl = UsedByCtrl, + app_incl_ctrl = InclCtrl, + app_uses_ctrl = UsesCtrl}, + redraw_apps(S2, Derived), + wxSizer:add(Main, Upper, + [{border, 2}, + {flag, ?wxALL bor ?wxEXPAND}, + {proportion, 1}]), + + wxSizer:add(Main, Lower, + [{border, 2}, + {flag, ?wxALL bor ?wxEXPAND}]), + wxPanel:setSizer(Panel, Main), + wxNotebook:addPage(S2#state.book, Panel, "Application dependencies", []), + S2. + +create_apps_list_ctrl(Panel, Sizer, Text) -> + Width = lists:max([100, ?WIN_WIDTH - 40]) div 4, + 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, Text), + wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), + %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?APPS_APP_COL_WIDTH), + wxListItem:destroy(ListItem), + + wxSizer:add(Sizer, ListCtrl, + [{border, 2}, + {flag, ?wxALL bor ?wxEXPAND}, + {proportion, 1}]), + wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, apps_list_ctrl}]), + wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_app}]), + wxWindow:connect(ListCtrl, enter_window), + ListCtrl. + +create_deps_page(S, Derived) -> + Panel = wxPanel:new(S#state.book, []), + Main = wxBoxSizer:new(?wxHORIZONTAL), + + UsedByCtrl = create_mods_list_ctrl(Panel, Main, "Modules used by others", " and their applications", undefined, undefined), + 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", undefined, undefined), + S2 = S#state{deps_used_by_ctrl = UsedByCtrl, + deps_uses_ctrl = UsesCtrl}, + redraw_mods(S2, Derived), + wxPanel:setSizer(Panel, Main), + wxNotebook:addPage(S2#state.book, Panel, "Module dependencies", []), + S2. + +create_mods_page(S, Derived) -> + Panel = wxPanel:new(S#state.book, []), + MainSz = wxBoxSizer:new(?wxHORIZONTAL), + + SourceCtrl = create_mods_list_ctrl(Panel, MainSz, ?source, "", whitelist_add, blacklist_add), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + WhiteCtrl = create_mods_list_ctrl(Panel, MainSz, ?whitelist, "", whitelist_del, blacklist_add), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + BlackCtrl = create_mods_list_ctrl(Panel, MainSz, ?blacklist, "", whitelist_add, blacklist_del), + wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + DerivedCtrl = create_mods_list_ctrl(Panel, MainSz, ?derived, "", whitelist_add, blacklist_add), + S2 = S#state{mods_source_ctrl = SourceCtrl, + mods_white_ctrl = WhiteCtrl, + mods_black_ctrl = BlackCtrl, + mods_derived_ctrl = DerivedCtrl}, + redraw_mods(S2, Derived), + wxPanel:setSizer(Panel, MainSz), + wxNotebook:addPage(S2#state.book, Panel, "Modules", []), + S2. + +create_mods_list_ctrl(Panel, OuterSz, Title, AppText, Tick, Cross) -> + ListCtrl = wxListCtrl:new(Panel, + [{style, + ?wxLC_REPORT bor + %% ?wxLC_SORT_ASCENDING bor + %% ?wxLC_SINGLE_SEL bor + ?wxHSCROLL bor + ?wxVSCROLL}]), + ToolTip = "Select module(s) or open separate module window with a double click.", + wxListCtrl:setToolTip(ListCtrl, ToolTip), + + %% Prep images + reltool_utils:assign_image_list(ListCtrl), + + %% Prep column label + ListItem = wxListItem:new(), + wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), + wxListItem:setText(ListItem, Title), + wxListCtrl:insertColumn(ListCtrl, ?MODS_MOD_COL, ListItem), + %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, ?MODS_MOD_COL_WIDTH), + Prop = + case AppText =/= "" of + true -> + wxListItem:setText(ListItem, AppText), + wxListCtrl:insertColumn(ListCtrl, ?MODS_APP_COL, ListItem), + %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, ?MODS_APP_COL_WIDTH), + 2; + false -> + 1 + end, + wxListItem:destroy(ListItem), + + ButtonSz = wxBoxSizer:new(?wxHORIZONTAL), + create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_TICK_MARK", Tick), + create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_CROSS_MARK", Cross), + wxEvtHandler:connect(ListCtrl, size, [{skip, true}, {userData, mods_list_ctrl}]), + wxListCtrl:connect(ListCtrl, command_list_item_activated, [{userData, open_mod}]), + wxWindow:connect(ListCtrl, enter_window), + InnerSz = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(InnerSz, ListCtrl, + [{border, 2}, + {flag, ?wxALL bor ?wxEXPAND}, + {proportion, 1}]), + wxSizer:add(InnerSz, ButtonSz, + [{flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(OuterSz, InnerSz, + [{flag, ?wxALL bor ?wxEXPAND}, + {proportion, Prop}]), + ListCtrl. + +create_button(_Panel, Sizer, _ListCtrl, _Title, _BitMapName, undefined) -> + wxSizer:addStretchSpacer(Sizer); +create_button(Panel, Sizer, ListCtrl, Title, BitMapName, Action) -> + %% InnerSz = wxBoxSizer:new(?wxVERTICAL), + BitMap = wxArtProvider:getBitmap(BitMapName), + Button = wxBitmapButton:new(Panel, ?wxID_ANY, BitMap, []), + ToolTip = action_to_tool_tip(Title, Action), + wxBitmapButton:setToolTip(Button, ToolTip), + %% wxSizer:add(InnerSz, Button, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + Opts = [{userData, {mod_button, Action, ListCtrl}}], + wxEvtHandler:connect(Button, command_button_clicked, Opts), + wxSizer:add(Sizer, Button, + [{border, 2}, + {flag, ?wxALL bor ?wxALIGN_CENTER_HORIZONTAL}, + {proportion, 1}]). + +action_to_tool_tip(Label, Action) -> + case Action of + whitelist_add when Label =:= ?whitelist -> + "Remove selected module(s) from whitelist."; + whitelist_add -> + "Add selected module(s) to whitelist."; + whitelist_del -> + "Remove selected module(s)from whitelist."; + blacklist_add when Label =:= ?blacklist -> + "Remove selected module(s) from blacklist."; + blacklist_add -> + "Add selected module(s) to blacklist."; + blacklist_del -> + "Remove selected module(s) from blacklist." + end. + +create_config_page(#state{app = App} = S) -> + Panel = wxPanel:new(S#state.book, []), + TopSizer = wxBoxSizer:new(?wxVERTICAL), + + %% Source dirs + {LatestRadio, SelectedRadio, SourceBox} = + create_double_box(Panel, + TopSizer, + "Source selection policy", + "Use latest version", + use_latest_vsn, + "Use selected version", + use_selected_vsn, + "Directories", + App#app.sorted_dirs, + version), + + InclSizer = wxBoxSizer:new(?wxHORIZONTAL), + + %% Application inclusion + {AppGlobalRadio, AppLocalRadio, AppLocalBox} = + create_double_box(Panel, + InclSizer, + "Application inclusion policy", + "Use global config", + global_incl_cond, + "Use application specific config", + local_incl_cond, + "Application specific", + reltool_utils:incl_conds(), + incl_cond), + + %% Module inclusion + {ModGlobalRadio, ModLocalRadio, ModLocalBox} = + create_double_box(Panel, + InclSizer, + "Module inclusion policy", + "Use global config", + global_mod_cond, + "Use application specific config", + local_mod_cond, + "Application specific", + reltool_utils:mod_conds(), + mod_cond), + wxSizer:add(TopSizer, InclSizer, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), + + S2 = S#state{config_app_global = AppGlobalRadio, + config_app_local = AppLocalRadio, + config_app_local_box = AppLocalBox, + config_mod_global = ModGlobalRadio, + config_mod_local = ModLocalRadio, + config_mod_local_box = ModLocalBox, + config_latest = LatestRadio, + config_selected = SelectedRadio, + config_source_box = SourceBox}, + redraw_config(S2), + wxPanel:setSizer(Panel, TopSizer), + wxNotebook:addPage(S2#state.book, Panel, "Application settings", []), + S2. + +create_double_box(Panel, Sizer, TopLabel, + OuterText, OuterData, + InnerText, InnerData, + InternalLabel, InternalChoices, InternalChoiceData) -> + TopSizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, + [{label, TopLabel}]), + OuterSizer = wxBoxSizer:new(?wxVERTICAL), + OuterRadio = wxRadioButton:new(Panel, ?wxID_ANY, OuterText, + [{style, ?wxRB_GROUP}]), + wxEvtHandler:connect(OuterRadio, command_radiobutton_selected, + [{userData, OuterData}]), + InnerRadio = wxRadioButton:new(Panel, ?wxID_ANY, InnerText), + wxEvtHandler:connect(InnerRadio, command_radiobutton_selected, + [{userData, InnerData}]), + InnerBox = wxRadioBox:new(Panel, + ?wxID_ANY, + InternalLabel, + ?wxDefaultPosition, + ?wxDefaultSize, + InternalChoices, + []), + wxEvtHandler:connect(InnerBox, command_radiobox_selected, + [{userData, InternalChoiceData}]), + wxSizer:add(OuterSizer, OuterRadio, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(OuterSizer, InnerRadio, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(TopSizer, OuterSizer, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), + wxSizer:add(TopSizer, InnerBox, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(Sizer, TopSizer, + [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), + {OuterRadio, InnerRadio, InnerBox}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_event(#state{sys = Sys, app = App} = S, Wx) -> + %% io:format("wx: ~p\n", [Wx]), + case Wx of + #wx{obj = ObjRef, event = #wxMouse{type = enter_window}} -> + wxWindow:setFocus(ObjRef), + S; + #wx{obj= ListCtrl, userData = mods_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} -> + HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), + case HasApps of + false -> + wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, W); + true -> + wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, (2 * W) div 3), + wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, W div 3) + end, + S; + #wx{obj= ListCtrl, userData = apps_list_ctrl, event = #wxSize{type = size, size = {W, _H}}} -> + wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, W), + S; + #wx{userData = open_app, + obj = ListCtrl, + event = #wxList{type = command_list_item_activated, itemIndex = Pos}} -> + AppBase = wxListCtrl:getItemText(ListCtrl, Pos), + {AppName, _AppVsn} = reltool_utils:split_app_name(AppBase), + {ok, _AppPid} = reltool_sys_win:open_app(S#state.parent_pid, AppName), + S; + #wx{userData = open_mod, + obj = ListCtrl, + event = #wxList{type = command_list_item_activated, itemIndex = Pos}} -> + ModName = list_to_atom(wxListCtrl:getItemText(ListCtrl, Pos)), + create_mod_window(S, ModName); + #wx{userData = global_incl_cond} -> + %% Use global setting + change_incl_cond(S, App, undefined); + #wx{userData = local_incl_cond} -> + %% Use app spec setting + change_incl_cond(S, App, Sys#sys.incl_cond); + #wx{userData = incl_cond, + %% Change app spec setting + event = #wxCommand{type = command_radiobox_selected, + cmdString = Sel}} -> + AppCond = reltool_utils:list_to_incl_cond(Sel), + change_incl_cond(S, App, AppCond); + + #wx{userData = global_mod_cond} -> + %% Use global setting + change_mod_cond(S, App, undefined); + #wx{userData = local_mod_cond} -> + %% Use app spec setting + change_mod_cond(S, App, Sys#sys.mod_cond); + #wx{userData = mod_cond, + %% Change app spec setting + event = #wxCommand{type = command_radiobox_selected, + cmdString = Sel}} -> + ModCond = reltool_utils:list_to_mod_cond(Sel), + change_mod_cond(S, App, ModCond); + + #wx{userData = use_latest_vsn} -> + %% Use latest version + App2 = App#app{use_selected_vsn = undefined}, + S2 = change_version(S, App2, App#app.active_dir), + redraw_window(S2); + #wx{userData = use_selected_vsn} -> + %% Use selected version + App2 = App#app{use_selected_vsn = true}, + {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), + S2 = S#state{app = App3}, + redraw_window(S2); + #wx{userData = version, + event = #wxCommand{type = command_radiobox_selected, + cmdString = ActiveDir}} -> + %% Change app source + S2 = change_version(S, App, ActiveDir), + redraw_window(S2); + #wx{userData = {mod_button, Action, ListCtrl}, + event = #wxCommand{type = command_button_clicked}} -> + Items = reltool_utils:get_items(ListCtrl), + handle_mod_button(S, Items, Action); + _ -> + error_logger:format("~p~p got unexpected app event from wx:\n\t~p\n", + [?MODULE, self(), Wx]), + S + end. + +create_mod_window(#state{parent_pid = RelPid, xref_pid = Xref, common = C} = S, ModName) -> + case lists:keysearch(ModName, #mod_win.name, S#state.mod_wins) of + false -> + WxEnv = wx:get_env(), + {ok, Pid} = reltool_mod_win:start_link(WxEnv, Xref, RelPid, C, ModName), + MW = #mod_win{name = ModName, pid = Pid}, + S#state{mod_wins = [MW | S#state.mod_wins]}; + {value, MW} -> + reltool_app_win:raise(MW#mod_win.pid), + S + end. + +handle_mod_button(#state{app = App} = S, Items, Action) -> + App2 = lists:foldl(fun(Item, A) -> move_mod(A, Item, Action) end, App, Items), + {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), + S2 = S#state{app = App3}, + redraw_window(S2). + +move_mod(App, {_ItemNo, ModStr}, Action) -> + ModName = list_to_atom(ModStr), + Mods = App#app.mods, + {value, M} = lists:keysearch(ModName, #mod.name, Mods), + AppCond = + case Action of + whitelist_add -> + case M#mod.incl_cond of + include -> undefined; + exclude -> include; + undefined -> include + end; + whitelist_del -> + undefined; + blacklist_add -> + exclude; + blacklist_del -> + undefined; + _ -> + error_logger:format("~p~p got unexpected mod button event: ~p\n\t ~p\n", + [?MODULE, self(), ModName, Action]), + M#mod.incl_cond + end, + M2 = M#mod{incl_cond = AppCond}, + Mods2 = lists:keystore(ModName, #mod.name, Mods, M2), + App#app{mods = Mods2}. + +change_incl_cond(S, App, NewAppCond) -> + App2 = App#app{incl_cond = NewAppCond}, + {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), + S2 = S#state{app = App3}, + redraw_window(S2). + +change_mod_cond(S, App, NewModCond) -> + App2 = App#app{mod_cond = NewModCond}, + {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), + S2 = S#state{app = App3}, + redraw_window(S2). + +change_version(S, App, NewDir) -> + App2 = App#app{active_dir = NewDir, label = undefined, vsn = undefined, info = undefined}, + {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), + Title = app_title(App3), + wxFrame:setTitle(S#state.frame, Title), + S#state{app = App3}. + +redraw_apps(#state{app = #app{info = AppInfo}, + app_used_by_ctrl = UsedByCtrl, + app_required_ctrl = RequiredCtrl, + app_incl_ctrl = InclCtrl, + app_uses_ctrl = UsesCtrl, + xref_pid = Xref}, + {_SourceMods, _WhiteMods, _BlackMods, _DerivedMods, UsedByMods, UsesMods}) -> + UsedByApps = lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsedByMods]), + Select = + fun(AppName) -> + {ok, App} = reltool_server:get_app(Xref, AppName), + case App#app.status of + missing -> {AppName, ?ERR_IMAGE}; + ok -> {AppName, ?TICK_IMAGE} + end + end, + RequiredApps = lists:sort(lists:map(Select, AppInfo#app_info.applications)), + InclApps = lists:map(Select, AppInfo#app_info.incl_apps), + UsesApps = lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsesMods]), + do_redraw_apps(UsedByCtrl, UsedByApps), + do_redraw_apps(RequiredCtrl, RequiredApps), + do_redraw_apps(InclCtrl, InclApps), + do_redraw_apps(UsesCtrl, UsesApps), + ok. + +do_redraw_apps(ListCtrl, []) -> + wxListCtrl:deleteAllItems(ListCtrl); + %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?wxLIST_AUTOSIZE_USEHEADER); +do_redraw_apps(ListCtrl, AppImages) -> + wxListCtrl:deleteAllItems(ListCtrl), + Add = + fun({AppName, ImageId}, {Row, Prev}) when AppName =/= Prev -> + wxListCtrl:insertItem(ListCtrl, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(ListCtrl, Row, {240,240,255}); + true -> + ignore + end, + Str = atom_to_list(AppName), + wxListCtrl:setItem(ListCtrl, Row, ?APPS_APP_COL, Str, [{imageId, ImageId}]), + {Row + 1, AppName}; + ({_, _}, Acc) -> + Acc + end, + wx:foldl(Add, {0, undefined}, AppImages). + +%% print(X, X, Format, Args) -> +%% io:format(Format, Args); +%% print(_, _, _, _) -> +%% ok. + +redraw_mods(#state{mods_source_ctrl = SourceCtrl, + mods_white_ctrl = WhiteCtrl, + mods_black_ctrl = BlackCtrl, + mods_derived_ctrl = DerivedCtrl, + deps_used_by_ctrl = UsedByCtrl, + deps_uses_ctrl = UsesCtrl, + app = #app{is_pre_included = IsPre, is_included = IsIncl}, + status_bar = Bar}, + {SourceMods, WhiteMods, BlackMods, DerivedMods, UsedByMods, UsesMods}) -> + InclStatus = + case IsIncl of + true when IsPre =:= true -> "Whitelist - "; + true -> "Derived - "; + false -> "Blacklist - "; + undefined -> "Source - " + end, + Status = lists:concat([InclStatus, + length(WhiteMods), " whitelisted modules and ", + length(DerivedMods), " derived modules."]), + wxStatusBar:setStatusText(Bar, Status), + opt_redraw_mods(SourceCtrl, SourceMods), + opt_redraw_mods(WhiteCtrl, WhiteMods), + opt_redraw_mods(BlackCtrl, BlackMods), + opt_redraw_mods(DerivedCtrl, DerivedMods), + opt_redraw_mods(UsedByCtrl, UsedByMods), + opt_redraw_mods(UsesCtrl, UsesMods). + +app_to_mods(#state{xref_pid = Xref, app = App}) -> + SourceMods = [M || M <- App#app.mods, + M#mod.is_included =/= true, + M#mod.is_pre_included =/= false], + WhiteMods = [M || M <- App#app.mods, + M#mod.is_pre_included =:= true], + BlackMods = [M || M <- App#app.mods, + M#mod.is_pre_included =:= false], + DerivedMods = [M || M <- App#app.mods, + M#mod.is_included =:= true, + M#mod.is_pre_included =/= true], + GetMod = + fun(ModName) when is_atom(ModName) -> + {ok, M} = reltool_server:get_mod(Xref, ModName), + if + M#mod.app_name =:= App#app.name, M#mod.is_included =:= true -> + false; + true -> + {true, M} + end + end, + UsedByMods = lists:zf(GetMod, App#app.used_by_mods), + UsesMods = lists:zf(GetMod, App#app.uses_mods), + { + [select_image(source, M) || M <- SourceMods], + [select_image(whitelist, M) || M <- WhiteMods], + [select_image(blacklist, M) || M <- BlackMods], + [select_image(derived, M) || M <- DerivedMods], + [select_image(used_by, M) || M <- UsedByMods], + [select_image(uses, M) || M <- UsesMods] + }. + +select_image(Kind, M) -> + Image = + case Kind of + blacklist when M#mod.status =:= missing -> + ?WARN_IMAGE; + source when M#mod.status =:= missing -> + ?WARN_IMAGE; + _ when M#mod.status =:= missing -> + ?ERR_IMAGE; + blacklist when M#mod.incl_cond =:= exclude -> + ?CROSS_IMAGE; + blacklist -> + ?SOURCE_IMAGE; + source -> + ?CROSS_IMAGE; + whitelist when M#mod.incl_cond =:= include -> + ?TICK_IMAGE; + whitelist -> + ?SOURCE_IMAGE; + derived -> + ?TICK_IMAGE; + used_by when M#mod.is_included =:= true -> + ?TICK_IMAGE; + used_by when M#mod.is_included =:= false -> + ?WARN_IMAGE; + used_by -> + ?ERR_IMAGE; + uses when M#mod.is_included =:= true -> + ?TICK_IMAGE; + uses when M#mod.is_included =:= false -> + ?WARN_IMAGE; + uses -> + ?ERR_IMAGE + end, + {Image, M#mod.app_name, M}. + +opt_redraw_mods(undefined, _ImageMods) -> + ok; +opt_redraw_mods(ListCtrl, ImageMods) -> + HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), + do_redraw_mods(ListCtrl, ImageMods, HasApps). + +do_redraw_mods(ListCtrl, [], _HasApps) -> + wxListCtrl:deleteAllItems(ListCtrl); +do_redraw_mods(ListCtrl, ImageMods, HasApps) -> + 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}]), + case HasApps of + false -> + ok; + true -> + wxListCtrl:setItem(ListCtrl, + Row, + ?MODS_APP_COL, + atom_to_list(AppName), + [{imageId, ImageId}]) + end, + Row + 1 + end, + wx:foldl(Add, 0, lists:sort(ImageMods)). + +redraw_config(#state{sys = #sys{incl_cond = GlobalIncl, + mod_cond = GlobalSource}, + app = #app{incl_cond = LocalIncl, + mod_cond = LocalSource, + use_selected_vsn = UseSelected, + active_dir = ActiveDir, + sorted_dirs = SortedDirs}, + config_app_global = AppGlobalRadio, + config_app_local = AppLocalRadio, + config_app_local_box = AppLocalBox, + config_mod_global = ModGlobalRadio, + config_mod_local = ModLocalRadio, + config_mod_local_box = ModLocalBox, + config_latest = LatestRadio, + config_selected = SelectedRadio, + config_source_box = SourceBox}) -> + redraw_double_box(GlobalIncl, + LocalIncl, + AppGlobalRadio, + AppLocalRadio, + AppLocalBox, + fun reltool_utils:incl_cond_to_index/1), + redraw_double_box(GlobalSource, + LocalSource, + ModGlobalRadio, + ModLocalRadio, + ModLocalBox, + fun reltool_utils:mod_cond_to_index/1), + redraw_double_box(false, + UseSelected, + LatestRadio, + SelectedRadio, + SourceBox, + fun(true) -> + reltool_utils:elem_to_index(ActiveDir, SortedDirs) - 1; + (false) -> + 0 + end). + +redraw_double_box(Global, Local, GlobalRadio, LocalRadio, LocalBox, GetChoice) -> + AppCond = + case Local of + undefined -> + wxRadioButton:setValue(GlobalRadio, true), + wxRadioButton:setValue(LocalRadio, false), + wxRadioBox:disable(LocalBox), + Global; + _ -> + wxRadioButton:setValue(GlobalRadio, false), + wxRadioButton:setValue(LocalRadio, true), + wxRadioBox:enable(LocalBox), + Local + end, + Choice = GetChoice(AppCond), + wxRadioBox:setSelection(LocalBox, Choice). + +redraw_window(S) -> + %% wx_misc:beginBusyCursor(), + Derived = app_to_mods(S), + redraw_config(S), + redraw_mods(S, Derived), + redraw_apps(S, Derived), + %% wx_misc:endBusyCursor(), + S. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% 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}. |