1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2009-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20-module(reltool_app_win). 21 22%% Public 23-export([start_link/4, raise/1, refresh/1, open_mod/2]). 24 25%% Internal 26-export([init/5, loop/1]). 27 28%% sys callback functions 29-export([ 30 system_continue/3, 31 system_terminate/4, 32 system_code_change/4 33 ]). 34 35-include_lib("wx/include/wx.hrl"). 36-include("reltool.hrl"). 37 38-record(state, 39 {parent_pid, 40 xref_pid, 41 mod_wins, 42 sys, 43 common, 44 app, 45 frame, 46 panel, 47 book, 48 status_bar, 49 %% page, % apps | source | config 50 config_app_global, config_app_local, config_app_local_box, 51 config_mod_global, config_mod_local, config_mod_local_box, 52 config_latest, config_selected, config_source_box, 53 54 app_used_by_ctrl, app_required_ctrl, app_incl_ctrl, app_uses_ctrl, 55 mods_source_ctrl, mods_white_ctrl, mods_black_ctrl, mods_derived_ctrl, 56 deps_used_by_ctrl, deps_uses_ctrl, 57 popup_menu}). 58-record(mod_win, {name, pid}). 59 60-define(WIN_WIDTH, 800). 61-define(WIN_HEIGHT, 600). 62%% -define(MODS_MOD_COL_WIDTH, 250). 63%% -define(MODS_APP_COL_WIDTH, 250). 64%% -define(APPS_APP_COL_WIDTH, 250). 65 66-define(CLOSE_ITEM, ?wxID_EXIT). %% Use OS specific version if available 67-define(ABOUT_ITEM, ?wxID_ABOUT). %% Use OS specific 68-define(CONTENTS_ITEM, 300). 69 70-define(MODS_MOD_COL, 0). 71-define(MODS_APP_COL, 1). 72-define(APPS_APP_COL, 0). 73 74-define(source, "Available"). 75-define(whitelist, "Included"). 76-define(blacklist, "Excluded"). 77-define(derived, "Derived"). 78 79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 80%% Client 81 82start_link(WxEnv, Xref, Common, AppName) -> 83 proc_lib:start_link(?MODULE, 84 init, 85 [self(), WxEnv, Xref, Common, AppName], 86 infinity, 87 []). 88 89raise(Pid) -> 90 reltool_utils:cast(Pid, raise). 91 92refresh(Pid) -> 93 reltool_utils:cast(Pid, refresh). 94 95open_mod(Pid, ModName) -> 96 reltool_utils:call(Pid, {open_mod, ModName}). 97 98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 99%% Server 100 101init(Parent, WxEnv, Xref, C, AppName) -> 102 try 103 do_init(Parent, WxEnv, Xref, C, AppName) 104 catch 105 error:Reason:Stacktrace -> 106 exit({Reason, Stacktrace}) 107 end. 108 109do_init(Parent, WxEnv, Xref, C, AppName) -> 110 process_flag(trap_exit, C#common.trap_exit), 111 {ok, App} = reltool_server:get_app(Xref, AppName), 112 {ok, Sys} = reltool_server:get_sys(Xref), 113 S = #state{parent_pid = Parent, 114 xref_pid = Xref, 115 mod_wins = [], 116 sys = Sys, 117 common = C, 118 app = App}, 119 proc_lib:init_ack(Parent, {ok, self()}), 120 wx:set_env(WxEnv), 121 wx:debug(C#common.wx_debug), 122 S2 = wx:batch(fun() -> create_window(S) end), 123 loop(S2). 124 125loop(#state{xref_pid = Xref, common = C, app = App} = S) -> 126 receive 127 {system, From, Msg} -> 128 Dbg = C#common.sys_debug, 129 sys:handle_system_msg(Msg, 130 From, 131 S#state.parent_pid, 132 ?MODULE, 133 Dbg, 134 S); 135 {cast, _From, raise} -> 136 wxFrame:raise(S#state.frame), 137 wxFrame:setFocus(S#state.frame), 138 ?MODULE:loop(S); 139 {cast, _From, refresh} -> 140 case reltool_server:get_app(Xref, App#app.name) of 141 {ok, App2} -> 142 {ok, Sys} = reltool_server:get_sys(Xref), 143 S2 = redraw_window(S#state{sys = Sys, app = App2}), 144 [ok = reltool_mod_win:refresh(MW#mod_win.pid) || 145 MW <- S2#state.mod_wins], 146 ?MODULE:loop(S2); 147 {error, _Reason} -> 148 wxFrame:destroy(S#state.frame), 149 exit(shutdown) 150 end; 151 {call, ReplyTo, Ref, {open_mod, ModName}} -> 152 S2 = create_mod_window(S, ModName), 153 {value, #mod_win{pid = ModPid}} = 154 lists:keysearch(ModName, #mod_win.name, S2#state.mod_wins), 155 reltool_utils:reply(ReplyTo, Ref, {ok, ModPid}), 156 ?MODULE:loop(S2); 157 #wx{event = #wxSize{}} = Wx -> 158 Wx2 = reltool_utils:get_latest_resize(Wx), 159 S2 = handle_event(S, Wx2), 160 ?MODULE:loop(S2); 161 #wx{obj = ObjRef, 162 event = #wxClose{type = close_window}} -> 163 wxFrame:destroy(ObjRef), 164 exit(shutdown); 165 #wx{} = Wx -> 166 S2 = handle_event(S, Wx), 167 ?MODULE:loop(S2); 168 {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> 169 exit(Reason); 170 {'EXIT', Pid, _Reason} = Exit -> 171 exit_warning(Exit), 172 S2 = S#state{mod_wins = lists:keydelete(Pid, 173 #mod_win.pid, 174 S#state.mod_wins)}, 175 ?MODULE:loop(S2); 176 Msg -> 177 error_logger:format("~w~w got unexpected message:\n\t~tp\n", 178 [?MODULE, self(), Msg]), 179 ?MODULE:loop(S) 180 end. 181 182exit_warning({'EXIT', _Pid, shutdown}) -> 183 ok; 184exit_warning({'EXIT', _Pid, _Reason} = Msg) -> 185 error_logger:format("~w~w got unexpected message:\n\t~tp\n", 186 [?MODULE, self(), Msg]). 187 188create_window(#state{app = App} = S) -> 189 Title = app_title(App), 190 Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, []), 191 %% wxFrame:setSize(Frame, {?WIN_WIDTH, ?WIN_HEIGHT}), 192 Panel = wxPanel:new(Frame, []), 193 StatusBar = wxFrame:createStatusBar(Frame,[]), 194 195 Book = wxNotebook:new(Panel, ?wxID_ANY, []), 196 197 S2 = S#state{frame = Frame, 198 panel = Panel, 199 book = Book, 200 status_bar = StatusBar}, 201 Derived = app_to_mods(S2), 202 S3 = create_mods_page(S2, Derived), 203 S4 = create_apps_page(S3, Derived), 204 S5 = create_deps_page(S4, Derived), 205 S6 = create_config_page(S5), 206 Sizer = wxBoxSizer:new(?wxVERTICAL), 207 wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]), 208 209 wxPanel:setSizer(Panel, Sizer), 210 wxSizer:fit(Sizer, Frame), 211 wxSizer:setSizeHints(Sizer, Frame), 212 wxFrame:show(Frame), 213 214 wxFrame:connect(Frame, close_window), 215 S6. 216 217app_title(App) -> 218 lists:concat([?APPLICATION, " - ", App#app.label]). 219 220create_apps_page(S, Derived) -> 221 Panel = wxPanel:new(S#state.book, []), 222 Main = wxBoxSizer:new(?wxVERTICAL), 223 Upper = wxBoxSizer:new(?wxHORIZONTAL), 224 Lower = wxBoxSizer:new(?wxHORIZONTAL), 225 226 UsedByCtrl = create_apps_list_ctrl(Panel, Upper, "Used by"), 227 wxSizer:add(Upper, 228 wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 229 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 230 231 RequiredCtrl = create_apps_list_ctrl(Panel, Upper, "Required"), 232 wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 233 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 234 InclCtrl = create_apps_list_ctrl(Panel, Upper, "Included"), 235 wxSizer:add(Upper, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 236 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 237 UsesCtrl = create_apps_list_ctrl(Panel, Upper, "Uses"), 238 S2 = S#state{app_required_ctrl = RequiredCtrl, 239 app_used_by_ctrl = UsedByCtrl, 240 app_incl_ctrl = InclCtrl, 241 app_uses_ctrl = UsesCtrl}, 242 redraw_apps(S2, Derived), 243 wxSizer:add(Main, Upper, 244 [{border, 2}, 245 {flag, ?wxALL bor ?wxEXPAND}, 246 {proportion, 1}]), 247 248 wxSizer:add(Main, Lower, 249 [{border, 2}, 250 {flag, ?wxALL bor ?wxEXPAND}]), 251 wxPanel:setSizer(Panel, Main), 252 wxNotebook:addPage(S2#state.book, Panel, "Application dependencies", []), 253 S2. 254 255create_apps_list_ctrl(Panel, Sizer, Text) -> 256 Width = lists:max([100, ?WIN_WIDTH - 40]) div 4, 257 Height = lists:max([100, ?WIN_HEIGHT - 100]), 258 ListCtrl = wxListCtrl:new(Panel, 259 [{style, 260 ?wxLC_REPORT bor 261 %% ?wxLC_SORT_ASCENDING bor 262 ?wxLC_SINGLE_SEL bor 263 ?wxHSCROLL bor 264 ?wxVSCROLL}, 265 {size, {Width, Height}} 266 ]), 267 268 %% Prep images 269 reltool_utils:assign_image_list(ListCtrl), 270 271 %% Prep column label 272 ListItem = wxListItem:new(), 273 wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), 274 wxListItem:setText(ListItem, Text), 275 wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), 276 wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), 277 wxListItem:destroy(ListItem), 278 279 wxSizer:add(Sizer, ListCtrl, 280 [{border, 2}, 281 {flag, ?wxALL bor ?wxEXPAND}, 282 {proportion, 1}]), 283 wxEvtHandler:connect(ListCtrl, size, 284 [{skip, true}, {userData, apps_list_ctrl}]), 285 wxListCtrl:connect(ListCtrl, command_list_item_activated, 286 [{userData, open_app}]), 287 wxWindow:connect(ListCtrl, enter_window), 288 ListCtrl. 289 290create_deps_page(S, Derived) -> 291 Panel = wxPanel:new(S#state.book, []), 292 Main = wxBoxSizer:new(?wxHORIZONTAL), 293 294 UsedByCtrl = create_mods_list_ctrl(Panel, 295 Main, 296 "Modules using this", 297 " and their applications", 298 undefined, 299 undefined), 300 wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 301 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 302 UsesCtrl = create_mods_list_ctrl(Panel, 303 Main, 304 "Used modules", 305 " and their applications", 306 undefined, 307 undefined), 308 S2 = S#state{deps_used_by_ctrl = UsedByCtrl, 309 deps_uses_ctrl = UsesCtrl}, 310 redraw_mods(S2, Derived), 311 wxPanel:setSizer(Panel, Main), 312 wxNotebook:addPage(S2#state.book, Panel, "Module dependencies", []), 313 S2. 314 315create_mods_page(S, Derived) -> 316 Panel = wxPanel:new(S#state.book, []), 317 MainSz = wxBoxSizer:new(?wxHORIZONTAL), 318 319 SourceCtrl = create_mods_list_ctrl(Panel, 320 MainSz, 321 ?source, 322 "", 323 whitelist_add, 324 blacklist_add), 325 wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 326 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 327 WhiteCtrl = create_mods_list_ctrl(Panel, 328 MainSz, 329 ?whitelist, 330 "", 331 whitelist_del, 332 blacklist_add), 333 wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 334 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 335 BlackCtrl = create_mods_list_ctrl(Panel, 336 MainSz, 337 ?blacklist, 338 "", 339 whitelist_add, 340 blacklist_del), 341 wxSizer:add(MainSz, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), 342 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 343 DerivedCtrl = create_mods_list_ctrl(Panel, 344 MainSz, 345 ?derived, 346 "", 347 whitelist_add, 348 blacklist_add), 349 S2 = S#state{mods_source_ctrl = SourceCtrl, 350 mods_white_ctrl = WhiteCtrl, 351 mods_black_ctrl = BlackCtrl, 352 mods_derived_ctrl = DerivedCtrl}, 353 redraw_mods(S2, Derived), 354 wxPanel:setSizer(Panel, MainSz), 355 wxNotebook:addPage(S2#state.book, Panel, "Modules", []), 356 S2. 357 358create_mods_list_ctrl(Panel, OuterSz, Title, AppText, Tick, Cross) -> 359 ListCtrl = wxListCtrl:new(Panel, 360 [{style, 361 ?wxLC_REPORT bor 362 %% ?wxLC_SORT_ASCENDING bor 363 %% ?wxLC_SINGLE_SEL bor 364 ?wxHSCROLL bor 365 ?wxVSCROLL}]), 366 ToolTip = "Select module(s) or open separate module " 367 "window with a double click.", 368 wxListCtrl:setToolTip(ListCtrl, ToolTip), 369 370 %% Prep images 371 reltool_utils:assign_image_list(ListCtrl), 372 373 %% Prep column label 374 ListItem = wxListItem:new(), 375 wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), 376 wxListItem:setText(ListItem, Title), 377 wxListCtrl:insertColumn(ListCtrl, ?MODS_MOD_COL, ListItem), 378 %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, ?MODS_MOD_COL_WIDTH), 379 Prop = 380 case AppText =/= "" of 381 true -> 382 wxListItem:setText(ListItem, AppText), 383 wxListCtrl:insertColumn(ListCtrl, ?MODS_APP_COL, ListItem), 384 %% wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, 385 %% ?MODS_APP_COL_WIDTH), 386 2; 387 false -> 388 1 389 end, 390 wxListItem:destroy(ListItem), 391 392 ButtonSz = wxBoxSizer:new(?wxHORIZONTAL), 393 create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_TICK_MARK", Tick), 394 create_button(Panel, ButtonSz, ListCtrl, Title, "wxART_CROSS_MARK", Cross), 395 wxEvtHandler:connect(ListCtrl, size, 396 [{skip, true}, {userData, mods_list_ctrl}]), 397 wxListCtrl:connect(ListCtrl, command_list_item_activated, 398 [{userData, open_mod}]), 399 wxWindow:connect(ListCtrl, enter_window), 400 InnerSz = wxBoxSizer:new(?wxVERTICAL), 401 wxSizer:add(InnerSz, ListCtrl, 402 [{border, 2}, 403 {flag, ?wxALL bor ?wxEXPAND}, 404 {proportion, 1}]), 405 wxSizer:add(InnerSz, ButtonSz, 406 [{flag, ?wxALL bor ?wxEXPAND}]), 407 wxSizer:add(OuterSz, InnerSz, 408 [{flag, ?wxALL bor ?wxEXPAND}, 409 {proportion, Prop}]), 410 ListCtrl. 411 412create_button(_Panel, Sizer, _ListCtrl, _Title, _BitMapName, undefined) -> 413 wxSizer:addStretchSpacer(Sizer); 414create_button(Panel, Sizer, ListCtrl, Title, BitMapName, Action) -> 415 %% InnerSz = wxBoxSizer:new(?wxVERTICAL), 416 BitMap = wxArtProvider:getBitmap(BitMapName), 417 Button = wxBitmapButton:new(Panel, ?wxID_ANY, BitMap, []), 418 ToolTip = action_to_tool_tip(Title, Action), 419 wxBitmapButton:setToolTip(Button, ToolTip), 420 %% wxSizer:add(InnerSz, Button, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 421 Opts = [{userData, {mod_button, Action, ListCtrl}}], 422 wxEvtHandler:connect(Button, command_button_clicked, Opts), 423 wxSizer:add(Sizer, Button, 424 [{border, 2}, 425 {flag, ?wxALL bor ?wxALIGN_CENTER_HORIZONTAL}, 426 {proportion, 1}]). 427 428action_to_tool_tip(Label, Action) -> 429 case Action of 430 whitelist_add when Label =:= ?whitelist -> 431 "Remove selected module(s) from whitelist."; 432 whitelist_add -> 433 "Add selected module(s) to whitelist."; 434 whitelist_del -> 435 "Remove selected module(s)from whitelist."; 436 blacklist_add when Label =:= ?blacklist -> 437 "Remove selected module(s) from blacklist."; 438 blacklist_add -> 439 "Add selected module(s) to blacklist."; 440 blacklist_del -> 441 "Remove selected module(s) from blacklist." 442 end. 443 444create_config_page(#state{app = App} = S) -> 445 Panel = wxPanel:new(S#state.book, []), 446 TopSizer = wxBoxSizer:new(?wxVERTICAL), 447 448 %% Source dirs 449 {LatestRadio, SelectedRadio, SourceBox} = 450 create_double_box(Panel, 451 TopSizer, 452 "Source selection policy", 453 "Use latest version", 454 use_latest_vsn, 455 "Use selected version", 456 use_selected_vsn, 457 "Directories", 458 App#app.sorted_dirs, 459 version), 460 461 InclSizer = wxBoxSizer:new(?wxHORIZONTAL), 462 463 %% Application inclusion 464 {AppGlobalRadio, AppLocalRadio, AppLocalBox} = 465 create_double_box(Panel, 466 InclSizer, 467 "Application inclusion policy", 468 "Use global config", 469 global_incl_cond, 470 "Use application specific config", 471 local_incl_cond, 472 "Application specific", 473 reltool_utils:incl_conds(), 474 incl_cond), 475 476 %% Module inclusion 477 {ModGlobalRadio, ModLocalRadio, ModLocalBox} = 478 create_double_box(Panel, 479 InclSizer, 480 "Module inclusion policy", 481 "Use global config", 482 global_mod_cond, 483 "Use application specific config", 484 local_mod_cond, 485 "Application specific", 486 reltool_utils:mod_conds(), 487 mod_cond), 488 wxSizer:add(TopSizer, InclSizer, 489 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), 490 491 S2 = S#state{config_app_global = AppGlobalRadio, 492 config_app_local = AppLocalRadio, 493 config_app_local_box = AppLocalBox, 494 config_mod_global = ModGlobalRadio, 495 config_mod_local = ModLocalRadio, 496 config_mod_local_box = ModLocalBox, 497 config_latest = LatestRadio, 498 config_selected = SelectedRadio, 499 config_source_box = SourceBox}, 500 redraw_config(S2), 501 wxPanel:setSizer(Panel, TopSizer), 502 wxNotebook:addPage(S2#state.book, Panel, "Application settings", []), 503 S2. 504 505create_double_box(Panel, Sizer, TopLabel, 506 OuterText, OuterData, 507 InnerText, InnerData, 508 InternalLabel, InternalChoices, InternalChoiceData) -> 509 TopSizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, 510 [{label, TopLabel}]), 511 OuterSizer = wxBoxSizer:new(?wxVERTICAL), 512 OuterRadio = wxRadioButton:new(Panel, ?wxID_ANY, OuterText, 513 [{style, ?wxRB_GROUP}]), 514 wxEvtHandler:connect(OuterRadio, command_radiobutton_selected, 515 [{userData, OuterData}]), 516 InnerRadio = wxRadioButton:new(Panel, ?wxID_ANY, InnerText), 517 wxEvtHandler:connect(InnerRadio, command_radiobutton_selected, 518 [{userData, InnerData}]), 519 InnerBox = wxRadioBox:new(Panel, 520 ?wxID_ANY, 521 InternalLabel, 522 ?wxDefaultPosition, 523 ?wxDefaultSize, 524 InternalChoices, 525 []), 526 wxEvtHandler:connect(InnerBox, command_radiobox_selected, 527 [{userData, InternalChoiceData}]), 528 wxSizer:add(OuterSizer, OuterRadio, 529 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 530 wxSizer:add(OuterSizer, InnerRadio, 531 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 532 wxSizer:add(TopSizer, OuterSizer, 533 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}]), 534 wxSizer:add(TopSizer, InnerBox, 535 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), 536 wxSizer:add(Sizer, TopSizer, 537 [{border, 2}, {flag, ?wxALL bor ?wxEXPAND}, {proportion, 1}]), 538 {OuterRadio, InnerRadio, InnerBox}. 539 540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 541 542handle_event(#state{sys = Sys, app = App} = S, Wx) -> 543 %% io:format("wx: ~p\n", [Wx]), 544 case Wx of 545 #wx{obj = ObjRef, event = #wxMouse{type = enter_window}} -> 546 wxWindow:setFocus(ObjRef), 547 S; 548 #wx{obj= ListCtrl, 549 userData = mods_list_ctrl, 550 event = #wxSize{type = size, size = {W, _H}}} -> 551 HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), 552 case HasApps of 553 false -> 554 wxListCtrl:setColumnWidth(ListCtrl, ?MODS_MOD_COL, W); 555 true -> 556 wxListCtrl:setColumnWidth(ListCtrl, 557 ?MODS_MOD_COL, 558 (2 * W) div 3), 559 wxListCtrl:setColumnWidth(ListCtrl, ?MODS_APP_COL, W div 3) 560 end, 561 S; 562 #wx{obj = ListCtrl, 563 userData = apps_list_ctrl, 564 event = #wxSize{type = size, size = {W, _H}}} -> 565 wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, W), 566 S; 567 #wx{userData = open_app, 568 obj = ListCtrl, 569 event = #wxList{type = command_list_item_activated, 570 itemIndex = Pos}} -> 571 AppBase = wxListCtrl:getItemText(ListCtrl, Pos), 572 {AppName, _AppVsn} = reltool_utils:split_app_name(AppBase), 573 {ok, _AppPid} = reltool_sys_win:open_app(S#state.parent_pid, 574 AppName), 575 S; 576 #wx{userData = open_mod, 577 obj = ListCtrl, 578 event = #wxList{type = command_list_item_activated, 579 itemIndex = Pos}} -> 580 ModName = list_to_atom(wxListCtrl:getItemText(ListCtrl, Pos)), 581 create_mod_window(S, ModName); 582 #wx{userData = global_incl_cond} -> 583 %% Use global setting 584 change_incl_cond(S, App, undefined); 585 #wx{userData = local_incl_cond} -> 586 %% Use app spec setting 587 change_incl_cond(S, App, Sys#sys.incl_cond); 588 #wx{userData = incl_cond, 589 %% Change app spec setting 590 event = #wxCommand{type = command_radiobox_selected, 591 cmdString = Sel}} -> 592 AppCond = reltool_utils:list_to_incl_cond(Sel), 593 change_incl_cond(S, App, AppCond); 594 595 #wx{userData = global_mod_cond} -> 596 %% Use global setting 597 change_mod_cond(S, App, undefined); 598 #wx{userData = local_mod_cond} -> 599 %% Use app spec setting 600 change_mod_cond(S, App, Sys#sys.mod_cond); 601 #wx{userData = mod_cond, 602 %% Change app spec setting 603 event = #wxCommand{type = command_radiobox_selected, 604 cmdString = Sel}} -> 605 ModCond = reltool_utils:list_to_mod_cond(Sel), 606 change_mod_cond(S, App, ModCond); 607 608 #wx{userData = use_latest_vsn} -> 609 %% Use latest version 610 App2 = App#app{use_selected_vsn = undefined}, 611 S2 = change_version(S, App2, App#app.active_dir), 612 redraw_window(S2); 613 #wx{userData = use_selected_vsn} -> 614 %% Use selected version 615 App2 = App#app{use_selected_vsn = dir}, 616 {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), 617 S2 = S#state{app = App3}, 618 redraw_window(S2); 619 #wx{userData = version, 620 event = #wxCommand{type = command_radiobox_selected, 621 cmdString = ActiveDir}} -> 622 %% Change app source 623 App2 = App#app{use_selected_vsn = dir}, 624 S2 = change_version(S, App2, ActiveDir), 625 redraw_window(S2); 626 #wx{userData = {mod_button, Action, ListCtrl}, 627 event = #wxCommand{type = command_button_clicked}} -> 628 Items = reltool_utils:get_items(ListCtrl), 629 handle_mod_button(S, Items, Action); 630 _ -> 631 error_logger:format("~w~w got unexpected app event from " 632 "wx:\n\t~tp\n", 633 [?MODULE, self(), Wx]), 634 S 635 end. 636 637create_mod_window(#state{parent_pid = RelPid, xref_pid = Xref, common = C} = S, 638 ModName) -> 639 case lists:keysearch(ModName, #mod_win.name, S#state.mod_wins) of 640 false -> 641 WxEnv = wx:get_env(), 642 {ok, Pid} = 643 reltool_mod_win:start_link(WxEnv, Xref, RelPid, C, ModName), 644 MW = #mod_win{name = ModName, pid = Pid}, 645 S#state{mod_wins = [MW | S#state.mod_wins]}; 646 {value, MW} -> 647 reltool_app_win:raise(MW#mod_win.pid), 648 S 649 end. 650 651handle_mod_button(#state{app = App} = S, Items, Action) -> 652 App2 = lists:foldl(fun(Item, A) -> move_mod(A, Item, Action) end, 653 App, 654 Items), 655 {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), 656 S2 = S#state{app = App3}, 657 redraw_window(S2). 658 659move_mod(App, {_ItemNo, ModStr}, Action) -> 660 ModName = list_to_atom(ModStr), 661 Mods = App#app.mods, 662 {value, M} = lists:keysearch(ModName, #mod.name, Mods), 663 AppCond = 664 case Action of 665 whitelist_add -> 666 case M#mod.incl_cond of 667 include -> undefined; 668 exclude -> include; 669 undefined -> include 670 end; 671 whitelist_del -> 672 undefined; 673 blacklist_add -> 674 exclude; 675 blacklist_del -> 676 undefined; 677 _ -> 678 error_logger:format("~w~w got unexpected mod " 679 "button event: ~w\n\t ~tp\n", 680 [?MODULE, self(), ModName, Action]), 681 M#mod.incl_cond 682 end, 683 M2 = M#mod{incl_cond = AppCond}, 684 Mods2 = lists:keystore(ModName, #mod.name, Mods, M2), 685 App#app{mods = Mods2}. 686 687change_incl_cond(S, App, NewAppCond) -> 688 App2 = App#app{incl_cond = NewAppCond}, 689 {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), 690 S2 = S#state{app = App3}, 691 redraw_window(S2). 692 693change_mod_cond(S, App, NewModCond) -> 694 App2 = App#app{mod_cond = NewModCond}, 695 {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), 696 S2 = S#state{app = App3}, 697 redraw_window(S2). 698 699change_version(S, App, NewDir) -> 700 App2 = App#app{active_dir = NewDir, 701 label = undefined, 702 vsn = undefined, 703 info = undefined}, 704 {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), 705 Title = app_title(App3), 706 wxFrame:setTitle(S#state.frame, Title), 707 S#state{app = App3}. 708 709redraw_apps(#state{app = #app{info = AppInfo}, 710 app_used_by_ctrl = UsedByCtrl, 711 app_required_ctrl = RequiredCtrl, 712 app_incl_ctrl = InclCtrl, 713 app_uses_ctrl = UsesCtrl, 714 xref_pid = Xref}, 715 {_SourceMods, 716 _WhiteMods, 717 _BlackMods, 718 _DerivedMods, 719 UsedByMods, 720 UsesMods}) -> 721 UsedByApps = 722 lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsedByMods]), 723 Select = 724 fun(AppName) -> 725 {ok, App} = reltool_server:get_app(Xref, AppName), 726 case App#app.status of 727 missing -> {AppName, ?ERR_IMAGE}; 728 ok -> {AppName, ?TICK_IMAGE} 729 end 730 end, 731 RequiredApps = lists:sort(lists:map(Select, AppInfo#app_info.applications)), 732 InclApps = lists:map(Select, AppInfo#app_info.incl_apps), 733 UsesApps = 734 lists:usort([{M#mod.app_name, Image} || {Image, _, M} <- UsesMods]), 735 do_redraw_apps(UsedByCtrl, UsedByApps), 736 do_redraw_apps(RequiredCtrl, RequiredApps), 737 do_redraw_apps(InclCtrl, InclApps), 738 do_redraw_apps(UsesCtrl, UsesApps), 739 ok. 740 741do_redraw_apps(ListCtrl, []) -> 742 wxListCtrl:deleteAllItems(ListCtrl); 743 %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, 744%% ?wxLIST_AUTOSIZE_USEHEADER); 745do_redraw_apps(ListCtrl, AppImages) -> 746 wxListCtrl:deleteAllItems(ListCtrl), 747 Add = 748 fun({AppName, ImageId}, {Row, Prev}) when AppName =/= Prev -> 749 wxListCtrl:insertItem(ListCtrl, Row, ""), 750 if (Row rem 2) =:= 0 -> 751 wxListCtrl:setItemBackgroundColour(ListCtrl, 752 Row, 753 {240,240,255}); 754 true -> 755 ignore 756 end, 757 Str = atom_to_list(AppName), 758 wxListCtrl:setItem(ListCtrl, 759 Row, 760 ?APPS_APP_COL, 761 Str, 762 [{imageId, ImageId}]), 763 {Row + 1, AppName}; 764 ({_, _}, Acc) -> 765 Acc 766 end, 767 wx:foldl(Add, {0, undefined}, AppImages). 768 769%% print(X, X, Format, Args) -> 770%% io:format(Format, Args); 771%% print(_, _, _, _) -> 772%% ok. 773 774redraw_mods(#state{mods_source_ctrl = SourceCtrl, 775 mods_white_ctrl = WhiteCtrl, 776 mods_black_ctrl = BlackCtrl, 777 mods_derived_ctrl = DerivedCtrl, 778 deps_used_by_ctrl = UsedByCtrl, 779 deps_uses_ctrl = UsesCtrl, 780 app = #app{is_pre_included = IsPre, is_included = IsIncl}, 781 status_bar = Bar}, 782 {SourceMods, 783 WhiteMods, 784 BlackMods, 785 DerivedMods, 786 UsedByMods, 787 UsesMods}) -> 788 InclStatus = 789 case IsIncl of 790 true when IsPre =:= true -> "Whitelist - "; 791 true -> "Derived - "; 792 false -> "Blacklist - "; 793 undefined -> "Source - " 794 end, 795 Status = lists:concat([InclStatus, 796 length(WhiteMods), " whitelisted modules and ", 797 length(DerivedMods), " derived modules."]), 798 wxStatusBar:setStatusText(Bar, Status), 799 opt_redraw_mods(SourceCtrl, SourceMods), 800 opt_redraw_mods(WhiteCtrl, WhiteMods), 801 opt_redraw_mods(BlackCtrl, BlackMods), 802 opt_redraw_mods(DerivedCtrl, DerivedMods), 803 opt_redraw_mods(UsedByCtrl, UsedByMods), 804 opt_redraw_mods(UsesCtrl, UsesMods). 805 806app_to_mods(#state{xref_pid = Xref, app = App}) -> 807 SourceMods = [M || M <- App#app.mods, 808 M#mod.is_included =/= true, 809 M#mod.is_pre_included =/= false], 810 WhiteMods = [M || M <- App#app.mods, 811 M#mod.is_pre_included =:= true], 812 BlackMods = [M || M <- App#app.mods, 813 M#mod.is_pre_included =:= false], 814 DerivedMods = [M || M <- App#app.mods, 815 M#mod.is_included =:= true, 816 M#mod.is_pre_included =/= true], 817 GetMod = 818 fun(ModName) when is_atom(ModName) -> 819 {ok, M} = reltool_server:get_mod(Xref, ModName), 820 if 821 M#mod.app_name =:= App#app.name, 822 M#mod.is_included =:= true -> 823 false; 824 true -> 825 {true, M} 826 end 827 end, 828 UsedByMods = lists:zf(GetMod, App#app.used_by_mods), 829 UsesMods = lists:zf(GetMod, App#app.uses_mods), 830 { 831 [select_image(source, M) || M <- SourceMods], 832 [select_image(whitelist, M) || M <- WhiteMods], 833 [select_image(blacklist, M) || M <- BlackMods], 834 [select_image(derived, M) || M <- DerivedMods], 835 [select_image(used_by, M) || M <- UsedByMods], 836 [select_image(uses, M) || M <- UsesMods] 837 }. 838 839select_image(Kind, M) -> 840 Image = 841 case Kind of 842 blacklist when M#mod.status =:= missing -> 843 ?WARN_IMAGE; 844 source when M#mod.status =:= missing -> 845 ?WARN_IMAGE; 846 _ when M#mod.status =:= missing -> 847 ?ERR_IMAGE; 848 blacklist when M#mod.incl_cond =:= exclude -> 849 ?CROSS_IMAGE; 850 blacklist -> 851 ?SOURCE_IMAGE; 852 source -> 853 ?CROSS_IMAGE; 854 whitelist when M#mod.incl_cond =:= include -> 855 ?TICK_IMAGE; 856 whitelist -> 857 ?SOURCE_IMAGE; 858 derived -> 859 ?TICK_IMAGE; 860 used_by when M#mod.is_included =:= true -> 861 ?TICK_IMAGE; 862 used_by when M#mod.is_included =:= false -> 863 ?WARN_IMAGE; 864 used_by -> 865 ?ERR_IMAGE; 866 uses when M#mod.is_included =:= true -> 867 ?TICK_IMAGE; 868 uses when M#mod.is_included =:= false -> 869 ?WARN_IMAGE; 870 uses -> 871 ?ERR_IMAGE 872 end, 873 {Image, M#mod.app_name, M}. 874 875opt_redraw_mods(undefined, _ImageMods) -> 876 ok; 877opt_redraw_mods(ListCtrl, ImageMods) -> 878 HasApps = (wxListCtrl:getColumnCount(ListCtrl) > 1), 879 do_redraw_mods(ListCtrl, ImageMods, HasApps). 880 881do_redraw_mods(ListCtrl, [], _HasApps) -> 882 wxListCtrl:deleteAllItems(ListCtrl); 883do_redraw_mods(ListCtrl, ImageMods, HasApps) -> 884 wxListCtrl:deleteAllItems(ListCtrl), 885 Add = 886 fun({ImageId, AppName, #mod{name = ModName}}, Row) -> 887 wxListCtrl:insertItem(ListCtrl, Row, ""), 888 if (Row rem 2) =:= 0 -> 889 wxListCtrl:setItemBackgroundColour(ListCtrl, 890 Row, 891 {240,240,255}); 892 true -> 893 ignore 894 end, 895 wxListCtrl:setItem(ListCtrl, 896 Row, 897 ?MODS_MOD_COL, 898 atom_to_list(ModName), 899 [{imageId, ImageId}]), 900 case HasApps of 901 false -> 902 ok; 903 true -> 904 wxListCtrl:setItem(ListCtrl, 905 Row, 906 ?MODS_APP_COL, 907 atom_to_list(AppName), 908 [{imageId, ImageId}]) 909 end, 910 Row + 1 911 end, 912 wx:foldl(Add, 0, lists:sort(ImageMods)). 913 914redraw_config(#state{sys = #sys{incl_cond = GlobalIncl, 915 mod_cond = GlobalSource}, 916 app = #app{incl_cond = LocalIncl, 917 mod_cond = LocalSource, 918 use_selected_vsn = UseSelected, 919 active_dir = ActiveDir, 920 sorted_dirs = SortedDirs}, 921 config_app_global = AppGlobalRadio, 922 config_app_local = AppLocalRadio, 923 config_app_local_box = AppLocalBox, 924 config_mod_global = ModGlobalRadio, 925 config_mod_local = ModLocalRadio, 926 config_mod_local_box = ModLocalBox, 927 config_latest = LatestRadio, 928 config_selected = SelectedRadio, 929 config_source_box = SourceBox}) -> 930 redraw_double_box(GlobalIncl, 931 LocalIncl, 932 AppGlobalRadio, 933 AppLocalRadio, 934 AppLocalBox, 935 fun reltool_utils:incl_cond_to_index/1), 936 redraw_double_box(GlobalSource, 937 LocalSource, 938 ModGlobalRadio, 939 ModLocalRadio, 940 ModLocalBox, 941 fun reltool_utils:mod_cond_to_index/1), 942 redraw_double_box(false, 943 UseSelected, 944 LatestRadio, 945 SelectedRadio, 946 SourceBox, 947 fun(false) -> 948 0; 949 (_) -> 950 reltool_utils:elem_to_index(ActiveDir, 951 SortedDirs) - 1 952 end). 953 954redraw_double_box(Global, Local, GlobalRadio, LocalRadio, LocalBox, GetChoice) -> 955 AppCond = 956 case Local of 957 undefined -> 958 wxRadioButton:setValue(GlobalRadio, true), 959 wxRadioButton:setValue(LocalRadio, false), 960 wxRadioBox:disable(LocalBox), 961 Global; 962 _ -> 963 wxRadioButton:setValue(GlobalRadio, false), 964 wxRadioButton:setValue(LocalRadio, true), 965 wxRadioBox:enable(LocalBox), 966 Local 967 end, 968 Choice = GetChoice(AppCond), 969 wxRadioBox:setSelection(LocalBox, Choice). 970 971redraw_window(S) -> 972 %% wx_misc:beginBusyCursor(), 973 Derived = app_to_mods(S), 974 redraw_config(S), 975 redraw_mods(S, Derived), 976 redraw_apps(S, Derived), 977 %% wx_misc:endBusyCursor(), 978 S. 979 980%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 981%% sys callbacks 982 983system_continue(_Parent, _Debug, S) -> 984 ?MODULE:loop(S). 985 986system_terminate(Reason, _Parent, _Debug, _S) -> 987 exit(Reason). 988 989system_code_change(S,_Module,_OldVsn,_Extra) -> 990 {ok, S}. 991