%% -*- erlang-indent-level: 2 -*-
%%------------------------------------------------------------------------
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%%-----------------------------------------------------------------------
%%% File : dialyzer_gui_wx.erl
%%% Authors : Elli Fragkaki <[email protected]>
%%% Description : The wx-based graphical user interface of dialyzer.
%%%
%%% Created : 07 Oct 2009 by Elli Fragkaki <[email protected]>
%%%-----------------------------------------------------------------------
-module(dialyzer_gui_wx).
-export([start/1]).
-include("dialyzer.hrl").
-include("dialyzer_gui_wx.hrl").
%%------------------------------------------------------------------------
-define(DIALYZER_ERROR_TITLE, "Dialyzer Error").
-define(DIALYZER_MESSAGE_TITLE, "Dialyzer Message").
%%------------------------------------------------------------------------
-type wx_object() :: any(). %% XXX: should be imported from wx
-record(menu, {file :: wx_object(),
warnings :: wx_object(),
plt :: wx_object(),
options :: wx_object(),
help :: wx_object()}).
-record(gui_state, {add :: wx_object(),
add_dir :: wx_object(),
add_rec :: wx_object(),
chosen_box :: wx_object(),
analysis_pid :: pid(),
del_file :: wx_object(),
doc_plt :: dialyzer_plt:plt(),
clear_chosen :: wx_object(),
clear_log :: wx_object(),
explain_warn :: wx_object(),
clear_warn :: wx_object(),
init_plt :: dialyzer_plt:plt(),
dir_entry :: wx_object(),
file_box :: wx_object(),
files_to_analyze :: ordset(string()),
gui :: wx_object(),
log :: wx_object(),
menu :: #menu{},
mode :: wx_object(),
options :: #options{},
run :: wx_object(),
stop :: wx_object(),
frame :: wx_object(),
warnings_box :: wx_object(),
explanation_box :: wx_object(),
wantedWarnings :: list(),
rawWarnings :: list(),
backend_pid :: pid(),
expl_pid :: pid()}).
%%------------------------------------------------------------------------
-spec start(#options{}) -> ?RET_NOTHING_SUSPICIOUS.
start(DialyzerOptions) ->
process_flag(trap_exit, true),
Wx = wx:new(),
State = wx:batch(fun() -> create_window(Wx, DialyzerOptions) end),
gui_loop(State).
create_window(Wx, DialyzerOptions) ->
{ok, Host} = inet:gethostname(),
%%---------- initializing frame ---------
Frame = wxFrame:new(Wx, -1, "Dialyzer " ++ ?VSN ++ " @ " ++ Host),
wxFrame:connect(Frame, close_window),
FileMenu = createFileMenu(),
WarningsMenu = createWarningsMenu(),
PltMenu = createPltMenu(),
OptionsMenu = createOptionsMenu(),
HelpMenu = createHelpMenu(),
MenuBar = wxMenuBar:new(),
wxMenuBar:append(MenuBar, FileMenu, "File"),
wxMenuBar:append(MenuBar, WarningsMenu, "Warnings"),
wxMenuBar:append(MenuBar, PltMenu, "Plt"),
wxMenuBar:append(MenuBar, OptionsMenu, "Options"),
wxMenuBar:append(MenuBar, HelpMenu, "Help"),
wxFrame:setMenuBar(Frame, MenuBar),
ok = wxFrame:connect(Frame, command_menu_selected),
%%----------- Set Labels -------------
Lab1 = wxStaticText:new(Frame, ?LABEL1, "Directories or modules to analyze"),
OptionsLabel = wxStaticText:new(Frame, ?LABEL2, "Analysis Options"),
LogLabel = wxStaticText:new(Frame, ?LABEL3, "Log"),
FileLabel = wxStaticText:new(Frame, ?LABEL4, "File: "),
DirLabel = wxStaticText:new(Frame, ?LABEL5, "Dir: "),
WarningsLabel = wxStaticText:new(Frame, ?LABEL6, "Warnings"),
%%---------- Set TextBoxes -----------
ChosenBox = wxListBox:new(Frame, ?ChosenBox,
[{size, {250,200}},
{style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL
bor ?wxLB_NEEDED_SB}]),
LogBox = wxTextCtrl:new(Frame, ?LogBox,
[{size, {530,200}},
{style, ?wxTE_MULTILINE
bor ?wxTE_READONLY bor ?wxHSCROLL}]),
DefaultPath = code:root_dir(),
FilePicker = wxFilePickerCtrl:new(Frame, ?FilePicker,
[{path, DefaultPath},
{message, "Choose File to Analyse"},
{style,?wxFLP_FILE_MUST_EXIST bor ?wxFLP_USE_TEXTCTRL}]),
wxPickerBase:setTextCtrlProportion(FilePicker,3),
wxPickerBase:setPickerCtrlProportion(FilePicker,2),
DirPicker = wxDirPickerCtrl:new(Frame, ?DirPicker,
[{path, DefaultPath},
{message, "Choose Directory to Analyze"},
{style,?wxDIRP_DIR_MUST_EXIST bor ?wxDIRP_USE_TEXTCTRL}]),
WarningsBox = wxListBox:new(Frame, ?WarningsBox,
[{size, {700,200}},
{style, ?wxLB_HSCROLL
bor ?wxLB_NEEDED_SB}]),
%%--------- Set Buttons --------------
DeleteButton = wxButton:new(Frame, ?Del_Button, [{label, "Delete"}]),
DeleteAllButton = wxButton:new(Frame, ?DelAll_Button, [{label, "Delete All"}]),
FileType = wxRadioBox:new(Frame, ?RADIOBOX, " File Type: " , {1,1}, {150,90},
[["BeamFiles"],["SourceFiles"]]),
ClearLogButton = wxButton:new(Frame, ?ClearLog_Button, [{label, "Clear Log"}]),
AddButton = wxButton:new(Frame, ?Add_Button, [{label, "Add"}]),
AddDirButton = wxButton:new(Frame, ?AddDir_Button, [{label, "Add Dir"}]),
AddRecButton = wxButton:new(Frame, ?AddRec_Button, [{label, "Add Recursively"}]),
ExplainWarnButton = wxButton:new(Frame, ?ExplWarn_Button, [{label, "Explain Warning"}]),
ClearWarningsButton = wxButton:new(Frame, ?ClearWarn_Button, [{label, "Clear Warnings"}]),
RunButton = wxButton:new(Frame, ?Run_Button, [{label, "Run"}]),
StopButton = wxButton:new(Frame, ?Stop_Button, [{label, "Stop"}]),
wxWindow:disable(StopButton),
%%--------- Connect Buttons -----------
wxButton:connect(DeleteButton, command_button_clicked),
wxButton:connect(DeleteAllButton, command_button_clicked),
wxButton:connect(ClearLogButton, command_button_clicked),
wxButton:connect(AddButton, command_button_clicked),
wxButton:connect(AddDirButton, command_button_clicked),
wxButton:connect(AddRecButton, command_button_clicked),
wxButton:connect(ExplainWarnButton, command_button_clicked),
wxButton:connect(ClearWarningsButton, command_button_clicked),
wxButton:connect(RunButton, command_button_clicked),
wxButton:connect(StopButton, command_button_clicked),
%%------------Set Layout ------------
All = wxBoxSizer:new(?wxVERTICAL),
Top = wxBoxSizer:new(?wxHORIZONTAL),
Left = wxBoxSizer:new(?wxVERTICAL),
Right = wxBoxSizer:new(?wxVERTICAL),
RightUp = wxBoxSizer:new(?wxHORIZONTAL),
Opts = [{flag,?wxEXPAND bor ?wxALL}, {proportion,1}, {border, 1}],
Opts3 = [{flag,?wxEXPAND bor ?wxALL}, {proportion,3}, {border, 1}],
Center = [{flag, ?wxALIGN_CENTER_HORIZONTAL}],
ChooseItem = wxBoxSizer:new(?wxVERTICAL),
FileTypeItem = wxBoxSizer:new(?wxVERTICAL),
LogItem = wxBoxSizer:new(?wxVERTICAL),
FileDirItem = wxBoxSizer:new(?wxVERTICAL),
FileItem = wxBoxSizer:new(?wxHORIZONTAL),
DirItem = wxBoxSizer:new(?wxHORIZONTAL),
AddDirButtons = wxBoxSizer:new(?wxHORIZONTAL),
WarningsItem = wxBoxSizer:new(?wxVERTICAL),
ChooseButtons = wxBoxSizer:new(?wxHORIZONTAL),
WarnButtons = wxBoxSizer:new(?wxHORIZONTAL),
RunButtons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons = wxFlexGridSizer:new(3),
wxSizer:add(ChooseButtons, DeleteButton, ?BorderOpt),
wxSizer:add(ChooseButtons, DeleteAllButton, ?BorderOpt),
wxSizer:add(ChooseItem, Lab1, Center),
wxSizer:add(ChooseItem, ChosenBox, Opts),
wxSizer:add(ChooseItem, ChooseButtons, ?BorderOpt),
wxSizer:add(FileTypeItem, OptionsLabel),
wxSizer:add(FileTypeItem, FileType, [{border, 5}, {flag, ?wxALL}]),
wxSizer:add(LogItem, LogLabel, Center),
wxSizer:add(LogItem, LogBox, Opts3),
wxSizer:add(LogItem, ClearLogButton, ?BorderOpt),
wxSizer:add(FileItem, FileLabel),
wxSizer:add(FileItem, FilePicker),
wxSizer:add(DirItem, DirLabel),
wxSizer:add(DirItem, DirPicker),
wxSizer:add(AddDirButtons, AddDirButton, ?BorderOpt),
wxSizer:add(AddDirButtons, AddRecButton, ?BorderOpt),
wxSizer:add(FileDirItem, FileItem),
wxSizer:add(FileDirItem, AddButton, ?BorderOpt),
wxSizer:add(FileDirItem, DirItem, ?BorderOpt),
wxSizer:add(FileDirItem, AddDirButtons, ?BorderOpt),
wxSizer:add(WarnButtons, ExplainWarnButton, ?BorderOpt),
wxSizer:add(WarnButtons, ClearWarningsButton, ?BorderOpt),
wxSizer:add(RunButtons, RunButton, ?BorderOpt),
wxSizer:add(RunButtons, StopButton, ?BorderOpt),
wxSizer:add(Buttons, WarnButtons),
wxSizer:add(Buttons, wxStaticText:new(Frame, ?LABEL7, ""), [{flag, ?wxEXPAND}]),
wxSizer:add(Buttons, RunButtons),
wxFlexGridSizer:addGrowableCol(Buttons, 1),
wxSizer:add(WarningsItem, WarningsLabel, Center),
wxSizer:add(WarningsItem, WarningsBox, Opts3),
wxSizer:add(WarningsItem, Buttons, [{flag, ?wxEXPAND bor ?wxALL},?Border]),
wxSizer:add(Left, ChooseItem, Opts),
wxSizer:add(Left, FileDirItem, [{proportion, 1}, {border, 60}, {flag, ?wxTOP}]),
wxSizer:add(RightUp, FileTypeItem, ?BorderOpt),
wxSizer:add(RightUp, LogItem, Opts3),
wxSizer:add(Right, RightUp, Opts3),
wxSizer:add(Right, WarningsItem, Opts3),
wxSizer:add(Top, Left, Opts),
wxSizer:add(Top, Right, Opts3),
wxSizer:add(All, Top, Opts),
wxWindow:setSizer(Frame, All),
wxWindow:setSizeHints(Frame, {1150,600}),
wxWindow:show(Frame),
Warnings = [{?WARN_RETURN_NO_RETURN, ?menuID_WARN_NO_RETURN_FUN},
{?WARN_RETURN_ONLY_EXIT, ?menuID_WARN_ERROR_HANDLING_FUN},
{?WARN_NOT_CALLED, ?menuID_WARN_UNUSED_FUN},
{?WARN_NON_PROPER_LIST, ?menuID_WARN_LIST_CONSTR},
{?WARN_FUN_APP, ?menuID_WARN_BAD_FUN},
{?WARN_MATCHING, ?menuID_WARN_MATCH_FAILURES},
{?WARN_OPAQUE, ?menuID_WARN_OPAQUE},
{?WARN_FAILING_CALL, ?menuID_WARN_FAIL_FUN_CALLS},
{?WARN_CALLGRAPH, ?menuID_WARN_UNEXPORTED_FUN},
{?WARN_RACE_CONDITION, ?menuID_WARN_RACE_CONDITIONS},
%% For contracts.
{?WARN_CONTRACT_TYPES,?menuID_WARN_WRONG_CONTRACTS},
{?WARN_CONTRACT_SYNTAX, ?menuID_WARN_CONTRACT_SYNTAX}
],
Menu = #menu{file = FileMenu,
warnings = WarningsMenu,
plt = PltMenu,
options =OptionsMenu,
help = HelpMenu},
InitPlt = try dialyzer_plt:from_file(DialyzerOptions#options.init_plt)
catch throw:{dialyzer_error, _} -> dialyzer_plt:new()
end,
#gui_state{add = AddButton,
add_dir = AddDirButton,
add_rec = AddRecButton,
chosen_box = ChosenBox,
clear_chosen = DeleteAllButton,
clear_log = ClearLogButton,
explain_warn = ExplainWarnButton,
clear_warn = ClearWarningsButton,
del_file = DeleteButton,
doc_plt = dialyzer_plt:new(),
dir_entry = DirPicker,
file_box = FilePicker,
files_to_analyze = ordsets:new(),
gui = Wx,
init_plt = InitPlt,
log = LogBox,
menu = Menu,
mode = FileType,
options = DialyzerOptions,
run = RunButton,
stop = StopButton,
frame = Frame,
warnings_box = WarningsBox,
wantedWarnings = Warnings,
rawWarnings = []}.
createFileMenu() ->
FileMenu = wxMenu:new(),
wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_WARNINGS},
{text, "Save &Warnings"}])),
wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_SAVE_LOG},
{text, "Save &Log"}])),
wxMenu:append(FileMenu, wxMenuItem:new([{id, ?menuID_FILE_QUIT},
{text, "E&xit\tAlt-X"}])),
FileMenu.
createWarningsMenu() ->
WarningsMenu = wxMenu:new(),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_MATCH_FAILURES,
"Match failures"),
wxMenu:check(WarningsMenu, ?menuID_WARN_MATCH_FAILURES, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_FAIL_FUN_CALLS,
"Failing function calls"),
wxMenu:check(WarningsMenu, ?menuID_WARN_FAIL_FUN_CALLS, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_BAD_FUN,
"Bad fun applications"),
wxMenu:check(WarningsMenu, ?menuID_WARN_BAD_FUN, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_OPAQUE,
"Opaqueness violations"),
wxMenu:check(WarningsMenu, ?menuID_WARN_OPAQUE, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_LIST_CONSTR,
"Improper list constructions"),
wxMenu:check(WarningsMenu, ?menuID_WARN_LIST_CONSTR, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_UNUSED_FUN,
"Unused functions"),
wxMenu:check(WarningsMenu, ?menuID_WARN_UNUSED_FUN, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_ERROR_HANDLING_FUN,
"Error handling functions"),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_NO_RETURN_FUN,
"Functions of no return"),
wxMenu:check(WarningsMenu, ?menuID_WARN_NO_RETURN_FUN, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_UNEXPORTED_FUN,
"Call to unexported function"),
wxMenu:check(WarningsMenu, ?menuID_WARN_UNEXPORTED_FUN, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_RACE_CONDITIONS,
"Possible race conditions"),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_WRONG_CONTRACTS,
"Wrong contracts"),
wxMenu:check(WarningsMenu, ?menuID_WARN_WRONG_CONTRACTS, true),
wxMenu:appendCheckItem(WarningsMenu,
?menuID_WARN_CONTRACT_SYNTAX,
"Wrong contract syntax"),
wxMenu:check(WarningsMenu, ?menuID_WARN_CONTRACT_SYNTAX, true),
WarningsMenu.
createPltMenu() ->
PltMenu = wxMenu:new(),
wxMenu:appendCheckItem(PltMenu,
?menuID_PLT_INIT_EMPTY,
"Init with empty PLT"),
wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SHOW_CONTENTS},
{text, "Show contents"}])),
wxMenu:append(PltMenu, wxMenuItem:new([{id, ?menuID_PLT_SEARCH_CONTENTS},
{text, "Search contents"}])),
PltMenu.
createOptionsMenu() ->
OptsMenu = wxMenu:new(),
wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_MACRO},
{text, "Manage Macro Definitions"}])),
wxMenu:append(OptsMenu, wxMenuItem:new([{id, ?menuID_OPTIONS_INCLUDE_DIR},
{text, "Manage Include Directories"}])),
OptsMenu.
createHelpMenu() ->
HelpMenu = wxMenu:new(),
wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_MANUAL},
{text, "Manual"}])),
wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_WARNING_OPTIONS},
{text, "Warning Options"}])),
wxMenu:append(HelpMenu, wxMenuItem:new([{id, ?menuID_HELP_ABOUT},
{text, "About"}])),
HelpMenu.
%% ----------------------------------------------------------------
%%
%% Main GUI Loop
%%
-spec gui_loop(#gui_state{}) -> ?RET_NOTHING_SUSPICIOUS.
gui_loop(#gui_state{backend_pid = BackendPid, doc_plt = DocPlt,
log = Log, frame = Frame,
warnings_box = WarningsBox} = State) ->
receive
#wx{event = #wxClose{}} ->
%% io:format("~p Closing window ~n", [self()]),
ok = wxFrame:setStatusText(Frame, "Closing...",[]),
wxWindow:destroy(Frame),
?RET_NOTHING_SUSPICIOUS;
%% ----- Menu -----
#wx{id = ?menuID_FILE_SAVE_LOG, obj = Frame,
event = #wxCommand{type = command_menu_selected}} ->
save_file(State, log),
gui_loop(State);
#wx{id=?menuID_FILE_SAVE_WARNINGS, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
save_file(State, warnings),
gui_loop(State);
#wx{id=?menuID_FILE_QUIT, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
case maybe_quit(State) of
true -> ?RET_NOTHING_SUSPICIOUS;
false -> gui_loop(State)
end;
#wx{id=?menuID_PLT_SHOW_CONTENTS, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
show_doc_plt(State),
gui_loop(State);
#wx{id=?menuID_PLT_SEARCH_CONTENTS, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
case dialyzer_plt:get_specs(DocPlt) of
"" -> error_sms(State, "No analysis has been made yet!\n");
_ -> search_doc_plt(State)
end,
gui_loop(State);
#wx{id=?menuID_OPTIONS_INCLUDE_DIR, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
NewOptions = include_dialog(State),
NewState = State#gui_state{options = NewOptions},
gui_loop(NewState);
#wx{id=?menuID_OPTIONS_MACRO, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
NewOptions = macro_dialog(State),
NewState = State#gui_state{options = NewOptions},
gui_loop(NewState);
#wx{id=?menuID_HELP_MANUAL, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
handle_help(State, "Dialyzer Manual", "manual.txt"),
gui_loop(State);
#wx{id=?menuID_HELP_WARNING_OPTIONS, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
handle_help(State, "Dialyzer Warnings", "warnings.txt"),
gui_loop(State);
#wx{id=?menuID_HELP_ABOUT, obj=Frame,
event=#wxCommand{type=command_menu_selected}} ->
Message = " This is DIALYZER version " ++ ?VSN ++ " \n"++
"DIALYZER is a DIscrepany AnaLYZer for ERlang programs.\n\n"++
" Copyright (C) Tobias Lindahl <[email protected]>\n"++
" Kostis Sagonas <[email protected]>\n\n",
output_sms(State, "About Dialyzer", Message, info),
gui_loop(State);
%% ------ Buttons ---------
#wx{id=?Add_Button,
event=#wxCommand{type=command_button_clicked}} ->
State1 = handle_add_files(State),
gui_loop(State1);
#wx{id=?AddDir_Button,
event=#wxCommand{type=command_button_clicked}} ->
State1 = handle_add_dir(State),
gui_loop(State1);
#wx{id=?AddRec_Button,
event=#wxCommand{type=command_button_clicked}} ->
State1 = handle_add_rec(State),
gui_loop(State1);
#wx{id=?Del_Button,
event=#wxCommand{type=command_button_clicked}} ->
State1 = handle_file_delete(State),
gui_loop(State1);
#wx{id=?DelAll_Button,
event=#wxCommand{type=command_button_clicked}} ->
State1 = handle_file_delete_all(State),
gui_loop(State1);
#wx{id=?ClearLog_Button,
event=#wxCommand{type=command_button_clicked}} ->
wxTextCtrl:clear(State#gui_state.log),
gui_loop(State);
#wx{id=?ExplWarn_Button,
event=#wxCommand{type=command_button_clicked}} ->
handle_explanation(State),
gui_loop(State);
#wx{id=?ClearWarn_Button,
event=#wxCommand{type=command_button_clicked}} ->
wxListBox:clear(WarningsBox),
NewState = State#gui_state{rawWarnings = []},
gui_loop(NewState);
#wx{id=?Run_Button,
event=#wxCommand{type=command_button_clicked}} ->
NewState = start_analysis(State),
gui_loop(NewState);
#wx{id=?Stop_Button,
event=#wxCommand{type=command_button_clicked}} ->
BackendPid ! {self(), stop},
config_gui_stop(State),
update_editor(Log, "\n***** Analysis stopped ****\n"),
gui_loop(State);
%% ----- Analysis -----
{BackendPid, ext_calls, ExtCalls} ->
Msg = io_lib:format("The following functions are called "
"but type information about them is not available.\n"
"The analysis might get more precise by including "
"the modules containing these functions:\n\n\t~p\n",
[ExtCalls]),
free_editor(State,"Analysis Done", Msg),
gui_loop(State);
{BackendPid, log, LogMsg} ->
update_editor(Log, LogMsg),
gui_loop(State);
{BackendPid, warnings, Warns} ->
SortedWarns = lists:keysort(2, Warns), %% Sort on file/line
NewState = add_warnings(State, SortedWarns),
gui_loop(NewState);
{BackendPid, cserver, CServer, Plt} ->
Self = self(),
Fun =
fun() ->
dialyzer_explanation:expl_loop(Self, CServer, Plt)
end,
ExplanationPid = spawn_link(Fun),
gui_loop(State#gui_state{expl_pid = ExplanationPid});
{BackendPid, done, _NewPlt, NewDocPlt} ->
message(State, "Analysis done"),
config_gui_stop(State),
gui_loop(State#gui_state{doc_plt = NewDocPlt});
{'EXIT', BackendPid, {error, Reason}} ->
free_editor(State, ?DIALYZER_ERROR_TITLE, Reason),
config_gui_stop(State),
gui_loop(State);
{'EXIT', BackendPid, Reason} when Reason =/= 'normal' ->
free_editor(State, ?DIALYZER_ERROR_TITLE, io_lib:format("~p", [Reason])),
config_gui_stop(State),
gui_loop(State)
end.
maybe_quit(#gui_state{frame = Frame} = State) ->
case dialog(State, "Do you really want to quit?", ?DIALYZER_MESSAGE_TITLE) of
true ->
wxWindow:destroy(Frame),
true;
false ->
false
end.
%% ------------ Yes/No Question ------------
dialog(#gui_state{frame = Frame}, Message, Title) ->
MessageWin = wxMessageDialog:new(Frame, Message, [{caption, Title},{style, ?wxYES_NO bor ?wxICON_QUESTION bor ?wxNO_DEFAULT}]),
case wxDialog:showModal(MessageWin) of
?wxID_YES ->
true;
?wxID_NO ->
false;
?wxID_CANCEL ->
false
end.
search_doc_plt(#gui_state{gui = Wx} = State) ->
Dialog = wxFrame:new(Wx, ?SearchPltDialog, "Search the PLT",[{size,{400,100}},{style, ?wxSTAY_ON_TOP}]),
Size = {size,{120,30}},
ModLabel = wxStaticText:new(Dialog, ?ModLabel, "Module"),
ModText = wxTextCtrl:new(Dialog, ?ModText,[Size]),
FunLabel = wxStaticText:new(Dialog, ?FunLabel, "Function"),
FunText = wxTextCtrl:new(Dialog, ?FunText,[Size]),
ArLabel = wxStaticText:new(Dialog, ?ArLabel, "Arity"),
ArText = wxTextCtrl:new(Dialog, ?ArText,[Size]),
SearchButton = wxButton:new(Dialog, ?SearchButton, [{label, "Search"}]),
wxButton:connect(SearchButton, command_button_clicked),
Cancel = wxButton:new(Dialog, ?Search_Cancel, [{label, "Cancel"}]),
wxButton:connect(Cancel, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
Top = wxBoxSizer:new(?wxHORIZONTAL),
ModLayout = wxBoxSizer:new(?wxVERTICAL),
FunLayout = wxBoxSizer:new(?wxVERTICAL),
ArLayout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(ModLayout, ModLabel, ?BorderOpt),
wxSizer:add(ModLayout,ModText, ?BorderOpt),
wxSizer:add(FunLayout, FunLabel, ?BorderOpt),
wxSizer:add(FunLayout,FunText, ?BorderOpt),
wxSizer:add(ArLayout, ArLabel, ?BorderOpt),
wxSizer:add(ArLayout,ArText, ?BorderOpt),
wxSizer:add(Buttons, SearchButton, ?BorderOpt),
wxSizer:add(Buttons,Cancel, ?BorderOpt),
wxSizer:add(Top, ModLayout),
wxSizer:add(Top, FunLayout),
wxSizer:add(Top, ArLayout),
wxSizer:add(Layout, Top,[{flag, ?wxALIGN_CENTER}]),
wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
search_plt_loop(State, Dialog, ModText, FunText, ArText, SearchButton, Cancel).
search_plt_loop(State= #gui_state{doc_plt = DocPlt, frame = Frame}, Win, ModText, FunText, ArText, Search, Cancel) ->
receive
#wx{id = ?Search_Cancel,
event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?SearchPltDialog, event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win),
wxWindow:destroy(Frame);
#wx{id = ?SearchButton,
event = #wxCommand{type = command_button_clicked}} ->
M = format_search(wxTextCtrl:getValue(ModText)),
F = format_search(wxTextCtrl:getValue(FunText)),
A = format_search(wxTextCtrl:getValue(ArText)),
if
(M =:= '_') orelse (F =:= '_') orelse (A =:= '_') ->
error_sms(State, "Please give:\n Module (atom)\n Function (atom)\n Arity (integer)\n"),
search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel);
true ->
case dialyzer_plt:get_specs(DocPlt, M, F, A) of
none ->
error_sms(State, "No such function"),
search_plt_loop(State, Win, ModText, FunText, ArText, Search, Cancel);
NonEmptyString ->
wxWindow:destroy(Win),
free_editor(State, "Content of PLT", NonEmptyString)
end
end
end.
format_search([]) ->
'_';
format_search(String) ->
try list_to_integer(String)
catch error:_ -> list_to_atom(String)
end.
show_doc_plt(#gui_state{doc_plt = DocPLT} = State) ->
case dialyzer_plt:get_specs(DocPLT) of
"" -> error_sms(State, "No analysis has been made yet!\n");
NonEmptyString -> free_editor(State, "Content of PLT", NonEmptyString)
end.
message(State, Message) ->
output_sms(State, ?DIALYZER_MESSAGE_TITLE, Message, info).
error_sms(State, Message) ->
output_sms(State, ?DIALYZER_ERROR_TITLE, Message, error).
output_sms(#gui_state{frame = Frame}, Title, Message, Type) ->
case Type of
error ->
MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_ERROR}]);
info ->
MessageWin = wxMessageDialog:new(Frame,Message,[{caption, Title},{style, ?wxOK bor ?wxICON_INFORMATION}])
end,
wxWindow:setSizeHints(MessageWin, {350,100}),
wxDialog:showModal(MessageWin).
free_editor(#gui_state{gui = Wx, frame = Frame}, Title, Contents0) ->
Contents = lists:flatten(Contents0),
Tokens = string:tokens(Contents, "\n"),
NofLines = length(Tokens),
LongestLine = lists:max([length(X) || X <- Tokens]),
Height0 = NofLines * 25 + 80,
Height = if Height0 > 500 -> 500; true -> Height0 end,
Width0 = LongestLine * 7 + 60,
Width = if Width0 > 800 -> 800; true -> Width0 end,
Size = {size,{Width, Height}},
Win = wxFrame:new(Wx, ?Message, Title, [{size,{Width+4, Height+50}}]),
Editor = wxTextCtrl:new(Win, ?Message_Info,
[Size,
{style, ?wxTE_MULTILINE
bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]),
wxTextCtrl:appendText(Editor, Contents),
wxFrame:connect(Win, close_window),
Ok = wxButton:new(Win, ?Message_Ok, [{label, "OK"}]),
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(Layout, Editor, ?BorderOpt),
wxSizer:add(Layout, Ok, [{flag, ?wxALIGN_CENTER bor ?wxBOTTOM bor ?wxALL}, ?Border]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
show_info_loop(Frame, Win).
show_info_loop(Frame, Win) ->
receive
#wx{id = ?Message_Ok, event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?Message, event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Frame)
end.
handle_add_files(#gui_state{chosen_box = ChosenBox, file_box = FileBox,
files_to_analyze = FileList,
mode = Mode} = State) ->
case wxFilePickerCtrl:getPath(FileBox) of
"" ->
State;
File ->
NewFile = ordsets:new(),
NewFile1 = ordsets:add_element(File,NewFile),
Ext =
case wxRadioBox:getSelection(Mode) of
0 -> ".beam";
1-> ".erl"
end,
State#gui_state{files_to_analyze = add_files(filter_mods(NewFile1, Ext), FileList, ChosenBox, Ext)}
end.
handle_add_dir(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox,
files_to_analyze = FileList,
mode = Mode} = State) ->
case wxDirPickerCtrl:getPath(DirBox) of
"" ->
State;
Dir ->
NewDir = ordsets:new(),
NewDir1 = ordsets:add_element(Dir,NewDir),
Ext = case wxRadioBox:getSelection(Mode) of
0 -> ".beam";
1-> ".erl"
end,
State#gui_state{files_to_analyze = add_files(filter_mods(NewDir1,Ext), FileList, ChosenBox, Ext)}
end.
handle_add_rec(#gui_state{chosen_box = ChosenBox, dir_entry = DirBox, files_to_analyze = FileList,
mode = Mode} = State) ->
case wxDirPickerCtrl:getPath(DirBox) of
"" ->
State;
Dir ->
NewDir = ordsets:new(),
NewDir1 = ordsets:add_element(Dir,NewDir),
TargetDirs = ordsets:union(NewDir1, all_subdirs(NewDir1)),
case wxRadioBox:getSelection(Mode) of
0 -> Ext = ".beam";
1-> Ext = ".erl"
end,
State#gui_state{files_to_analyze = add_files(filter_mods(TargetDirs,Ext), FileList, ChosenBox, Ext)}
end.
handle_file_delete(#gui_state{chosen_box = ChosenBox,
files_to_analyze = FileList} = State) ->
{_, List} = wxListBox:getSelections(ChosenBox),
Set = ordsets:from_list([wxControlWithItems:getString(ChosenBox, X) || X <- List]),
FileList1 = ordsets:subtract(FileList,Set),
lists:foreach(fun (X) -> wxListBox:delete(ChosenBox, X) end, List),
State#gui_state{files_to_analyze = FileList1}.
handle_file_delete_all(#gui_state{chosen_box = ChosenBox} = State) ->
wxListBox:clear(ChosenBox),
State#gui_state{files_to_analyze = ordsets:new()}.
add_files(File, FileList, ChosenBox, Ext) ->
Set = filter_mods(FileList, Ext),
Files = ordsets:union(File, Set),
Files1 = ordsets:to_list(Files),
wxListBox:set(ChosenBox, Files1),
Files.
filter_mods(Mods, Extension) ->
Fun = fun(X) ->
filename:extension(X) =:= Extension
orelse
(filelib:is_dir(X) andalso
contains_files(X, Extension))
end,
ordsets:filter(Fun, Mods).
contains_files(Dir, Extension) ->
{ok, Files} = file:list_dir(Dir),
lists:any(fun(X) -> filename:extension(X) =:= Extension end, Files).
all_subdirs(Dirs) ->
all_subdirs(Dirs, []).
all_subdirs([Dir|T], Acc) ->
{ok, Files} = file:list_dir(Dir),
SubDirs = lists:zf(fun(F) ->
SubDir = filename:join(Dir, F),
case filelib:is_dir(SubDir) of
true -> {true, SubDir};
false -> false
end
end, Files),
NewAcc = ordsets:union(ordsets:from_list(SubDirs), Acc),
all_subdirs(T ++ SubDirs, NewAcc);
all_subdirs([], Acc) ->
Acc.
start_analysis(State) ->
Analysis = build_analysis_record(State),
case get_anal_files(State, Analysis#analysis.start_from) of
error ->
Msg = "You must choose one or more files or dirs\n"
"before starting the analysis!",
error_sms(State, Msg),
config_gui_stop(State),
State;
{ok, Files} ->
Msg = "\n========== Starting Analysis ==========\n\n",
update_editor(State#gui_state.log, Msg),
NewAnalysis = Analysis#analysis{files = Files},
run_analysis(State, NewAnalysis)
end.
build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options,
init_plt = InitPlt0}) ->
StartFrom =
case wxRadioBox:getSelection(Mode) of
0 -> byte_code;
1 -> src_code
end,
InitPlt =
case wxMenu:isChecked(Menu#menu.plt,?menuID_PLT_INIT_EMPTY) of
true -> dialyzer_plt:new();
false -> InitPlt0
end,
#analysis{defines = Options#options.defines,
include_dirs = Options#options.include_dirs,
plt = InitPlt,
start_from = StartFrom}.
get_anal_files(#gui_state{files_to_analyze = Files}, StartFrom) ->
FilteredMods =
case StartFrom of
src_code -> filter_mods(Files, ".erl");
byte_code -> filter_mods(Files, ".beam")
end,
FilteredDirs = [X || X <- Files, filelib:is_dir(X)],
case ordsets:union(FilteredMods, FilteredDirs) of
[] -> error;
Set -> {ok, Set}
end.
run_analysis(State, Analysis) ->
config_gui_start(State),
Self = self(),
NewAnalysis = Analysis#analysis{doc_plt = dialyzer_plt:new()},
LegalWarnings = find_legal_warnings(State),
Fun =
fun() ->
dialyzer_analysis_callgraph:start(Self, LegalWarnings, NewAnalysis)
end,
BackendPid = spawn_link(Fun),
State#gui_state{backend_pid = BackendPid}.
find_legal_warnings(#gui_state{menu = #menu{warnings = MenuWarnings},
wantedWarnings = Warnings }) ->
ordsets:from_list([Tag || {Tag, MenuItem} <- Warnings,
wxMenu:isChecked(MenuWarnings, MenuItem)]).
update_editor(Editor, Msg) ->
wxTextCtrl:appendText(Editor,Msg).
config_gui_stop(State) ->
wxWindow:disable(State#gui_state.stop),
wxWindow:enable(State#gui_state.run),
wxWindow:enable(State#gui_state.del_file),
wxWindow:enable(State#gui_state.clear_chosen),
wxWindow:enable(State#gui_state.add),
wxWindow:enable(State#gui_state.add_dir),
wxWindow:enable(State#gui_state.add_rec),
wxWindow:enable(State#gui_state.clear_warn),
wxWindow:enable(State#gui_state.clear_log),
Menu = State#gui_state.menu,
wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS,true),
wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG,true),
wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO,true),
wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR,true),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY,true),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS,true),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS,true),
wxRadioBox:enable(State#gui_state.mode).
config_gui_start(State) ->
wxWindow:enable(State#gui_state.stop),
wxWindow:disable(State#gui_state.run),
wxWindow:disable(State#gui_state.del_file),
wxWindow:disable(State#gui_state.clear_chosen),
wxWindow:disable(State#gui_state.add),
wxWindow:disable(State#gui_state.add_dir),
wxWindow:disable(State#gui_state.add_rec),
wxWindow:disable(State#gui_state.clear_warn),
wxWindow:disable(State#gui_state.clear_log),
Menu = State#gui_state.menu,
wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_WARNINGS, false),
wxMenu:enable(Menu#menu.file,?menuID_FILE_SAVE_LOG, false),
wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_MACRO, false),
wxMenu:enable(Menu#menu.options,?menuID_OPTIONS_INCLUDE_DIR, false),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_INIT_EMPTY, false),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_SHOW_CONTENTS, false),
wxMenu:enable(Menu#menu.plt,?menuID_PLT_SEARCH_CONTENTS, false),
wxRadioBox:disable(State#gui_state.mode).
save_file(#gui_state{frame = Frame, warnings_box = WBox, log = Log} = State, Type) ->
case Type of
warnings ->
Message = "Save Warnings",
Box = WBox;
log -> Message = "Save Log",
Box = Log
end,
case wxTextCtrl:getValue(Box) of
"" -> error_sms(State,"There is nothing to save...\n");
_ ->
DefaultPath = code:root_dir(),
FileDialog = wxFileDialog:new(Frame,
[{defaultDir, DefaultPath},
{message, Message},
{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FileDialog) of
?wxID_OK -> Path = wxFileDialog:getPath(FileDialog),
case wxTextCtrl:saveFile(Box,[{file,Path}]) of
true -> ok;
false -> error_sms(State,"Could not write to file:\n" ++ Path)
end;
?wxID_CANCEL -> wxWindow:destroy(FileDialog);
_ -> error_sms(State,"Could not write to file:\n")
end
end.
include_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Size = {size,{300,480}},
Dialog = wxFrame:new(Wx, ?IncludeDir, "Include Directories",[Size]),
DirLabel = wxStaticText:new(Dialog, ?InclLabel, "Directory: "),
DefaultPath = code:root_dir(),
DirPicker = wxDirPickerCtrl:new(Dialog, ?InclPicker,
[{path, DefaultPath},
{message, "Choose Directory to Include"},
{style,?wxDIRP_DIR_MUST_EXIST bor ?wxDIRP_USE_TEXTCTRL}]),
Box = wxListBox:new(Dialog, ?InclBox,
[{size, {200,300}},
{style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL
bor ?wxLB_NEEDED_SB}]),
AddButton = wxButton:new(Dialog, ?InclAdd, [{label, "Add"}]),
DeleteButton = wxButton:new(Dialog, ?InclDel, [{label, "Delete"}]),
DeleteAllButton = wxButton:new(Dialog, ?InclDelAll, [{label, "Delete All"}]),
Ok = wxButton:new(Dialog, ?InclOk, [{label, "OK"}]),
Cancel = wxButton:new(Dialog, ?InclCancel, [{label, "Cancel"}]),
wxButton:connect(AddButton, command_button_clicked),
wxButton:connect(DeleteButton, command_button_clicked),
wxButton:connect(DeleteAllButton, command_button_clicked),
wxButton:connect(Ok, command_button_clicked),
wxButton:connect(Cancel, command_button_clicked),
Dirs = [io_lib:format("~s", [X])
|| X <- Options#options.include_dirs],
wxListBox:set(Box, Dirs),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(Layout, DirLabel, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxSizer:add(Layout, DirPicker, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxSizer:add(Layout,AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
wxSizer:add(Layout,Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
wxSizer:add(Layout,Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxSizer:add(Buttons1, Ok, ?BorderOpt),
wxSizer:add(Buttons1,Cancel, ?BorderOpt),
wxSizer:add(Layout,Buttons1,[{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
include_loop(Options, Dialog, Box, DirPicker, Frame).
include_loop(Options, Win, Box, DirPicker, Frame) ->
receive
#wx{id = ?InclCancel,
event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win),
Options;
#wx{id = ?IncludeDir, event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win),
Options;
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win),
wxWindow:destroy(Frame);
#wx{id = ?InclOk,
event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win),
Options;
#wx{id = ?InclAdd,
event = #wxCommand{type = command_button_clicked}} ->
Dirs = Options#options.include_dirs,
NewDirs =
case wxDirPickerCtrl:getPath(DirPicker) of
"" -> Dirs;
Add -> [Add|Dirs]
end,
NewOptions = Options#options{include_dirs = NewDirs},
wxListBox:set(Box, NewDirs),
include_loop(NewOptions, Win, Box, DirPicker, Frame);
#wx{id = ?InclDel,
event = #wxCommand{type = command_button_clicked}} ->
NewOptions =
case wxListBox:getSelections(Box) of
{0,_} -> Options;
{_,List} ->
DelList = [wxControlWithItems:getString(Box,X) || X <- List],
NewDirs = Options#options.include_dirs -- DelList,
lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List),
Options#options{include_dirs = NewDirs}
end,
include_loop(NewOptions, Win, Box, DirPicker, Frame);
#wx{id = ?InclDelAll,
event = #wxCommand{type = command_button_clicked}} ->
wxListBox:clear(Box),
NewOptions = Options#options{include_dirs = []},
include_loop(NewOptions, Win, Box, DirPicker, Frame)
end.
macro_dialog(#gui_state{gui = Wx, frame = Frame, options = Options}) ->
Size = {size,{300,480}},
Size1 = {size,{120,30}},
Dialog = wxFrame:new(Wx, ?MacroDir, "Macro Definitions",[Size]),
MacroLabel = wxStaticText:new(Dialog, ?MacroLabel, "Macro"),
TermLabel = wxStaticText:new(Dialog, ?TermLabel, "Term"),
MacroText = wxTextCtrl:new(Dialog, ?MacroText, [Size1]),
TermText = wxTextCtrl:new(Dialog, ?TermText, [Size1]),
Box = wxListBox:new(Dialog, ?MacroBox,
[{size, {250,300}},
{style, ?wxLB_EXTENDED bor ?wxLB_HSCROLL
bor ?wxLB_NEEDED_SB}]),
AddButton = wxButton:new(Dialog, ?MacroAdd, [{label, "Add"}]),
DeleteButton = wxButton:new(Dialog, ?MacroDel, [{label, "Delete"}]),
DeleteAllButton = wxButton:new(Dialog, ?MacroDelAll, [{label, "Delete All"}]),
Ok = wxButton:new(Dialog, ?MacroOk, [{label, "OK"}]),
Cancel = wxButton:new(Dialog, ?MacroCancel, [{label, "Cancel"}]),
wxButton:connect(AddButton, command_button_clicked),
wxButton:connect(DeleteButton, command_button_clicked),
wxButton:connect(DeleteAllButton, command_button_clicked),
wxButton:connect(Ok, command_button_clicked),
wxButton:connect(Cancel, command_button_clicked),
Macros = [io_lib:format("~p = ~p", [X, Y])
|| {X,Y} <- Options#options.defines],
wxListBox:set(Box, Macros),
Layout = wxBoxSizer:new(?wxVERTICAL),
Item = wxBoxSizer:new(?wxHORIZONTAL),
MacroItem = wxBoxSizer:new(?wxVERTICAL),
TermItem = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
Buttons1 = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(MacroItem, MacroLabel, ?BorderOpt),
wxSizer:add(MacroItem, MacroText, ?BorderOpt),
wxSizer:add(TermItem, TermLabel, ?BorderOpt),
wxSizer:add(TermItem, TermText, ?BorderOpt),
wxSizer:add(Item, MacroItem),
wxSizer:add(Item, TermItem),
wxSizer:add(Layout, Item, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxSizer:add(Layout, AddButton, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
wxSizer:add(Layout, Box, [{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
wxSizer:add(Buttons, DeleteButton, ?BorderOpt),
wxSizer:add(Buttons, DeleteAllButton, ?BorderOpt),
wxSizer:add(Layout, Buttons, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxSizer:add(Buttons1, Ok, ?BorderOpt),
wxSizer:add(Buttons1, Cancel, ?BorderOpt),
wxSizer:add(Layout, Buttons1, [{flag, ?wxALIGN_RIGHT bor ?wxBOTTOM}]),
wxFrame:connect(Dialog, close_window),
wxWindow:setSizer(Dialog, Layout),
wxFrame:show(Dialog),
macro_loop(Options, Dialog, Box, MacroText, TermText, Frame).
macro_loop(Options, Win, Box, MacroText, TermText, Frame) ->
receive
#wx{id = ?MacroCancel,
event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win),
Options;
#wx{id = ?MacroDir, event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win),
Options;
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win),
wxWindow:destroy(Frame);
#wx{id = ?MacroOk,
event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win),
Options;
#wx{id = ?MacroAdd,
event = #wxCommand{type = command_button_clicked}} ->
Defines = Options#options.defines,
NewDefines =
case wxTextCtrl:getValue(MacroText) of
"" -> Defines;
Macro ->
case wxTextCtrl:getValue(TermText) of
"" ->
orddict:store(list_to_atom(Macro), true, Defines);
String ->
orddict:store(list_to_atom(Macro), String, Defines)
end
end,
NewOptions = Options#options{defines = NewDefines},
NewEntries = [io_lib:format("~p = ~p", [X, Y]) || {X, Y} <- NewDefines],
wxListBox:set(Box, NewEntries),
macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame);
#wx{id = ?MacroDel,
event = #wxCommand{type = command_button_clicked}} ->
NewOptions =
case wxListBox:getSelections(Box) of
{0, _} -> Options;
{_, List} ->
Fun =
fun(X) ->
Val = wxControlWithItems:getString(Box,X),
[MacroName|_] = re:split(Val, " ", [{return, list}]),
list_to_atom(MacroName)
end,
Delete = [Fun(X) || X <- List],
lists:foreach(fun (X) -> wxListBox:delete(Box, X) end, List),
Defines = Options#options.defines,
NewDefines = lists:foldl(fun(X, Acc) ->
orddict:erase(X, Acc)
end,
Defines, Delete),
Options#options{defines = NewDefines}
end,
macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame);
#wx{id = ?MacroDelAll,
event = #wxCommand{type = command_button_clicked}} ->
wxListBox:clear(Box),
NewOptions = Options#options{defines = []},
macro_loop(NewOptions, Win, Box, MacroText, TermText, Frame)
end.
handle_help(State, Title, Txt) ->
FileName = filename:join([code:lib_dir(dialyzer), "doc", Txt]),
case file:open(FileName, [read]) of
{error, Reason} ->
error_sms(State,
io_lib:format("Could not find doc/~s file!\n\n ~p",
[Txt, Reason]));
{ok, _Handle} ->
case file:read_file(FileName) of
{error, Reason} ->
error_sms(State,
io_lib:format("Could not read doc/~s file!\n\n ~p",
[Txt, Reason]));
{ok, Binary} ->
Contents = binary_to_list(Binary),
free_editor(State, Title, Contents)
end
end.
add_warnings(#gui_state{warnings_box = WarnBox,
rawWarnings = RawWarns} = State, Warnings) ->
NewRawWarns = RawWarns ++ Warnings,
WarnList = [dialyzer:format_warning(W) -- "\n" || W <- NewRawWarns],
wxListBox:set(WarnBox, WarnList),
State#gui_state{rawWarnings = NewRawWarns}.
handle_explanation(#gui_state{rawWarnings = RawWarns,
warnings_box = WarnBox,
expl_pid = ExplPid} = State) ->
case wxListBox:isEmpty(WarnBox) of
true ->
error_sms(State, "\nThere are no warnings.\nRun the dialyzer first.");
false ->
case wxListBox:getSelections(WarnBox)of
{0, []} ->
error_sms(State,"\nYou must choose a warning to be explained\n");
{_, [WarnNumber]} ->
Warn = lists:nth(WarnNumber+1,RawWarns),
Self = self(),
ExplPid ! {Self, warning, Warn},
explanation_loop(State)
end
end.
explanation_loop(#gui_state{expl_pid = ExplPid} = State) ->
receive
{ExplPid, explanation, Explanation} ->
show_explanation(State, Explanation);
_ -> io:format("Unknown message\n"),
explanation_loop(State)
end.
show_explanation(#gui_state{gui = Wx} = State, Explanation) ->
case Explanation of
none ->
output_sms(State, ?DIALYZER_MESSAGE_TITLE,
"There is not any explanation for this error!\n", info);
Expl ->
ExplString = format_explanation(Expl),
Size = {size,{700, 300}},
Win = wxFrame:new(Wx, ?ExplWin, "Dialyzer Explanation", [{size,{740, 350}}]),
Editor = wxTextCtrl:new(Win, ?ExplText,
[Size,
{style, ?wxTE_MULTILINE
bor ?wxTE_READONLY bor ?wxVSCROLL bor ?wxEXPAND}]),
wxTextCtrl:appendText(Editor, ExplString),
wxFrame:connect(Win, close_window),
ExplButton = wxButton:new(Win, ?ExplButton, [{label, "Further Explain"}]),
wxButton:connect(ExplButton, command_button_clicked),
Ok = wxButton:new(Win, ?ExplOk, [{label, "OK"}]),
wxButton:connect(Ok, command_button_clicked),
Layout = wxBoxSizer:new(?wxVERTICAL),
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:add(Buttons, ExplButton, ?BorderOpt),
wxSizer:add(Buttons, Ok, ?BorderOpt),
wxSizer:add(Layout, Editor,[{flag, ?wxALIGN_CENTER_HORIZONTAL bor ?wxALL}, ?Border]),
wxSizer:add(Layout, Buttons,[{flag, ?wxALIGN_CENTER_HORIZONTAL}]),
wxWindow:setSizer(Win, Layout),
wxWindow:show(Win),
show_explanation_loop(State#gui_state{explanation_box = Editor}, Win, Explanation)
end.
show_explanation_loop(#gui_state{frame = Frame, expl_pid = ExplPid} = State, Win, Explanation) ->
receive
{ExplPid, none, _} ->
output_sms(State, ?DIALYZER_MESSAGE_TITLE,
"There is not any other explanation for this error!\n", info),
show_explanation_loop(State, Win, Explanation);
{ExplPid, further, NewExplanation} ->
update_explanation(State, NewExplanation),
show_explanation_loop(State, Win, NewExplanation);
#wx{id = ?ExplButton, event = #wxCommand{type = command_button_clicked}} ->
ExplPid ! {self(), further, Explanation},
show_explanation_loop(State, Win, Explanation);
#wx{id = ?ExplOk, event = #wxCommand{type = command_button_clicked}} ->
wxWindow:destroy(Win);
#wx{id = ?ExplWin, event = #wxClose{type = close_window}} ->
wxWindow:destroy(Win);
#wx{event = #wxClose{type = close_window}} ->
wxWindow:destroy(Frame)
end.
update_explanation(#gui_state{explanation_box = Box}, Explanation) ->
ExplString = format_explanation(Explanation),
wxTextCtrl:appendText(Box, "\n --------------------------- \n"),
wxTextCtrl:appendText(Box, ExplString).
format_explanation({function_return, {M, F, A}, NewList}) ->
io_lib:format("The function ~p: ~p/~p returns ~p\n",
[M, F, A, erl_types:t_to_string(NewList)]);
format_explanation(Explanation) ->
io_lib:format("~p\n", [Explanation]).