%%
%% %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}.