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 ExtCalls1 = lists:usort([MFA || {MFA, _FileLocation} <- ExtCalls]), 470 Map = fun({M,F,A}) -> io_lib:format("\t~tp:~tp/~p",[M,F,A]) end, 471 ExtCallString = lists:join("\n", lists:map(Map, ExtCalls1)), 472 Msg = io_lib:format("The following functions are called " 473 "but type information about them is not available.\n" 474 "The analysis might get more precise by including " 475 "the modules containing these functions:\n\n~ts\n", 476 [ExtCallString]), 477 free_editor(State,"Analysis Done", Msg), 478 gui_loop(State); 479 {BackendPid, ext_types, ExtTypes} -> 480 ExtTypes1 = lists:usort([MFA || {MFA, _FileLocation} <- ExtTypes]), 481 Map = fun({M,F,A}) -> io_lib:format("\t~tp:~tp/~p",[M,F,A]) end, 482 ExtTypeString = lists:join("\n", lists:map(Map, ExtTypes1)), 483 Msg = io_lib:format("The following remote types are being used " 484 "but information about them is not available.\n" 485 "The analysis might get more precise by including " 486 "the modules containing these types and making sure " 487 "that they are exported:\n\n~ts\n", 488 [ExtTypeString]), 489 free_editor(State, "Analysis done", Msg), 490 gui_loop(State); 491 {BackendPid, log, LogMsg} -> 492 update_editor(Log, LogMsg), 493 gui_loop(State); 494 {BackendPid, warnings, Warns} -> 495 SortedWarns = lists:keysort(2, Warns), %% Sort on file/location 496 NewState = add_warnings(State, SortedWarns), 497 gui_loop(NewState); 498 {BackendPid, cserver, CServer, Plt} -> 499 Self = self(), 500 Fun = 501 fun() -> 502 dialyzer_explanation:expl_loop(Self, CServer, Plt) 503 end, 504 ExplanationPid = spawn_link(Fun), 505 gui_loop(State#gui_state{expl_pid = ExplanationPid}); 506 {BackendPid, done, _NewPlt, NewDocPlt} -> 507 message(State, "Analysis done"), 508 config_gui_stop(State), 509 dialyzer_plt:delete(State#gui_state.doc_plt), 510 gui_loop(State#gui_state{doc_plt = NewDocPlt}); 511 {'EXIT', BackendPid, {error, Reason}} -> 512 free_editor(State, ?DIALYZER_ERROR_TITLE, Reason), 513 config_gui_stop(State), 514 gui_loop(State); 515 {'EXIT', BackendPid, Reason} when Reason =/= 'normal' -> 516 free_editor(State, ?DIALYZER_ERROR_TITLE, io_lib:format("~tp", [Reason])), 517 config_gui_stop(State), 518 gui_loop(State) 519 end. 520 521maybe_quit(#gui_state{frame = Frame} = State) -> 522 case dialog(State, "Do you really want to quit?", ?DIALYZER_MESSAGE_TITLE) of 523 true -> 524 wxWindow:destroy(Frame), 525 true; 526 false -> 527 false 528 end. 529 530%% ------------ Yes/No Question ------------ 531dialog(#gui_state{frame = Frame}, Message, Title) -> 532 MessageWin = wxMessageDialog:new(Frame, Message, [{caption, Title},{style, ?wxYES_NO bor ?wxICON_QUESTION bor ?wxNO_DEFAULT}]), 533 case wxDialog:showModal(MessageWin) of 534 ?wxID_YES -> 535 true; 536 ?wxID_NO -> 537 false; 538 ?wxID_CANCEL -> 539 false 540 end. 541 542search_doc_plt(#gui_state{gui = Wx} = State) -> 543 Dialog = wxFrame:new(Wx, ?SearchPltDialog, "Search the PLT",[{size,{400,100}},{style, ?wxSTAY_ON_TOP}]), 544 Size = {size,{120,30}}, 545 ModLabel = wxStaticText:new(Dialog, ?ModLabel, "Module"), 546 ModText = wxTextCtrl:new(Dialog, ?ModText,[Size]), 547 FunLabel = wxStaticText:new(Dialog, ?FunLabel, "Function"), 548 FunText = wxTextCtrl:new(Dialog, ?FunText,[Size]), 549 ArLabel = wxStaticText:new(Dialog, ?ArLabel, "Arity"), 550 ArText = wxTextCtrl:new(Dialog, ?ArText,[Size]), 551 SearchButton = wxButton:new(Dialog, ?SearchButton, [{label, "Search"}]), 552 wxButton:connect(SearchButton, command_button_clicked), 553 Cancel = wxButton:new(Dialog, ?Search_Cancel, [{label, "Cancel"}]), 554 wxButton:connect(Cancel, command_button_clicked), 555 556 Layout = wxBoxSizer:new(?wxVERTICAL), 557 Top = wxBoxSizer:new(?wxHORIZONTAL), 558 ModLayout = wxBoxSizer:new(?wxVERTICAL), 559 FunLayout = wxBoxSizer:new(?wxVERTICAL), 560 ArLayout = wxBoxSizer:new(?wxVERTICAL), 561 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 562 563 _ = wxSizer:add(ModLayout, ModLabel, ?BorderOpt), 564 _ = wxSizer:add(ModLayout, ModText, ?BorderOpt), 565 _ = wxSizer:add(FunLayout, FunLabel, ?BorderOpt), 566 _ = wxSizer:add(FunLayout,FunText, ?BorderOpt), 567 _ = wxSizer:add(ArLayout, ArLabel, ?BorderOpt), 568 _ = wxSizer:add(ArLayout,ArText, ?BorderOpt), 569 _ = wxSizer:add(Buttons, SearchButton, ?BorderOpt), 570 _ = wxSizer:add(Buttons,Cancel, ?BorderOpt), 571 572 _ = wxSizer:add(Top, ModLayout), 573 _ = wxSizer:add(Top, FunLayout), 574 _ = wxSizer:add(Top, ArLayout), 575 _ = wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]), 576 _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]), 577 wxFrame:connect(Dialog, close_window), 578 wxWindow:setSizer(Dialog, Layout), 579 wxFrame:show(Dialog), 580 search_plt_loop(State, Dialog, ModText, FunText, ArText, SearchButton, Cancel). 581 582search_plt_loop(State= #gui_state{doc_plt = DocPlt, frame = Frame}, Win, ModText, FunText, ArText, Search, Cancel) -> 583 receive 584 #wx{id = ?Search_Cancel, 585 event = #wxCommand{type = command_button_clicked}} -> 586 wxWindow:destroy(Win); 587 #wx{id = ?SearchPltDialog, event = #wxClose{type = close_window}} -> 588 wxWindow:destroy(Win); 589 #wx{event = #wxClose{type = close_window}} -> 590 wxWindow:destroy(Win), 591 wxWindow:destroy(Frame); 592 #wx{id = ?SearchButton, 593 event = #wxCommand{type = command_button_clicked}} -> 594 M = format_search(wxTextCtrl:getValue(ModText)), 595 F = format_search(wxTextCtrl:getValue(FunText)), 596 A = format_search(wxTextCtrl:getValue(ArText)), 597 598 if 599 (M =:= '_') orelse (F =:= '_') orelse (A =:= '_') -> 600 error_sms(State, "Please give:\n Module (atom)\n Function (atom)\n Arity (integer)\n"), 601 search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel); 602 true -> 603 case dialyzer_plt:get_specs(DocPlt, M, F, A) of 604 none -> 605 error_sms(State, "No such function"), 606 search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel); 607 NonEmptyString -> 608 wxWindow:destroy(Win), 609 free_editor(State, "Content of PLT", NonEmptyString) 610 end 611 end 612 end. 613 614format_search([]) -> 615 '_'; 616format_search(String) -> 617 try list_to_integer(String) 618 catch error:_ -> list_to_atom(String) 619 end. 620 621show_doc_plt(#gui_state{doc_plt = DocPLT} = State) -> 622 case dialyzer_plt:get_specs(DocPLT) of 623 "" -> error_sms(State, "No analysis has been made yet!\n"); 624 NonEmptyString -> free_editor(State, "Content of PLT", NonEmptyString) 625 end. 626 627message(State, Message) -> 628 output_sms(State, ?DIALYZER_MESSAGE_TITLE, Message, info). 629 630error_sms(State, Message) -> 631 output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error). 632 633output_sms(#gui_state{frame = Frame}, Title, Message, Type) -> 634 Style = case Type of 635 error -> ?wxOK bor ?wxICON_ERROR; 636 info -> ?wxOK bor ?wxICON_INFORMATION 637 end, 638 Options = [{caption, Title}, {style, Style}], 639 MessageWin = wxMessageDialog:new(Frame, Message, Options), 640 wxWindow:setSizeHints(MessageWin, {350,100}), 641 wxDialog:showModal(MessageWin), 642 ok. 643 644free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) -> 645 Contents = lists:flatten(Contents0), 646 Tokens = string:lexemes(Contents, "\n"), 647 NofLines = length(Tokens), 648 LongestLine = lists:max([length(X) || X <- Tokens]), 649 Height0 = NofLines * 25 + 80, 650 Height = if Height0 > 500 -> 500; true -> Height0 end, 651 Width0 = LongestLine * 7 + 60, 652 Width = if Width0 > 800 -> 800; true -> Width0 end, 653 Size = {size,{Width, Height}}, 654 Win = wxFrame:new(Wx, ?Message, Title, [{size,{Width+4, Height+50}}]), 655 656 Editor = wxTextCtrl:new(Win, ?Message_Info, 657 [Size, 658 {style, ?wxTE_MULTILINE 659 bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]), 660 wxTextCtrl:appendText(Editor, Contents), 661 wxFrame:connect(Win, close_window), 662 Ok = wxButton:new(Win, ?Message_Ok, [{label, "OK"}]), 663 wxButton:connect(Ok, command_button_clicked), 664 Layout = wxBoxSizer:new(?wxVERTICAL), 665 666 _ = wxSizer:add(Layout, Editor, ?BorderOpt), 667 Flag = ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL, 668 _ = wxSizer:add(Layout, Ok, [{flag, Flag}, ?Border]), 669 wxWindow:setSizer(Win, Layout), 670 wxWindow:show(Win), 671 show_info_loop(Frame, Win). 672 673show_info_loop(Frame, Win) -> 674 receive 675 #wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} -> 676 wxWindow:destroy(Win); 677 #wx{id = ?Message, event = #wxClose{type = close_window}} -> 678 wxWindow:destroy(Win); 679 #wx{event = #wxClose{type = close_window}} -> 680 wxWindow:destroy(Frame) 681 end. 682 683handle_add_files(#gui_state{chosen_box = ChosenBox, file_box = FileBox, 684 files_to_analyze = FileList, 685 mode = Mode} = State) -> 686 case wxFilePickerCtrl:getPath(FileBox) of 687 "" -> 688 State; 689 File -> 690 NewFile = ordsets:new(), 691 NewFile1 = ordsets:add_element(File,NewFile), 692 Ext = 693 case wxRadioBox:getSelection(Mode) of 694 0 -> ".beam"; 695 1-> ".erl" 696 end, 697 State#gui_state{files_to_analyze = add_files(filter_mods(NewFile1, Ext), FileList, ChosenBox, Ext)} 698 end. 699 700handle_add_dir(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, 701 files_to_analyze = FileList, mode = Mode} = State) -> 702 case wxDirPickerCtrl:getPath(DirBox) of 703 "" -> 704 State; 705 Dir -> 706 NewDir = ordsets:new(), 707 NewDir1 = ordsets:add_element(Dir,NewDir), 708 Ext = case wxRadioBox:getSelection(Mode) of 709 0 -> ".beam"; 710 1-> ".erl" 711 end, 712 State#gui_state{files_to_analyze = add_files(filter_mods(NewDir1,Ext), FileList, ChosenBox, Ext)} 713 end. 714 715handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, 716 files_to_analyze = FileList, mode = Mode} = State) -> 717 case wxDirPickerCtrl:getPath(DirBox) of 718 "" -> 719 State; 720 Dir -> 721 NewDir = ordsets:new(), 722 NewDir1 = ordsets:add_element(Dir,NewDir), 723 TargetDirs = ordsets:union(NewDir1, all_subdirs(NewDir1)), 724 Ext = case wxRadioBox:getSelection(Mode) of 725 0 -> ".beam"; 726 1 -> ".erl" 727 end, 728 State#gui_state{files_to_analyze = add_files(filter_mods(TargetDirs, Ext), FileList, ChosenBox, Ext)} 729 end. 730 731handle_file_delete(#gui_state{chosen_box = ChosenBox, 732 files_to_analyze = FileList} = State) -> 733 {_, List} = wxListBox:getSelections(ChosenBox), 734 Set = ordsets:from_list([wxControlWithItems:getString(ChosenBox, X) || X <- List]), 735 FileList1 = ordsets:subtract(FileList,Set), 736 lists:foreach(fun (X) -> wxListBox:delete(ChosenBox, X) end, List), 737 State#gui_state{files_to_analyze = FileList1}. 738 739handle_file_delete_all(#gui_state{chosen_box = ChosenBox} = State) -> 740 wxListBox:clear(ChosenBox), 741 State#gui_state{files_to_analyze = ordsets:new()}. 742 743add_files(File, FileList, ChosenBox, Ext) -> 744 Set = filter_mods(FileList, Ext), 745 Files = ordsets:union(File, Set), 746 Files1 = ordsets:to_list(Files), 747 wxListBox:set(ChosenBox, Files1), 748 Files. 749 750filter_mods(Mods, Extension) -> 751 Fun = fun(X) -> 752 filename:extension(X) =:= Extension 753 orelse 754 (filelib:is_dir(X) andalso 755 contains_files(X, Extension)) 756 end, 757 ordsets:filter(Fun, Mods). 758 759contains_files(Dir, Extension) -> 760 {ok, Files} = file:list_dir(Dir), 761 lists:any(fun(X) -> filename:extension(X) =:= Extension end, Files). 762 763all_subdirs(Dirs) -> 764 all_subdirs(Dirs, []). 765 766all_subdirs([Dir|T], Acc) -> 767 {ok, Files} = file:list_dir(Dir), 768 SubDirs = lists:zf(fun(F) -> 769 SubDir = filename:join(Dir, F), 770 case filelib:is_dir(SubDir) of 771 true -> {true, SubDir}; 772 false -> false 773 end 774 end, Files), 775 NewAcc = ordsets:union(ordsets:from_list(SubDirs), Acc), 776 all_subdirs(T ++ SubDirs, NewAcc); 777all_subdirs([], Acc) -> 778 Acc. 779 780start_analysis(State) -> 781 Analysis = build_analysis_record(State), 782 case get_anal_files(State, Analysis#analysis.start_from) of 783 error -> 784 Msg = "You must choose one or more files or dirs\n" 785 "before starting the analysis!", 786 error_sms(State, Msg), 787 config_gui_stop(State), 788 State; 789 {ok, Files} -> 790 Msg = "\n========== Starting Analysis ==========\n\n", 791 update_editor(State#gui_state.log, Msg), 792 NewAnalysis = Analysis#analysis{files = Files}, 793 run_analysis(State, NewAnalysis) 794 end. 795 796build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, 797 init_plt = InitPlt0}) -> 798 StartFrom = 799 case wxRadioBox:getSelection(Mode) of 800 0 -> byte_code; 801 1 -> src_code 802 end, 803 InitPlt = 804 case wxMenu:isChecked(Menu#menu.plt, ?menuID_PLT_INIT_EMPTY) of 805 true -> dialyzer_plt:new(); 806 false -> InitPlt0 807 end, 808 #analysis{defines = Options#options.defines, 809 include_dirs = Options#options.include_dirs, 810 plt = InitPlt, 811 start_from = StartFrom, 812 solvers = Options#options.solvers}. 813 814get_anal_files(#gui_state{files_to_analyze = Files}, StartFrom) -> 815 FilteredMods = 816 case StartFrom of 817 src_code -> filter_mods(Files, ".erl"); 818 byte_code -> filter_mods(Files, ".beam") 819 end, 820 FilteredDirs = [X || X <- Files, filelib:is_dir(X)], 821 case ordsets:union(FilteredMods, FilteredDirs) of 822 [] -> error; 823 Set -> {ok, Set} 824 end. 825 826run_analysis(State, Analysis) -> 827 config_gui_start(State), 828 Self = self(), 829 NewAnalysis = Analysis#analysis{doc_plt = dialyzer_plt:new()}, 830 LegalWarnings = find_legal_warnings(State), 831 Fun = 832 fun() -> 833 dialyzer_analysis_callgraph:start(Self, LegalWarnings, NewAnalysis) 834 end, 835 BackendPid = spawn_link(Fun), 836 State#gui_state{backend_pid = BackendPid}. 837 838find_legal_warnings(#gui_state{menu = #menu{warnings = MenuWarnings}, 839 wantedWarnings = Warnings }) -> 840 ordsets:from_list([Tag || {Tag, MenuItem} <- Warnings, 841 wxMenu:isChecked(MenuWarnings, MenuItem)]). 842 843update_editor(Editor, Msg) -> 844 wxTextCtrl:appendText(Editor,Msg). 845 846config_gui_stop(State) -> 847 wxWindow:disable(State#gui_state.stop), 848 wxWindow:enable(State#gui_state.run), 849 wxWindow:enable(State#gui_state.del_file), 850 wxWindow:enable(State#gui_state.clear_chosen), 851 wxWindow:enable(State#gui_state.add), 852 wxWindow:enable(State#gui_state.add_dir), 853 wxWindow:enable(State#gui_state.add_rec), 854 wxWindow:enable(State#gui_state.clear_warn), 855 wxWindow:enable(State#gui_state.clear_log), 856 Menu = State#gui_state.menu, 857 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS,true), 858 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG,true), 859 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO,true), 860 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR,true), 861 wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY,true), 862 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS,true), 863 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS,true), 864 wxRadioBox:enable(State#gui_state.mode). 865 866config_gui_start(State) -> 867 wxWindow:enable(State#gui_state.stop), 868 wxWindow:disable(State#gui_state.run), 869 wxWindow:disable(State#gui_state.del_file), 870 wxWindow:disable(State#gui_state.clear_chosen), 871 wxWindow:disable(State#gui_state.add), 872 wxWindow:disable(State#gui_state.add_dir), 873 wxWindow:disable(State#gui_state.add_rec), 874 wxWindow:disable(State#gui_state.clear_warn), 875 wxWindow:disable(State#gui_state.clear_log), 876 Menu = State#gui_state.menu, 877 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS, false), 878 wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG, false), 879 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO, false), 880 wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR, false), 881 wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY, false), 882 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS, false), 883 wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS, false), 884 wxRadioBox:disable(State#gui_state.mode). 885 886save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Type) -> 887 {Message, Box} = case Type of 888 warnings -> {"Save Warnings", WBox}; 889 log -> {"Save Log", Log} 890 end, 891 case wxTextCtrl:getValue(Box) of 892 "" -> error_sms(State,"There is nothing to save...\n"); 893 _ -> 894 DefaultPath = code:root_dir(), 895 FileDialog = wxFileDialog:new(Frame, 896 [{defaultDir, DefaultPath}, 897 {message, Message}, 898 {style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]), 899 case wxFileDialog:showModal(FileDialog) of 900 ?wxID_OK -> 901 Path = wxFileDialog:getPath(FileDialog), 902 case wxTextCtrl:saveFile(Box,[{file,Path}]) of 903 true -> ok; 904 false -> error_sms(State, "Could not write to file:\n" ++ Path) 905 end; 906 ?wxID_CANCEL -> wxWindow:destroy(FileDialog); 907 _ -> error_sms(State, "Could not write to file:\n") 908 end 909 end. 910 911include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> 912 Size = {size,{300,480}}, 913 Dialog = wxFrame:new(Wx, ?IncludeDir, "Include Directories",[Size]), 914 DirLabel = wxStaticText:new(Dialog, ?InclLabel, "Directory: "), 915 DefaultPath = code:root_dir(), 916 DirPicker = wxDirPickerCtrl:new(Dialog, ?InclPicker, 917 [{path, DefaultPath}, 918 {message, "Choose Directory to Include"}, 919 {style,?wxDIRP_DIR_MUST_EXIST bor ?wxDIRP_USE_TEXTCTRL}]), 920 Box = wxListBox:new(Dialog, ?InclBox, 921 [{size, {200,300}}, 922 {style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL 923 bor ?wxLB_NEEDED_SB}]), 924 AddButton = wxButton:new(Dialog, ?InclAdd, [{label, "Add"}]), 925 DeleteButton = wxButton:new(Dialog, ?InclDel, [{label, "Delete"}]), 926 DeleteAllButton = wxButton:new(Dialog, ?InclDelAll, [{label, "Delete All"}]), 927 Ok = wxButton:new(Dialog, ?InclOk, [{label, "OK"}]), 928 Cancel = wxButton:new(Dialog, ?InclCancel, [{label, "Cancel"}]), 929 wxButton:connect(AddButton, command_button_clicked), 930 wxButton:connect(DeleteButton, command_button_clicked), 931 wxButton:connect(DeleteAllButton, command_button_clicked), 932 wxButton:connect(Ok, command_button_clicked), 933 wxButton:connect(Cancel, command_button_clicked), 934 Dirs = [io_lib:format("~ts", [X]) || X <- Options#options.include_dirs], 935 wxListBox:set(Box, Dirs), 936 Layout = wxBoxSizer:new(?wxVERTICAL), 937 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 938 Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), 939 940 _ = wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 941 _ = wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 942 _ = wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 943 _ = wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 944 _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), 945 _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), 946 _ = wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 947 _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), 948 _ = wxSizer:add(Buttons1,Cancel, ?BorderOpt), 949 _ = wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), 950 951 wxFrame:connect(Dialog, close_window), 952 wxWindow:setSizer(Dialog, Layout), 953 wxFrame:show(Dialog), 954 include_loop(Options, Dialog, Box, DirPicker, Frame). 955 956include_loop(Options, Win, Box, DirPicker, Frame) -> 957 receive 958 #wx{id = ?InclCancel, 959 event = #wxCommand{type = command_button_clicked}} -> 960 wxWindow:destroy(Win), 961 Options; 962 #wx{id = ?IncludeDir, event = #wxClose{type = close_window}} -> 963 wxWindow:destroy(Win), 964 Options; 965 #wx{event = #wxClose{type = close_window}} -> 966 wxWindow:destroy(Win), 967 wxWindow:destroy(Frame); 968 #wx{id = ?InclOk, 969 event = #wxCommand{type = command_button_clicked}} -> 970 wxWindow:destroy(Win), 971 Options; 972 #wx{id = ?InclAdd, 973 event = #wxCommand{type = command_button_clicked}} -> 974 Dirs = Options#options.include_dirs, 975 NewDirs = 976 case wxDirPickerCtrl:getPath(DirPicker) of 977 "" -> Dirs; 978 Add -> [Add|Dirs] 979 end, 980 NewOptions = Options#options{include_dirs = NewDirs}, 981 wxListBox:set(Box, NewDirs), 982 include_loop(NewOptions, Win, Box, DirPicker, Frame); 983 #wx{id = ?InclDel, 984 event = #wxCommand{type = command_button_clicked}} -> 985 NewOptions = 986 case wxListBox:getSelections(Box) of 987 {0,_} -> Options; 988 {_,List} -> 989 DelList = [wxControlWithItems:getString(Box,X) || X <- List], 990 NewDirs = Options#options.include_dirs -- DelList, 991 lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List), 992 Options#options{include_dirs = NewDirs} 993 end, 994 include_loop(NewOptions, Win, Box, DirPicker, Frame); 995 #wx{id = ?InclDelAll, 996 event = #wxCommand{type = command_button_clicked}} -> 997 wxListBox:clear(Box), 998 NewOptions = Options#options{include_dirs = []}, 999 include_loop(NewOptions, Win, Box, DirPicker, Frame) 1000 end. 1001 1002macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) -> 1003 Size = {size,{300,480}}, 1004 Size1 = {size,{120,30}}, 1005 Dialog = wxFrame:new(Wx, ?MacroDir, "Macro Definitions",[Size]), 1006 MacroLabel = wxStaticText:new(Dialog, ?MacroLabel, "Macro"), 1007 TermLabel = wxStaticText:new(Dialog, ?TermLabel, "Term"), 1008 MacroText = wxTextCtrl:new(Dialog, ?MacroText, [Size1]), 1009 TermText = wxTextCtrl:new(Dialog, ?TermText, [Size1]), 1010 Box = wxListBox:new(Dialog, ?MacroBox, 1011 [{size, {250,300}}, 1012 {style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL 1013 bor ?wxLB_NEEDED_SB}]), 1014 1015 AddButton = wxButton:new(Dialog, ?MacroAdd, [{label, "Add"}]), 1016 DeleteButton = wxButton:new(Dialog, ?MacroDel, [{label, "Delete"}]), 1017 DeleteAllButton = wxButton:new(Dialog, ?MacroDelAll, [{label, "Delete All"}]), 1018 Ok = wxButton:new(Dialog, ?MacroOk, [{label, "OK"}]), 1019 Cancel = wxButton:new(Dialog, ?MacroCancel, [{label, "Cancel"}]), 1020 wxButton:connect(AddButton, command_button_clicked), 1021 wxButton:connect(DeleteButton, command_button_clicked), 1022 wxButton:connect(DeleteAllButton, command_button_clicked), 1023 wxButton:connect(Ok, command_button_clicked), 1024 wxButton:connect(Cancel, command_button_clicked), 1025 1026 Macros = [io_lib:format("~p = ~p", [X, Y]) 1027 || {X,Y} <- Options#options.defines], 1028 1029 wxListBox:set(Box, Macros), 1030 Layout = wxBoxSizer:new(?wxVERTICAL), 1031 Item = wxBoxSizer:new(?wxHORIZONTAL), 1032 MacroItem = wxBoxSizer:new(?wxVERTICAL), 1033 TermItem = wxBoxSizer:new(?wxVERTICAL), 1034 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 1035 Buttons1 = wxBoxSizer:new(?wxHORIZONTAL), 1036 1037 _ = wxSizer:add(MacroItem, MacroLabel, ?BorderOpt), 1038 _ = wxSizer:add(MacroItem, MacroText, ?BorderOpt), 1039 _ = wxSizer:add(TermItem, TermLabel, ?BorderOpt), 1040 _ = wxSizer:add(TermItem, TermText, ?BorderOpt), 1041 _ = wxSizer:add(Item, MacroItem), 1042 _ = wxSizer:add(Item, TermItem), 1043 _ = wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1044 _ = wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1045 _ = wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1046 _ = wxSizer:add(Buttons, DeleteButton, ?BorderOpt), 1047 _ = wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt), 1048 _ = wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1049 _ = wxSizer:add(Buttons1, Ok, ?BorderOpt), 1050 _ = wxSizer:add(Buttons1, Cancel, ?BorderOpt), 1051 _ = wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]), 1052 1053 wxFrame:connect(Dialog, close_window), 1054 wxWindow:setSizer(Dialog, Layout), 1055 wxFrame:show(Dialog), 1056 macro_loop(Options, Dialog, Box, MacroText, TermText, Frame). 1057 1058macro_loop(Options, Win, Box, MacroText, TermText, Frame) -> 1059 receive 1060 #wx{id = ?MacroCancel, 1061 event = #wxCommand{type = command_button_clicked}} -> 1062 wxWindow:destroy(Win), 1063 Options; 1064 #wx{id = ?MacroDir, event = #wxClose{type = close_window}} -> 1065 wxWindow:destroy(Win), 1066 Options; 1067 #wx{event = #wxClose{type = close_window}} -> 1068 wxWindow:destroy(Win), 1069 wxWindow:destroy(Frame); 1070 #wx{id = ?MacroOk, 1071 event = #wxCommand{type = command_button_clicked}} -> 1072 wxWindow:destroy(Win), 1073 Options; 1074 #wx{id = ?MacroAdd, 1075 event = #wxCommand{type = command_button_clicked}} -> 1076 Defines = Options#options.defines, 1077 NewDefines = 1078 case wxTextCtrl:getValue(MacroText) of 1079 "" -> Defines; 1080 Macro -> 1081 case wxTextCtrl:getValue(TermText) of 1082 "" -> 1083 orddict:store(list_to_atom(Macro), true, Defines); 1084 String -> 1085 orddict:store(list_to_atom(Macro), String, Defines) 1086 end 1087 end, 1088 NewOptions = Options#options{defines = NewDefines}, 1089 NewEntries = [io_lib:format("~p = ~p", [X, Y]) || {X, Y} <- NewDefines], 1090 wxListBox:set(Box, NewEntries), 1091 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame); 1092 #wx{id = ?MacroDel, 1093 event = #wxCommand{type = command_button_clicked}} -> 1094 NewOptions = 1095 case wxListBox:getSelections(Box) of 1096 {0, _} -> Options; 1097 {_, List} -> 1098 Fun = 1099 fun(X) -> 1100 Val = wxControlWithItems:getString(Box,X), 1101 [MacroName|_] = re:split(Val, " ", [{return, list}, unicode]), 1102 list_to_atom(MacroName) 1103 end, 1104 Delete = [Fun(X) || X <- List], 1105 lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List), 1106 Defines = Options#options.defines, 1107 NewDefines = lists:foldl(fun(X, Acc) -> 1108 orddict:erase(X, Acc) 1109 end, 1110 Defines, Delete), 1111 Options#options{defines = NewDefines} 1112 end, 1113 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame); 1114 #wx{id = ?MacroDelAll, 1115 event = #wxCommand{type = command_button_clicked}} -> 1116 wxListBox:clear(Box), 1117 NewOptions = Options#options{defines = []}, 1118 macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame) 1119 end. 1120 1121handle_help(State, Title, Txt) -> 1122 FileName = filename:join([code:lib_dir(dialyzer), "doc", Txt]), 1123 case file:open(FileName, [read]) of 1124 {error, Reason} -> 1125 error_sms(State, 1126 io_lib:format("Could not find doc/~ts file!\n\n ~tp", 1127 [Txt, Reason])); 1128 {ok, _Handle} -> 1129 case file:read_file(FileName) of 1130 {error, Reason} -> 1131 error_sms(State, 1132 io_lib:format("Could not read doc/~ts file!\n\n ~tp", 1133 [Txt, Reason])); 1134 {ok, Binary} -> 1135 Contents = binary_to_list(Binary), 1136 free_editor(State, Title, Contents) 1137 end 1138 end. 1139 1140add_warnings(#gui_state{warnings_box = WarnBox, 1141 rawWarnings = RawWarns} = State, Warnings) -> 1142 NewRawWarns = RawWarns ++ Warnings, 1143 %% The indentation cannot be turned off. 1144 %% The column numbers of locations are always displayed. 1145 WarnList = [string:trim(dialyzer:format_warning(W), trailing) || 1146 W <- NewRawWarns], 1147 wxListBox:set(WarnBox, WarnList), 1148 State#gui_state{rawWarnings = NewRawWarns}. 1149 1150handle_explanation(#gui_state{rawWarnings = RawWarns, 1151 warnings_box = WarnBox, 1152 expl_pid = ExplPid} = State) -> 1153 case wxListBox:isEmpty(WarnBox) of 1154 true -> 1155 error_sms(State, "\nThere are no warnings.\nRun the dialyzer first."); 1156 false -> 1157 case wxListBox:getSelections(WarnBox)of 1158 {0, []} -> 1159 error_sms(State,"\nYou must choose a warning to be explained\n"); 1160 {_, [WarnNumber]} -> 1161 Warn = lists:nth(WarnNumber+1,RawWarns), 1162 Self = self(), 1163 ExplPid ! {Self, warning, Warn}, 1164 explanation_loop(State) 1165 end 1166 end. 1167 1168explanation_loop(#gui_state{expl_pid = ExplPid} = State) -> 1169 receive 1170 {ExplPid, explanation, Explanation} -> 1171 show_explanation(State, Explanation); 1172 _ -> io:format("Unknown message\n"), 1173 explanation_loop(State) 1174 end. 1175 1176show_explanation(#gui_state{gui = Wx} = State, Explanation) -> 1177 case Explanation of 1178 none -> 1179 output_sms(State, ?DIALYZER_MESSAGE_TITLE, 1180 "There is not any explanation for this error!\n", info); 1181 Expl -> 1182 ExplString = format_explanation(Expl), 1183 Size = {size,{700, 300}}, 1184 Win = wxFrame:new(Wx, ?ExplWin, "Dialyzer Explanation", [{size,{740, 350}}]), 1185 1186 Editor = wxTextCtrl:new(Win, ?ExplText, 1187 [Size, 1188 {style, ?wxTE_MULTILINE 1189 bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]), 1190 wxTextCtrl:appendText(Editor, ExplString), 1191 wxFrame:connect(Win, close_window), 1192 ExplButton = wxButton:new(Win, ?ExplButton, [{label, "Further Explain"}]), 1193 wxButton:connect(ExplButton, command_button_clicked), 1194 Ok = wxButton:new(Win, ?ExplOk, [{label, "OK"}]), 1195 wxButton:connect(Ok, command_button_clicked), 1196 Layout = wxBoxSizer:new(?wxVERTICAL), 1197 Buttons = wxBoxSizer:new(?wxHORIZONTAL), 1198 _ = wxSizer:add(Buttons, ExplButton, ?BorderOpt), 1199 _ = wxSizer:add(Buttons, Ok, ?BorderOpt), 1200 _ = wxSizer:add(Layout, Editor, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]), 1201 _ = wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]), 1202 wxWindow:setSizer(Win, Layout), 1203 wxWindow:show(Win), 1204 NewState = State#gui_state{explanation_box = Editor}, 1205 show_explanation_loop(NewState, Win, Explanation) 1206 end. 1207 1208show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) -> 1209 receive 1210 {ExplPid, none, _} -> 1211 output_sms(State, ?DIALYZER_MESSAGE_TITLE, 1212 "There is not any other explanation for this error!\n", info), 1213 show_explanation_loop(State, Win, Explanation); 1214 {ExplPid, further, NewExplanation} -> 1215 update_explanation(State, NewExplanation), 1216 show_explanation_loop(State, Win, NewExplanation); 1217 #wx{id = ?ExplButton, event = #wxCommand{type = command_button_clicked}} -> 1218 ExplPid ! {self(), further, Explanation}, 1219 show_explanation_loop(State, Win, Explanation); 1220 #wx{id = ?ExplOk, event = #wxCommand{type = command_button_clicked}} -> 1221 wxWindow:destroy(Win); 1222 #wx{id = ?ExplWin, event = #wxClose{type = close_window}} -> 1223 wxWindow:destroy(Win); 1224 #wx{event = #wxClose{type = close_window}} -> 1225 wxWindow:destroy(Frame) 1226 end. 1227 1228update_explanation(#gui_state{explanation_box = Box}, Explanation) -> 1229 ExplString = format_explanation(Explanation), 1230 wxTextCtrl:appendText(Box, "\n --------------------------- \n"), 1231 wxTextCtrl:appendText(Box, ExplString). 1232 1233format_explanation({function_return, {M, F, A}, NewList}) -> 1234 io_lib:format("The function ~w:~tw/~w returns ~ts\n", 1235 [M, F, A, erl_types:t_to_string(NewList)]); 1236format_explanation(Explanation) -> 1237 io_lib:format("~p\n", [Explanation]). 1238