1%% -*- erlang-indent-level: 2 -*- 2%% 3%% Licensed under the Apache License, Version 2.0 (the "License"); 4%% you may not use this file except in compliance with the License. 5%% You may obtain a copy of the License at 6%% 7%% http://www.apache.org/licenses/LICENSE-2.0 8%% 9%% Unless required by applicable law or agreed to in writing, software 10%% distributed under the License is distributed on an "AS IS" BASIS, 11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12%% See the License for the specific language governing permissions and 13%% limitations under the License. 14 15%%%----------------------------------------------------------------------- 16%%% File : dialyzer_gui_wx.erl 17%%% Authors : Elli Fragkaki <ellifrag@gmail.com> 18%%% Description : The wx-based graphical user interface of dialyzer. 19%%% 20%%% Created : 07 Oct 2009 by Elli Fragkaki <ellifrag@gmail.com> 21%%%----------------------------------------------------------------------- 22 23-module(dialyzer_gui_wx). 24 25-export([start/1]). 26 27-include("dialyzer.hrl"). 28-include("dialyzer_gui_wx.hrl"). 29 30%%------------------------------------------------------------------------ 31 32-define(DIALYZER_ERROR_TITLE, "Dialyzer Error"). 33-define(DIALYZER_MESSAGE_TITLE, "Dialyzer Message"). 34 35%%------------------------------------------------------------------------ 36 37-record(menu, {file :: wx:wx_object(), 38 warnings :: wx:wx_object(), 39 plt :: wx:wx_object(), 40 options :: wx:wx_object(), 41 help :: wx:wx_object()}). 42-type menu() :: #menu{}. 43 44-record(gui_state, {add :: wx:wx_object(), 45 add_dir :: wx:wx_object(), 46 add_rec :: wx:wx_object(), 47 chosen_box :: wx:wx_object(), 48 del_file :: wx:wx_object(), 49 doc_plt :: dialyzer_plt:plt(), 50 clear_chosen :: wx:wx_object(), 51 clear_log :: wx:wx_object(), 52 explain_warn :: wx:wx_object(), 53 clear_warn :: wx:wx_object(), 54 init_plt :: dialyzer_plt:plt(), 55 dir_entry :: wx:wx_object(), 56 file_box :: wx:wx_object(), 57 files_to_analyze :: ordsets:ordset(string()), 58 gui :: wx:wx_object(), 59 log :: wx:wx_object(), 60 menu :: menu(), 61 mode :: wx:wx_object(), 62 options :: #options{}, 63 run :: wx:wx_object(), 64 stop :: wx:wx_object(), 65 frame :: wx:wx_object(), 66 warnings_box :: wx:wx_object(), 67 explanation_box :: wx:wx_object() | 'undefined', 68 wantedWarnings :: list(), 69 rawWarnings :: list(), 70 backend_pid :: pid() | 'undefined', 71 expl_pid :: pid() | 'undefined'}). 72 73%%------------------------------------------------------------------------ 74 75-spec start(#options{}) -> ?RET_NOTHING_SUSPICIOUS. 76 77start(DialyzerOptions) -> 78 process_flag(trap_exit, true), 79 Wx = wx:new(), 80 State = wx:batch(fun() -> create_window(Wx, DialyzerOptions) end), 81 gui_loop(State). 82 83create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) -> 84 {ok, Host} = inet:gethostname(), 85 86 %%---------- initializing frame --------- 87 Frame = wxFrame:new(Wx, -1, "Dialyzer " ++ ?VSN ++ " @ " ++ Host), 88 wxFrame:connect(Frame, close_window), 89 FileMenu = createFileMenu(), 90 WarningsMenu = createWarningsMenu(), 91 PltMenu = createPltMenu(), 92 OptionsMenu = createOptionsMenu(), 93 HelpMenu = createHelpMenu(), 94 95 MenuBar = wxMenuBar:new(), 96 wxMenuBar:append(MenuBar, FileMenu, "File"), 97 wxMenuBar:append(MenuBar, WarningsMenu, "Warnings"), 98 wxMenuBar:append(MenuBar, PltMenu, "Plt"), 99 wxMenuBar:append(MenuBar, OptionsMenu, "Options"), 100 wxMenuBar:append(MenuBar, HelpMenu, "Help"), 101 wxFrame:setMenuBar(Frame, MenuBar), 102 ok = wxFrame:connect(Frame, command_menu_selected), 103 104 %%----------- Set Labels ------------- 105 Lab1 = wxStaticText:new(Frame, ?LABEL1, "Directories or modules to analyze"), 106 OptionsLabel = wxStaticText:new(Frame, ?LABEL2, "Analysis Options"), 107 LogLabel = wxStaticText:new(Frame, ?LABEL3, "Log"), 108 FileLabel = wxStaticText:new(Frame, ?LABEL4, "File: "), 109 DirLabel = wxStaticText:new(Frame, ?LABEL5, "Dir: "), 110 WarningsLabel = wxStaticText:new(Frame, ?LABEL6, "Warnings"), 111 112 %%---------- Set TextBoxes ----------- 113 ChosenBox = wxListBox:new(Frame, ?ChosenBox, 114 [{size, {250,200}}, 115 {style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL 116 bor ?wxLB_NEEDED_SB}]), 117 LogBox = wxTextCtrl:new(Frame, ?LogBox, 118 [{size, {530,200}}, 119 {style, ?wxTE_MULTILINE 120 bor ?wxTE_READONLY bor ?wxHSCROLL}]), 121 DefaultPath = code:root_dir(), 122 123 FilePicker = wxFilePickerCtrl:new(Frame, ?FilePicker, 124 [{path, DefaultPath}, 125 {message, "Choose File to Analyse"}, 126 {style,?wxFLP_FILE_MUST_EXIST bor ?wxFLP_USE_TEXTCTRL}]), 127 wxPickerBase:setTextCtrlProportion(FilePicker,3), 128 wxPickerBase:setPickerCtrlProportion(FilePicker,2), 129 DirPicker = wxDirPickerCtrl:new(Frame, ?DirPicker, 130 [{path, DefaultPath}, 131 {message, "Choose Directory to Analyze"}, 132 {style,?wxDIRP_DIR_MUST_EXIST bor ?wxDIRP_USE_TEXTCTRL}]), 133 WarningsBox = wxListBox:new(Frame, ?WarningsBox, 134 [{size, {700,200}}, 135 {style, ?wxLB_HSCROLL 136 bor ?wxLB_NEEDED_SB}]), 137 138 %%--------- Set Buttons -------------- 139 DeleteButton = wxButton:new(Frame, ?Del_Button, [{label, "Delete"}]), 140 DeleteAllButton = wxButton:new(Frame, ?DelAll_Button, [{label, "Delete All"}]), 141 FileType = wxRadioBox:new(Frame, ?RADIOBOX, " File Type: " , {1,1}, {150,90}, 142 [["BeamFiles"],["SourceFiles"]]), 143 ClearLogButton = wxButton:new(Frame, ?ClearLog_Button, [{label, "Clear Log"}]), 144 AddButton = wxButton:new(Frame, ?Add_Button, [{label, "Add"}]), 145 AddDirButton = wxButton:new(Frame, ?AddDir_Button, [{label, "Add Dir"}]), 146 AddRecButton = wxButton:new(Frame, ?AddRec_Button, [{label, "Add Recursively"}]), 147 ExplainWarnButton = wxButton:new(Frame, ?ExplWarn_Button, [{label, "Explain Warning"}]), 148 ClearWarningsButton = wxButton:new(Frame, ?ClearWarn_Button, [{label, "Clear Warnings"}]), 149 RunButton = wxButton:new(Frame, ?Run_Button, [{label, "Run"}]), 150 StopButton = wxButton:new(Frame, ?Stop_Button, [{label, "Stop"}]), 151 wxWindow:disable(StopButton), 152 %%--------- Connect Buttons ----------- 153 wxButton:connect(DeleteButton, command_button_clicked), 154 wxButton:connect(DeleteAllButton, command_button_clicked), 155 wxButton:connect(ClearLogButton, command_button_clicked), 156 wxButton:connect(AddButton, command_button_clicked), 157 wxButton:connect(AddDirButton, command_button_clicked), 158 wxButton:connect(AddRecButton, command_button_clicked), 159 wxButton:connect(ExplainWarnButton, command_button_clicked), 160 wxButton:connect(ClearWarningsButton, command_button_clicked), 161 wxButton:connect(RunButton, command_button_clicked), 162 wxButton:connect(StopButton, command_button_clicked), 163 164 %%------------Set Layout ------------ 165 All = wxBoxSizer:new(?wxVERTICAL), 166 Top = wxBoxSizer:new(?wxHORIZONTAL), 167 Left = wxBoxSizer:new(?wxVERTICAL), 168 Right = wxBoxSizer:new(?wxVERTICAL), 169 RightUp = wxBoxSizer:new(?wxHORIZONTAL), 170 171 Opts = [{flag,?wxEXPAND bor ?wxALL}, {proportion,1}, {border, 1}], 172 Opts3 = [{flag,?wxEXPAND bor ?wxALL}, {proportion,3}, {border, 1}], 173 Center = [{flag, ?wxALIGN_CENTER_HORIZONTAL}], 174 175 ChooseItem = wxBoxSizer:new(?wxVERTICAL), 176 FileTypeItem = wxBoxSizer:new(?wxVERTICAL), 177 LogItem = wxBoxSizer:new(?wxVERTICAL), 178 FileDirItem = wxBoxSizer:new(?wxVERTICAL), 179 FileItem = wxBoxSizer:new(?wxHORIZONTAL), 180 DirItem = wxBoxSizer:new(?wxHORIZONTAL), 181 AddDirButtons = wxBoxSizer:new(?wxHORIZONTAL), 182 WarningsItem = wxBoxSizer:new(?wxVERTICAL), 183 ChooseButtons = wxBoxSizer:new(?wxHORIZONTAL), 184 WarnButtons = wxBoxSizer:new(?wxHORIZONTAL), 185 RunButtons = wxBoxSizer:new(?wxHORIZONTAL), 186 Buttons = wxFlexGridSizer:new(3), 187 188 _ = wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt), 189 _ = wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt), 190 _ = wxSizer:add(ChooseItem, Lab1, Center), 191 _ = wxSizer:add(ChooseItem, ChosenBox, Opts), 192 _ = wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt), 193 _ = wxSizer:add(FileTypeItem, OptionsLabel), 194 _ = wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]), 195 _ = wxSizer:add(LogItem, LogLabel, Center), 196 _ = wxSizer:add(LogItem, LogBox, Opts3), 197 _ = wxSizer:add(LogItem, ClearLogButton, ?BorderOpt), 198 _ = wxSizer:add(FileItem, FileLabel), 199 _ = wxSizer:add(FileItem, FilePicker), 200 _ = wxSizer:add(DirItem, DirLabel), 201 _ = wxSizer:add(DirItem, DirPicker), 202 _ = wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt), 203 _ = wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt), 204 _ = wxSizer:add(FileDirItem, FileItem), 205 _ = wxSizer:add(FileDirItem, AddButton, ?BorderOpt), 206 _ = wxSizer:add(FileDirItem, DirItem, ?BorderOpt), 207 _ = wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt), 208 _ = wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt), 209 _ = wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt), 210 _ = wxSizer:add(RunButtons, RunButton, ?BorderOpt), 211 _ = wxSizer:add(RunButtons, StopButton, ?BorderOpt), 212 _ = wxSizer:add(Buttons, WarnButtons), 213 _ = wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), 214 [{flag, ?wxEXPAND}]), 215 _ = wxSizer:add(Buttons, RunButtons), 216 _ = wxFlexGridSizer:addGrowableCol(Buttons, 1), 217 _ = wxSizer:add(WarningsItem, WarningsLabel, Center), 218 _ = wxSizer:add(WarningsItem, WarningsBox, Opts3), 219 _ = wxSizer:add(WarningsItem, Buttons, 220 [{flag, ?wxEXPAND bor ?wxALL}, ?Border]), 221 _ = wxSizer:add(Left, ChooseItem, Opts), 222 _ = wxSizer:add(Left, FileDirItem, 223 [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]), 224 _ = wxSizer:add(RightUp, FileTypeItem, ?BorderOpt), 225 _ = wxSizer:add(RightUp, LogItem, Opts3), 226 _ = wxSizer:add(Right, RightUp, Opts3), 227 _ = wxSizer:add(Right, WarningsItem, Opts3), 228 _ = wxSizer:add(Top, Left, Opts), 229 _ = wxSizer:add(Top, Right, Opts3), 230 231 _ = wxSizer:add(All, Top, Opts), 232 wxWindow:setSizer(Frame, All), 233 wxWindow:setSizeHints(Frame, {1150,600}), 234 wxWindow:show(Frame), 235 236 Warnings = [{?WARN_RETURN_NO_RETURN, ?menuID_WARN_NO_RETURN_FUN}, 237 {?WARN_RETURN_ONLY_EXIT, ?menuID_WARN_ERROR_HANDLING_FUN}, 238 {?WARN_NOT_CALLED, ?menuID_WARN_UNUSED_FUN}, 239 {?WARN_NON_PROPER_LIST, ?menuID_WARN_LIST_CONSTR}, 240 {?WARN_FUN_APP, ?menuID_WARN_BAD_FUN}, 241 {?WARN_MATCHING, ?menuID_WARN_MATCH_FAILURES}, 242 {?WARN_OPAQUE, ?menuID_WARN_OPAQUE}, 243 {?WARN_FAILING_CALL, ?menuID_WARN_FAIL_FUN_CALLS}, 244 {?WARN_CALLGRAPH, ?menuID_WARN_UNEXPORTED_FUN}, 245 {?WARN_RACE_CONDITION, ?menuID_WARN_RACE_CONDITIONS}, 246 %% For contracts. 247 {?WARN_CONTRACT_TYPES,?menuID_WARN_WRONG_CONTRACTS}, 248 {?WARN_CONTRACT_SYNTAX, ?menuID_WARN_CONTRACT_SYNTAX} 249 ], 250 Menu = #menu{file = FileMenu, 251 warnings = WarningsMenu, 252 plt = PltMenu, 253 options =OptionsMenu, 254 help = HelpMenu}, 255 256 InitPlt = 257 case InitPltFiles of 258 [] -> dialyzer_plt:new(); 259 _ -> 260 Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles], 261 dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts) 262 end, 263 264 #gui_state{add = AddButton, 265 add_dir = AddDirButton, 266 add_rec = AddRecButton, 267 chosen_box = ChosenBox, 268 clear_chosen = DeleteAllButton, 269 clear_log = ClearLogButton, 270 explain_warn = ExplainWarnButton, 271 clear_warn = ClearWarningsButton, 272 del_file = DeleteButton, 273 doc_plt = dialyzer_plt:new(), 274 dir_entry = DirPicker, 275 file_box = FilePicker, 276 files_to_analyze = ordsets:new(), 277 gui = Wx, 278 init_plt = InitPlt, 279 log = LogBox, 280 menu = Menu, 281 mode = FileType, 282 options = DialyzerOptions, 283 run = RunButton, 284 stop = StopButton, 285 frame = Frame, 286 warnings_box = WarningsBox, 287 wantedWarnings = Warnings, 288 rawWarnings = []}. 289 290createFileMenu() -> 291 FileMenu = wxMenu:new(), 292 _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS}, 293 {text, "Save &Warnings"}])), 294 _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG}, 295 {text, "Save &Log"}])), 296 _ = wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT}, 297 {text, "E&xit\tAlt-X"}])), 298 FileMenu. 299 300createWarningsMenu() -> 301 WarningsMenu = wxMenu:new(), 302 addCheckedItem(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, "Match failures"), 303 addCheckedItem(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, 304 "Failing function calls"), 305 addCheckedItem(WarningsMenu, ?menuID_WARN_BAD_FUN, "Bad fun applications"), 306 addCheckedItem(WarningsMenu, ?menuID_WARN_OPAQUE, "Opacity violations"), 307 addCheckedItem(WarningsMenu, ?menuID_WARN_LIST_CONSTR, 308 "Improper list constructions"), 309 addCheckedItem(WarningsMenu, ?menuID_WARN_UNUSED_FUN, "Unused functions"), 310 _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_ERROR_HANDLING_FUN, 311 "Error handling functions"), 312 addCheckedItem(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, 313 "Functions of no return"), 314 addCheckedItem(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, 315 "Call to unexported function"), 316 _ = wxMenu:appendCheckItem(WarningsMenu, ?menuID_WARN_RACE_CONDITIONS, 317 "Possible race conditions"), 318 addCheckedItem(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, "Wrong contracts"), 319 addCheckedItem(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, 320 "Wrong contract syntax"), 321 WarningsMenu. 322 323addCheckedItem(Menu, ItemId, Str) -> 324 _ = wxMenu:appendCheckItem(Menu, ItemId, Str), 325 wxMenu:check(Menu, ItemId, true). 326 327createPltMenu() -> 328 PltMenu = wxMenu:new(), 329 _ = wxMenu:appendCheckItem(PltMenu, ?menuID_PLT_INIT_EMPTY, 330 "Init with empty PLT"), 331 _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS}, 332 {text, "Show contents"}])), 333 _ = wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS}, 334 {text, "Search contents"}])), 335 PltMenu. 336 337createOptionsMenu() -> 338 OptsMenu = wxMenu:new(), 339 _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO}, 340 {text, "Manage Macro Definitions"}])), 341 _ = wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR}, 342 {text, "Manage Include Directories"}])), 343 OptsMenu. 344 345createHelpMenu() -> 346 HelpMenu = wxMenu:new(), 347 _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL}, 348 {text, "Manual"}])), 349 _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS}, 350 {text, "Warning Options"}])), 351 _ = wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT}, 352 {text, "About"}])), 353 HelpMenu. 354 355%% ---------------------------------------------------------------- 356%% 357%% Main GUI Loop 358%% 359 360-spec gui_loop(#gui_state{}) -> ?RET_NOTHING_SUSPICIOUS. 361 362gui_loop(#gui_state{backend_pid = BackendPid, doc_plt = DocPlt, 363 log = Log, frame = Frame, 364 warnings_box = WarningsBox} = State) -> 365 receive 366 #wx{event = #wxClose{}} -> 367 %% io:format("~p Closing window ~n", [self()]), 368 ok = wxFrame:setStatusText(Frame, "Closing...",[]), 369 wxWindow:destroy(Frame), 370 ?RET_NOTHING_SUSPICIOUS; 371 %% ----- Menu ----- 372 #wx{id = ?menuID_FILE_SAVE_LOG, obj = Frame, 373 event = #wxCommand{type = command_menu_selected}} -> 374 save_file(State, log), 375 gui_loop(State); 376 #wx{id=?menuID_FILE_SAVE_WARNINGS, obj=Frame, 377 event=#wxCommand{type=command_menu_selected}} -> 378 save_file(State, warnings), 379 gui_loop(State); 380 #wx{id=?menuID_FILE_QUIT, obj=Frame, 381 event=#wxCommand{type=command_menu_selected}} -> 382 case maybe_quit(State) of 383 true -> ?RET_NOTHING_SUSPICIOUS; 384 false -> gui_loop(State) 385 end; 386 #wx{id=?menuID_PLT_SHOW_CONTENTS, obj=Frame, 387 event=#wxCommand{type=command_menu_selected}} -> 388 show_doc_plt(State), 389 gui_loop(State); 390 #wx{id=?menuID_PLT_SEARCH_CONTENTS, obj=Frame, 391 event=#wxCommand{type=command_menu_selected}} -> 392 case dialyzer_plt:get_specs(DocPlt) of 393 "" -> error_sms(State, "No analysis has been made yet!\n"); 394 _ -> search_doc_plt(State) 395 end, 396 gui_loop(State); 397 #wx{id=?menuID_OPTIONS_INCLUDE_DIR, obj=Frame, 398 event=#wxCommand{type=command_menu_selected}} -> 399 NewOptions = include_dialog(State), 400 NewState = State#gui_state{options = NewOptions}, 401 gui_loop(NewState); 402 #wx{id=?menuID_OPTIONS_MACRO, obj=Frame, 403 event=#wxCommand{type=command_menu_selected}} -> 404 NewOptions = macro_dialog(State), 405 NewState = State#gui_state{options = NewOptions}, 406 gui_loop(NewState); 407 #wx{id=?menuID_HELP_MANUAL, obj=Frame, 408 event=#wxCommand{type=command_menu_selected}} -> 409 handle_help(State, "Dialyzer Manual", "manual.txt"), 410 gui_loop(State); 411 #wx{id=?menuID_HELP_WARNING_OPTIONS, obj=Frame, 412 event=#wxCommand{type=command_menu_selected}} -> 413 handle_help(State, "Dialyzer Warnings", "warnings.txt"), 414 gui_loop(State); 415 #wx{id=?menuID_HELP_ABOUT, obj=Frame, 416 event=#wxCommand{type=command_menu_selected}} -> 417 Message = " This is DIALYZER version " ++ ?VSN ++ " \n"++ 418 "DIALYZER is a DIscrepancy AnaLYZer for ERlang programs.\n\n"++ 419 " Copyright (C) Tobias Lindahl <tobiasl@it.uu.se>\n"++ 420 " Kostis Sagonas <kostis@it.uu.se>\n\n", 421 output_sms(State, "About Dialyzer", Message, info), 422 gui_loop(State); 423 %% ------ Buttons --------- 424 #wx{id=?Add_Button, 425 event=#wxCommand{type=command_button_clicked}} -> 426 State1 = handle_add_files(State), 427 gui_loop(State1); 428 #wx{id=?AddDir_Button, 429 event=#wxCommand{type=command_button_clicked}} -> 430 State1 = handle_add_dir(State), 431 gui_loop(State1); 432 #wx{id=?AddRec_Button, 433 event=#wxCommand{type=command_button_clicked}} -> 434 State1 = handle_add_rec(State), 435 gui_loop(State1); 436 #wx{id=?Del_Button, 437 event=#wxCommand{type=command_button_clicked}} -> 438 State1 = handle_file_delete(State), 439 gui_loop(State1); 440 #wx{id=?DelAll_Button, 441 event=#wxCommand{type=command_button_clicked}} -> 442 State1 = handle_file_delete_all(State), 443 gui_loop(State1); 444 #wx{id=?ClearLog_Button, 445 event=#wxCommand{type=command_button_clicked}} -> 446 wxTextCtrl:clear(State#gui_state.log), 447 gui_loop(State); 448 #wx{id=?ExplWarn_Button, 449 event=#wxCommand{type=command_button_clicked}} -> 450 handle_explanation(State), 451 gui_loop(State); 452 #wx{id=?ClearWarn_Button, 453 event=#wxCommand{type=command_button_clicked}} -> 454 wxListBox:clear(WarningsBox), 455 NewState = State#gui_state{rawWarnings = []}, 456 gui_loop(NewState); 457 #wx{id=?Run_Button, 458 event=#wxCommand{type=command_button_clicked}} -> 459 NewState = start_analysis(State), 460 gui_loop(NewState); 461 #wx{id=?Stop_Button, 462 event=#wxCommand{type=command_button_clicked}} -> 463 BackendPid ! {self(), stop}, 464 config_gui_stop(State), 465 update_editor(Log, "\n***** Analysis stopped ****\n"), 466 gui_loop(State); 467 %% ----- Analysis ----- 468 {BackendPid, ext_calls, ExtCalls} -> 469 Msg = io_lib:format("The following functions are called " 470 "but type information about them is not available.\n" 471 "The analysis might get more precise by including " 472 "the modules containing these functions:\n\n\t~tp\n", 473 [ExtCalls]), 474 free_editor(State,"Analysis Done", Msg), 475 gui_loop(State); 476 {BackendPid, ext_types, ExtTypes} -> 477 Map = fun({M,F,A}) -> io_lib:format("~tp:~tp/~p",[M,F,A]) end, 478 ExtTypeString = lists:join("\n", lists:map(Map, ExtTypes)), 479 Msg = io_lib:format("The following remote types are being used " 480 "but information about them is not available.\n" 481 "The analysis might get more precise by including " 482 "the modules containing these types and making sure " 483 "that they are exported:\n~ts\n", [ExtTypeString]), 484 free_editor(State, "Analysis done", Msg), 485 gui_loop(State); 486 {BackendPid, log, LogMsg} -> 487 update_editor(Log, LogMsg), 488 gui_loop(State); 489 {BackendPid, warnings, Warns} -> 490 SortedWarns = lists:keysort(2, Warns), %% Sort on file/line 491 NewState = add_warnings(State, SortedWarns), 492 gui_loop(NewState); 493 {BackendPid, cserver, CServer, Plt} -> 494 Self = self(), 495 Fun = 496 fun() -> 497 dialyzer_explanation:expl_loop(Self, CServer, Plt) 498 end, 499 ExplanationPid = spawn_link(Fun), 500 gui_loop(State#gui_state{expl_pid = ExplanationPid}); 501 {BackendPid, done, _NewPlt, NewDocPlt} -> 502 message(State, "Analysis done"), 503 config_gui_stop(State), 504 dialyzer_plt:delete(State#gui_state.doc_plt), 505 gui_loop(State#gui_state{doc_plt = NewDocPlt}); 506 {'EXIT', BackendPid, {error, Reason}} -> 507 free_editor(State, ?DIALYZER_ERROR_TITLE, Reason), 508 config_gui_stop(State), 509 gui_loop(State); 510 {'EXIT', BackendPid, Reason} when Reason =/= 'normal' -> 511 free_editor(State, ?DIALYZER_ERROR_TITLE, io_lib:format("~tp", [Reason])), 512 config_gui_stop(State), 513 gui_loop(State) 514 end. 515 516maybe_quit(#gui_state{frame = Frame} = State) -> 517 case dialog(State, "Do you really want to quit?", ?DIALYZER_MESSAGE_TITLE) of 518 true -> 519 wxWindow:destroy(Frame), 520 true; 521 false -> 522 false 523 end. 524 525%% ------------ Yes/No Question ------------ 526dialog(#gui_state{frame = Frame}, Message, Title) -> 527 MessageWin = wxMessageDialog:new(Frame, Message, [{caption, Title},{style, ?wxYES_NO bor ?wxICON_QUESTION bor ?wxNO_DEFAULT}]), 528 case wxDialog:showModal(MessageWin) of 529 ?wxID_YES -> 530 true; 531 ?wxID_NO -> 532 false; 533 ?wxID_CANCEL -> 534 false 535 end. 536 537search_doc_plt(#gui_state{gui = Wx} = State) -> 538 Dialog = wxFrame:new(Wx, ?SearchPltDialog, "Search the PLT",[{size,{400,100}},{style, ?wxSTAY_ON_TOP}]), 539 Size = {size,{120,30}}, 540 ModLabel = wxStaticText:new(Dialog, ?ModLabel, "Module"), 541 ModText = wxTextCtrl:new(Dialog, ?ModText,[Size]), 542 FunLabel = wxStaticText:new(Dialog, ?FunLabel, "Function"), 543 FunText = wxTextCtrl:new(Dialog, ?FunText,[Size]), 544 ArLabel = wxStaticText:new(Dialog, ?ArLabel, "Arity"), 545 ArText = wxTextCtrl:new(Dialog, ?ArText,[Size]), 546 SearchButton = wxButton:new(Dialog, ?SearchButton, [{label, "Search"}]), 547 wxButton:connect(SearchButton, command_button_clicked), 548 Cancel = wxButton:new(Dialog, ?Search_Cancel, [{label, "Cancel"}]), 549 wxButton:connect(Cancel, command_button_clicked), 550 551 Layout = wxBoxSizer:new(?wxVERTICAL), 552 Top = wxBoxSizer:new(?wxHORIZONTAL), 553 ModLayout = wxBoxSizer:new(?wxVERTICAL), 554 FunLayout = wxBoxSizer:new(?wxVERTICAL), 555 ArLayout = wxBoxSizer:new(?wxVERTICAL), 556 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 557 558 _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt), 559 _ = wxSizer:add(ModLayout, ModText, ?BorderOpt), 560 _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt), 561 _ = wxSizer:add(FunLayout,FunText, ?BorderOpt), 562 _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt), 563 _ = wxSizer:add(ArLayout,ArText, ?BorderOpt), 564 _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt), 565 _ = wxSizer:add(Buttons,Cancel, ?BorderOpt), 566 567 _ = wxSizer:add(Top, ModLayout), 568 _ = wxSizer:add(Top, FunLayout), 569 _ = wxSizer:add(Top, ArLayout), 570 _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]), 571 _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]), 572 wxFrame:connect(Dialog, close_window), 573 wxWindow:setSizer(Dialog, Layout), 574 wxFrame:show(Dialog), 575 search_plt_loop(State, Dialog, ModText, FunText, ArText, SearchButton, Cancel). 576 577search_plt_loop(State= #gui_state{doc_plt = DocPlt, frame = Frame}, Win, ModText, FunText, ArText, Search, Cancel) -> 578 receive 579 #wx{id = ?Search_Cancel, 580 event = #wxCommand{type = command_button_clicked}} -> 581 wxWindow:destroy(Win); 582 #wx{id = ?SearchPltDialog, event = #wxClose{type = close_window}} -> 583 wxWindow:destroy(Win); 584 #wx{event = #wxClose{type = close_window}} -> 585 wxWindow:destroy(Win), 586 wxWindow:destroy(Frame); 587 #wx{id = ?SearchButton, 588 event = #wxCommand{type = command_button_clicked}} -> 589 M = format_search(wxTextCtrl:getValue(ModText)), 590 F = format_search(wxTextCtrl:getValue(FunText)), 591 A = format_search(wxTextCtrl:getValue(ArText)), 592 593 if 594 (M =:= '_') orelse (F =:= '_') orelse (A =:= '_') -> 595 error_sms(State, "Please give:\n Module (atom)\n Function (atom)\n Arity (integer)\n"), 596 search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel); 597 true -> 598 case dialyzer_plt:get_specs(DocPlt, M, F, A) of 599 none -> 600 error_sms(State, "No such function"), 601 search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel); 602 NonEmptyString -> 603 wxWindow:destroy(Win), 604 free_editor(State, "Content of PLT", NonEmptyString) 605 end 606 end 607 end. 608 609format_search([]) -> 610 '_'; 611format_search(String) -> 612 try list_to_integer(String) 613 catch error:_ -> list_to_atom(String) 614 end. 615 616show_doc_plt(#gui_state{doc_plt = DocPLT} = State) -> 617 case dialyzer_plt:get_specs(DocPLT) of 618 "" -> error_sms(State, "No analysis has been made yet!\n"); 619 NonEmptyString -> free_editor(State, "Content of PLT", NonEmptyString) 620 end. 621 622message(State, Message) -> 623 output_sms(State, ?DIALYZER_MESSAGE_TITLE, Message, info). 624 625error_sms(State, Message) -> 626 output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error). 627 628output_sms(#gui_state{frame = Frame}, Title, Message, Type) -> 629 Style = case Type of 630 error -> ?wxOK bor ?wxICON_ERROR; 631 info -> ?wxOK bor ?wxICON_INFORMATION 632 end, 633 Options = [{caption, Title}, {style, Style}], 634 MessageWin = wxMessageDialog:new(Frame, Message, Options), 635 wxWindow:setSizeHints(MessageWin, {350,100}), 636 wxDialog:showModal(MessageWin), 637 ok. 638 639free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) -> 640 Contents = lists:flatten(Contents0), 641 Tokens = string:lexemes(Contents, "\n"), 642 NofLines = length(Tokens), 643 LongestLine = lists:max([length(X) || X <- Tokens]), 644 Height0 = NofLines * 25 + 80, 645 Height = if Height0 > 500 -> 500; true -> Height0 end, 646 Width0 = LongestLine * 7 + 60, 647 Width = if Width0 > 800 -> 800; true -> Width0 end, 648 Size = {size,{Width, Height}}, 649 Win = wxFrame:new(Wx, ?Message, Title, [{size,{Width+4, Height+50}}]), 650 651 Editor = wxTextCtrl:new(Win, ?Message_Info, 652 [Size, 653 {style, ?wxTE_MULTILINE 654 bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]), 655 wxTextCtrl:appendText(Editor, Contents), 656 wxFrame:connect(Win, close_window), 657 Ok = wxButton:new(Win, ?Message_Ok, [{label, "OK"}]), 658 wxButton:connect(Ok, command_button_clicked), 659 Layout = wxBoxSizer:new(?wxVERTICAL), 660 661 _ = wxSizer:add(Layout, Editor, ?BorderOpt), 662 Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL, 663 _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]), 664 wxWindow:setSizer(Win, Layout), 665 wxWindow:show(Win), 666 show_info_loop(Frame, Win). 667 668show_info_loop(Frame, Win) -> 669 receive 670 #wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} -> 671 wxWindow:destroy(Win); 672 #wx{id = ?Message, event = #wxClose{type = close_window}} -> 673 wxWindow:destroy(Win); 674 #wx{event = #wxClose{type = close_window}} -> 675 wxWindow:destroy(Frame) 676 end. 677 678handle_add_files(#gui_state{chosen_box = ChosenBox, file_box = FileBox, 679 files_to_analyze = FileList, 680 mode = Mode} = State) -> 681 case wxFilePickerCtrl:getPath(FileBox) of 682 "" -> 683 State; 684 File -> 685 NewFile = ordsets:new(), 686 NewFile1 = ordsets:add_element(File,NewFile), 687 Ext = 688 case wxRadioBox:getSelection(Mode) of 689 0 -> ".beam"; 690 1-> ".erl" 691 end, 692 State#gui_state{files_to_analyze = add_files(filter_mods(NewFile1, Ext), FileList, ChosenBox, Ext)} 693 end. 694 695handle_add_dir(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, 696 files_to_analyze = FileList, mode = Mode} = State) -> 697 case wxDirPickerCtrl:getPath(DirBox) of 698 "" -> 699 State; 700 Dir -> 701 NewDir = ordsets:new(), 702 NewDir1 = ordsets:add_element(Dir,NewDir), 703 Ext = case wxRadioBox:getSelection(Mode) of 704 0 -> ".beam"; 705 1-> ".erl" 706 end, 707 State#gui_state{files_to_analyze = add_files(filter_mods(NewDir1,Ext), FileList, ChosenBox, Ext)} 708 end. 709 710handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, 711 files_to_analyze = FileList, mode = Mode} = State) -> 712 case wxDirPickerCtrl:getPath(DirBox) of 713 "" -> 714 State; 715 Dir -> 716 NewDir = ordsets:new(), 717 NewDir1 = ordsets:add_element(Dir,NewDir), 718 TargetDirs = ordsets:union(NewDir1, all_subdirs(NewDir1)), 719 Ext = case wxRadioBox:getSelection(Mode) of 720 0 -> ".beam"; 721 1 -> ".erl" 722 end, 723 State#gui_state{files_to_analyze = add_files(filter_mods(TargetDirs, Ext), FileList, ChosenBox, Ext)} 724 end. 725 726handle_file_delete(#gui_state{chosen_box = ChosenBox, 727 files_to_analyze = FileList} = State) -> 728 {_, List} = wxListBox:getSelections(ChosenBox), 729 Set = ordsets:from_list([wxControlWithItems:getString(ChosenBox, X) || X <- List]), 730 FileList1 = ordsets:subtract(FileList,Set), 731 lists:foreach(fun (X) -> wxListBox:delete(ChosenBox, X) end, List), 732 State#gui_state{files_to_analyze = FileList1}. 733 734handle_file_delete_all(#gui_state{chosen_box = ChosenBox} = State) -> 735 wxListBox:clear(ChosenBox), 736 State#gui_state{files_to_analyze = ordsets:new()}. 737 738add_files(File, FileList, ChosenBox, Ext) -> 739 Set = filter_mods(FileList, Ext), 740 Files = ordsets:union(File, Set), 741 Files1 = ordsets:to_list(Files), 742 wxListBox:set(ChosenBox, Files1), 743 Files. 744 745filter_mods(Mods, Extension) -> 746 Fun = fun(X) -> 747 filename:extension(X) =:= Extension 748 orelse 749 (filelib:is_dir(X) andalso 750 contains_files(X, Extension)) 751 end, 752 ordsets:filter(Fun, Mods). 753 754contains_files(Dir, Extension) -> 755 {ok, Files} = file:list_dir(Dir), 756 lists:any(fun(X) -> filename:extension(X) =:= Extension end, Files). 757 758all_subdirs(Dirs) -> 759 all_subdirs(Dirs, []). 760 761all_subdirs([Dir|T], Acc) -> 762 {ok, Files} = file:list_dir(Dir), 763 SubDirs = lists:zf(fun(F) -> 764 SubDir = filename:join(Dir, F), 765 case filelib:is_dir(SubDir) of 766 true -> {true, SubDir}; 767 false -> false 768 end 769 end, Files), 770 NewAcc = ordsets:union(ordsets:from_list(SubDirs), Acc), 771 all_subdirs(T ++ SubDirs, NewAcc); 772all_subdirs([], Acc) -> 773 Acc. 774 775start_analysis(State) -> 776 Analysis = build_analysis_record(State), 777 case get_anal_files(State, Analysis#analysis.start_from) of 778 error -> 779 Msg = "You must choose one or more files or dirs\n" 780 "before starting the analysis!", 781 error_sms(State, Msg), 782 config_gui_stop(State), 783 State; 784 {ok, Files} -> 785 Msg = "\n========== Starting Analysis ==========\n\n", 786 update_editor(State#gui_state.log, Msg), 787 NewAnalysis = Analysis#analysis{files = Files}, 788 run_analysis(State, NewAnalysis) 789 end. 790 791build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, 792 init_plt = InitPlt0}) -> 793 StartFrom = 794 case wxRadioBox:getSelection(Mode) of 795 0 -> byte_code; 796 1 -> src_code 797 end, 798 InitPlt = 799 case wxMenu:isChecked(Menu#menu.plt, ?menuID_PLT_INIT_EMPTY) of 800 true -> dialyzer_plt:new(); 801 false -> InitPlt0 802 end, 803 #analysis{defines = Options#options.defines, 804 include_dirs = Options#options.include_dirs, 805 plt = InitPlt, 806 start_from = StartFrom, 807 solvers = Options#options.solvers}. 808 809get_anal_files(#gui_state{files_to_analyze = Files}, StartFrom) -> 810 FilteredMods = 811 case StartFrom of 812 src_code -> filter_mods(Files, ".erl"); 813 byte_code -> filter_mods(Files, ".beam") 814 end, 815 FilteredDirs = [X || X <- Files, filelib:is_dir(X)], 816 case ordsets:union(FilteredMods, FilteredDirs) of 817 [] -> error; 818 Set -> {ok, Set} 819 end. 820 821run_analysis(State, Analysis) -> 822 config_gui_start(State), 823 Self = self(), 824 NewAnalysis = Analysis#analysis{doc_plt = dialyzer_plt:new()}, 825 LegalWarnings = find_legal_warnings(State), 826 Fun = 827 fun() -> 828 dialyzer_analysis_callgraph:start(Self, LegalWarnings, NewAnalysis) 829 end, 830 BackendPid = spawn_link(Fun), 831 State#gui_state{backend_pid = BackendPid}. 832 833find_legal_warnings(#gui_state{menu = #menu{warnings = MenuWarnings}, 834 wantedWarnings = Warnings }) -> 835 ordsets:from_list([Tag || {Tag, MenuItem} <- Warnings, 836 wxMenu:isChecked(MenuWarnings, MenuItem)]). 837 838update_editor(Editor, Msg) -> 839 wxTextCtrl:appendText(Editor,Msg). 840 841config_gui_stop(State) -> 842 wxWindow:disable(State#gui_state.stop), 843 wxWindow:enable(State#gui_state.run), 844 wxWindow:enable(State#gui_state.del_file), 845 wxWindow:enable(State#gui_state.clear_chosen), 846 wxWindow:enable(State#gui_state.add), 847 wxWindow:enable(State#gui_state.add_dir), 848 wxWindow:enable(State#gui_state.add_rec), 849 wxWindow:enable(State#gui_state.clear_warn), 850 wxWindow:enable(State#gui_state.clear_log), 851 Menu = State#gui_state.menu, 852 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS,true), 853 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG,true), 854 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO,true), 855 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR,true), 856 wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY,true), 857 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS,true), 858 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS,true), 859 wxRadioBox:enable(State#gui_state.mode). 860 861config_gui_start(State) -> 862 wxWindow:enable(State#gui_state.stop), 863 wxWindow:disable(State#gui_state.run), 864 wxWindow:disable(State#gui_state.del_file), 865 wxWindow:disable(State#gui_state.clear_chosen), 866 wxWindow:disable(State#gui_state.add), 867 wxWindow:disable(State#gui_state.add_dir), 868 wxWindow:disable(State#gui_state.add_rec), 869 wxWindow:disable(State#gui_state.clear_warn), 870 wxWindow:disable(State#gui_state.clear_log), 871 Menu = State#gui_state.menu, 872 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS, false), 873 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG, false), 874 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO, false), 875 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR, false), 876 wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY, false), 877 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS, false), 878 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS, false), 879 wxRadioBox:disable(State#gui_state.mode). 880 881save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Type) -> 882 {Message, Box} = case Type of 883 warnings -> {"Save Warnings", WBox}; 884 log -> {"Save Log", Log} 885 end, 886 case wxTextCtrl:getValue(Box) of 887 "" -> error_sms(State,"There is nothing to save...\n"); 888 _ -> 889 DefaultPath = code:root_dir(), 890 FileDialog = wxFileDialog:new(Frame, 891 [{defaultDir, DefaultPath}, 892 {message, Message}, 893 {style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), 894 case wxFileDialog:showModal(FileDialog) of 895 ?wxID_OK -> 896 Path = wxFileDialog:getPath(FileDialog), 897 case wxTextCtrl:saveFile(Box,[{file,Path}]) of 898 true -> ok; 899 false -> error_sms(State, "Could not write to file:\n" ++ Path) 900 end; 901 ?wxID_CANCEL -> wxWindow:destroy(FileDialog); 902 _ -> error_sms(State, "Could not write to file:\n") 903 end 904 end. 905 906include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> 907 Size = {size,{300,480}}, 908 Dialog = wxFrame:new(Wx, ?IncludeDir, "Include Directories",[Size]), 909 DirLabel = wxStaticText:new(Dialog, ?InclLabel, "Directory: "), 910 DefaultPath = code:root_dir(), 911 DirPicker = wxDirPickerCtrl:new(Dialog, ?InclPicker, 912 [{path, DefaultPath}, 913 {message, "Choose Directory to Include"}, 914 {style,?wxDIRP_DIR_MUST_EXIST bor ?wxDIRP_USE_TEXTCTRL}]), 915 Box = wxListBox:new(Dialog, ?InclBox, 916 [{size, {200,300}}, 917 {style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL 918 bor ?wxLB_NEEDED_SB}]), 919 AddButton = wxButton:new(Dialog, ?InclAdd, [{label, "Add"}]), 920 DeleteButton = wxButton:new(Dialog, ?InclDel, [{label, "Delete"}]), 921 DeleteAllButton = wxButton:new(Dialog, ?InclDelAll, [{label, "Delete All"}]), 922 Ok = wxButton:new(Dialog, ?InclOk, [{label, "OK"}]), 923 Cancel = wxButton:new(Dialog, ?InclCancel, [{label, "Cancel"}]), 924 wxButton:connect(AddButton, command_button_clicked), 925 wxButton:connect(DeleteButton, command_button_clicked), 926 wxButton:connect(DeleteAllButton, command_button_clicked), 927 wxButton:connect(Ok, command_button_clicked), 928 wxButton:connect(Cancel, command_button_clicked), 929 Dirs = [io_lib:format("~ts", [X]) || X <- Options#options.include_dirs], 930 wxListBox:set(Box, Dirs), 931 Layout = wxBoxSizer:new(?wxVERTICAL), 932 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 933 Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), 934 935 _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 936 _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 937 _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 938 _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 939 _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), 940 _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), 941 _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 942 _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), 943 _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt), 944 _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), 945 946 wxFrame:connect(Dialog, close_window), 947 wxWindow:setSizer(Dialog, Layout), 948 wxFrame:show(Dialog), 949 include_loop(Options, Dialog, Box, DirPicker, Frame). 950 951include_loop(Options, Win, Box, DirPicker, Frame) -> 952 receive 953 #wx{id = ?InclCancel, 954 event = #wxCommand{type = command_button_clicked}} -> 955 wxWindow:destroy(Win), 956 Options; 957 #wx{id = ?IncludeDir, event = #wxClose{type = close_window}} -> 958 wxWindow:destroy(Win), 959 Options; 960 #wx{event = #wxClose{type = close_window}} -> 961 wxWindow:destroy(Win), 962 wxWindow:destroy(Frame); 963 #wx{id = ?InclOk, 964 event = #wxCommand{type = command_button_clicked}} -> 965 wxWindow:destroy(Win), 966 Options; 967 #wx{id = ?InclAdd, 968 event = #wxCommand{type = command_button_clicked}} -> 969 Dirs = Options#options.include_dirs, 970 NewDirs = 971 case wxDirPickerCtrl:getPath(DirPicker) of 972 "" -> Dirs; 973 Add -> [Add|Dirs] 974 end, 975 NewOptions = Options#options{include_dirs = NewDirs}, 976 wxListBox:set(Box, NewDirs), 977 include_loop(NewOptions, Win, Box, DirPicker, Frame); 978 #wx{id = ?InclDel, 979 event = #wxCommand{type = command_button_clicked}} -> 980 NewOptions = 981 case wxListBox:getSelections(Box) of 982 {0,_} -> Options; 983 {_,List} -> 984 DelList = [wxControlWithItems:getString(Box,X) || X <- List], 985 NewDirs = Options#options.include_dirs -- DelList, 986 lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List), 987 Options#options{include_dirs = NewDirs} 988 end, 989 include_loop(NewOptions, Win, Box, DirPicker, Frame); 990 #wx{id = ?InclDelAll, 991 event = #wxCommand{type = command_button_clicked}} -> 992 wxListBox:clear(Box), 993 NewOptions = Options#options{include_dirs = []}, 994 include_loop(NewOptions, Win, Box, DirPicker, Frame) 995 end. 996 997macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> 998 Size = {size,{300,480}}, 999 Size1 = {size,{120,30}}, 1000 Dialog = wxFrame:new(Wx, ?MacroDir, "Macro Definitions",[Size]), 1001 MacroLabel = wxStaticText:new(Dialog, ?MacroLabel, "Macro"), 1002 TermLabel = wxStaticText:new(Dialog, ?TermLabel, "Term"), 1003 MacroText = wxTextCtrl:new(Dialog, ?MacroText, [Size1]), 1004 TermText = wxTextCtrl:new(Dialog, ?TermText, [Size1]), 1005 Box = wxListBox:new(Dialog, ?MacroBox, 1006 [{size, {250,300}}, 1007 {style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL 1008 bor ?wxLB_NEEDED_SB}]), 1009 1010 AddButton = wxButton:new(Dialog, ?MacroAdd, [{label, "Add"}]), 1011 DeleteButton = wxButton:new(Dialog, ?MacroDel, [{label, "Delete"}]), 1012 DeleteAllButton = wxButton:new(Dialog, ?MacroDelAll, [{label, "Delete All"}]), 1013 Ok = wxButton:new(Dialog, ?MacroOk, [{label, "OK"}]), 1014 Cancel = wxButton:new(Dialog, ?MacroCancel, [{label, "Cancel"}]), 1015 wxButton:connect(AddButton, command_button_clicked), 1016 wxButton:connect(DeleteButton, command_button_clicked), 1017 wxButton:connect(DeleteAllButton, command_button_clicked), 1018 wxButton:connect(Ok, command_button_clicked), 1019 wxButton:connect(Cancel, command_button_clicked), 1020 1021 Macros = [io_lib:format("~p = ~p", [X, Y]) 1022 || {X,Y} <- Options#options.defines], 1023 1024 wxListBox:set(Box, Macros), 1025 Layout = wxBoxSizer:new(?wxVERTICAL), 1026 Item = wxBoxSizer:new(?wxHORIZONTAL), 1027 MacroItem = wxBoxSizer:new(?wxVERTICAL), 1028 TermItem = wxBoxSizer:new(?wxVERTICAL), 1029 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 1030 Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), 1031 1032 _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt), 1033 _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt), 1034 _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt), 1035 _ = wxSizer:add(TermItem, TermText, ?BorderOpt), 1036 _ = wxSizer:add(Item, MacroItem), 1037 _ = wxSizer:add(Item, TermItem), 1038 _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1039 _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1040 _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1041 _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), 1042 _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), 1043 _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1044 _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), 1045 _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt), 1046 _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), 1047 1048 wxFrame:connect(Dialog, close_window), 1049 wxWindow:setSizer(Dialog, Layout), 1050 wxFrame:show(Dialog), 1051 macro_loop(Options, Dialog, Box, MacroText, TermText, Frame). 1052 1053macro_loop(Options, Win, Box, MacroText, TermText, Frame) -> 1054 receive 1055 #wx{id = ?MacroCancel, 1056 event = #wxCommand{type = command_button_clicked}} -> 1057 wxWindow:destroy(Win), 1058 Options; 1059 #wx{id = ?MacroDir, event = #wxClose{type = close_window}} -> 1060 wxWindow:destroy(Win), 1061 Options; 1062 #wx{event = #wxClose{type = close_window}} -> 1063 wxWindow:destroy(Win), 1064 wxWindow:destroy(Frame); 1065 #wx{id = ?MacroOk, 1066 event = #wxCommand{type = command_button_clicked}} -> 1067 wxWindow:destroy(Win), 1068 Options; 1069 #wx{id = ?MacroAdd, 1070 event = #wxCommand{type = command_button_clicked}} -> 1071 Defines = Options#options.defines, 1072 NewDefines = 1073 case wxTextCtrl:getValue(MacroText) of 1074 "" -> Defines; 1075 Macro -> 1076 case wxTextCtrl:getValue(TermText) of 1077 "" -> 1078 orddict:store(list_to_atom(Macro), true, Defines); 1079 String -> 1080 orddict:store(list_to_atom(Macro), String, Defines) 1081 end 1082 end, 1083 NewOptions = Options#options{defines = NewDefines}, 1084 NewEntries = [io_lib:format("~p = ~p", [X, Y]) || {X, Y} <- NewDefines], 1085 wxListBox:set(Box, NewEntries), 1086 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame); 1087 #wx{id = ?MacroDel, 1088 event = #wxCommand{type = command_button_clicked}} -> 1089 NewOptions = 1090 case wxListBox:getSelections(Box) of 1091 {0, _} -> Options; 1092 {_, List} -> 1093 Fun = 1094 fun(X) -> 1095 Val = wxControlWithItems:getString(Box,X), 1096 [MacroName|_] = re:split(Val, " ", [{return, list}, unicode]), 1097 list_to_atom(MacroName) 1098 end, 1099 Delete = [Fun(X) || X <- List], 1100 lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List), 1101 Defines = Options#options.defines, 1102 NewDefines = lists:foldl(fun(X, Acc) -> 1103 orddict:erase(X, Acc) 1104 end, 1105 Defines, Delete), 1106 Options#options{defines = NewDefines} 1107 end, 1108 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame); 1109 #wx{id = ?MacroDelAll, 1110 event = #wxCommand{type = command_button_clicked}} -> 1111 wxListBox:clear(Box), 1112 NewOptions = Options#options{defines = []}, 1113 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame) 1114 end. 1115 1116handle_help(State, Title, Txt) -> 1117 FileName = filename:join([code:lib_dir(dialyzer), "doc", Txt]), 1118 case file:open(FileName, [read]) of 1119 {error, Reason} -> 1120 error_sms(State, 1121 io_lib:format("Could not find doc/~ts file!\n\n ~tp", 1122 [Txt, Reason])); 1123 {ok, _Handle} -> 1124 case file:read_file(FileName) of 1125 {error, Reason} -> 1126 error_sms(State, 1127 io_lib:format("Could not read doc/~ts file!\n\n ~tp", 1128 [Txt, Reason])); 1129 {ok, Binary} -> 1130 Contents = binary_to_list(Binary), 1131 free_editor(State, Title, Contents) 1132 end 1133 end. 1134 1135add_warnings(#gui_state{warnings_box = WarnBox, 1136 rawWarnings = RawWarns} = State, Warnings) -> 1137 NewRawWarns = RawWarns ++ Warnings, 1138 %% The indentation cannot be turned off. 1139 WarnList = [string:trim(dialyzer:format_warning(W), trailing) || 1140 W <- NewRawWarns], 1141 wxListBox:set(WarnBox, WarnList), 1142 State#gui_state{rawWarnings = NewRawWarns}. 1143 1144handle_explanation(#gui_state{rawWarnings = RawWarns, 1145 warnings_box = WarnBox, 1146 expl_pid = ExplPid} = State) -> 1147 case wxListBox:isEmpty(WarnBox) of 1148 true -> 1149 error_sms(State, "\nThere are no warnings.\nRun the dialyzer first."); 1150 false -> 1151 case wxListBox:getSelections(WarnBox)of 1152 {0, []} -> 1153 error_sms(State,"\nYou must choose a warning to be explained\n"); 1154 {_, [WarnNumber]} -> 1155 Warn = lists:nth(WarnNumber+1,RawWarns), 1156 Self = self(), 1157 ExplPid ! {Self, warning, Warn}, 1158 explanation_loop(State) 1159 end 1160 end. 1161 1162explanation_loop(#gui_state{expl_pid = ExplPid} = State) -> 1163 receive 1164 {ExplPid, explanation, Explanation} -> 1165 show_explanation(State, Explanation); 1166 _ -> io:format("Unknown message\n"), 1167 explanation_loop(State) 1168 end. 1169 1170show_explanation(#gui_state{gui = Wx} = State, Explanation) -> 1171 case Explanation of 1172 none -> 1173 output_sms(State, ?DIALYZER_MESSAGE_TITLE, 1174 "There is not any explanation for this error!\n", info); 1175 Expl -> 1176 ExplString = format_explanation(Expl), 1177 Size = {size,{700, 300}}, 1178 Win = wxFrame:new(Wx, ?ExplWin, "Dialyzer Explanation", [{size,{740, 350}}]), 1179 1180 Editor = wxTextCtrl:new(Win, ?ExplText, 1181 [Size, 1182 {style, ?wxTE_MULTILINE 1183 bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]), 1184 wxTextCtrl:appendText(Editor, ExplString), 1185 wxFrame:connect(Win, close_window), 1186 ExplButton = wxButton:new(Win, ?ExplButton, [{label, "Further Explain"}]), 1187 wxButton:connect(ExplButton, command_button_clicked), 1188 Ok = wxButton:new(Win, ?ExplOk, [{label, "OK"}]), 1189 wxButton:connect(Ok, command_button_clicked), 1190 Layout = wxBoxSizer:new(?wxVERTICAL), 1191 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 1192 _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt), 1193 _ = wxSizer:add(Buttons, Ok, ?BorderOpt), 1194 _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1195 _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1196 wxWindow:setSizer(Win, Layout), 1197 wxWindow:show(Win), 1198 NewState = State#gui_state{explanation_box = Editor}, 1199 show_explanation_loop(NewState, Win, Explanation) 1200 end. 1201 1202show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) -> 1203 receive 1204 {ExplPid, none, _} -> 1205 output_sms(State, ?DIALYZER_MESSAGE_TITLE, 1206 "There is not any other explanation for this error!\n", info), 1207 show_explanation_loop(State, Win, Explanation); 1208 {ExplPid, further, NewExplanation} -> 1209 update_explanation(State, NewExplanation), 1210 show_explanation_loop(State, Win, NewExplanation); 1211 #wx{id = ?ExplButton, event = #wxCommand{type = command_button_clicked}} -> 1212 ExplPid ! {self(), further, Explanation}, 1213 show_explanation_loop(State, Win, Explanation); 1214 #wx{id = ?ExplOk, event = #wxCommand{type = command_button_clicked}} -> 1215 wxWindow:destroy(Win); 1216 #wx{id = ?ExplWin, event = #wxClose{type = close_window}} -> 1217 wxWindow:destroy(Win); 1218 #wx{event = #wxClose{type = close_window}} -> 1219 wxWindow:destroy(Frame) 1220 end. 1221 1222update_explanation(#gui_state{explanation_box = Box}, Explanation) -> 1223 ExplString = format_explanation(Explanation), 1224 wxTextCtrl:appendText(Box, "\n --------------------------- \n"), 1225 wxTextCtrl:appendText(Box, ExplString). 1226 1227format_explanation({function_return, {M, F, A}, NewList}) -> 1228 io_lib:format("The function ~w:~tw/~w returns ~ts\n", 1229 [M, F, A, erl_types:t_to_string(NewList)]); 1230format_explanation(Explanation) -> 1231 io_lib:format("~p\n", [Explanation]). 1232