aboutsummaryrefslogtreecommitdiffstats
path: root/lib/reltool/src/reltool_app_win.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/reltool/src/reltool_app_win.erl')
-rw-r--r--lib/reltool/src/reltool_app_win.erl886
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}.