%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(dbg_ui_view).
%% External exports
-export([start/2]).
-record(state, {gs, % term() Graphics system id
win, % term() Attach process window data
coords, % {X,Y} Mouse point position
mod % atom() Module
}).
%%====================================================================
%% External exports
%%====================================================================
%%--------------------------------------------------------------------
%% start(GS, Mod)
%% Mod = atom()
%%--------------------------------------------------------------------
start(GS, Mod) ->
Title = "View Module " ++ atom_to_list(Mod),
case dbg_ui_winman:is_started(Title) of
true -> ignore;
false -> spawn(fun () -> init(GS, Mod, Title) end)
end.
-spec stop() -> no_return().
stop() ->
exit(stop).
%%====================================================================
%% Main loop and message handling
%%====================================================================
init(GS, Mod, Title) ->
%% Subscribe to messages from the interpreter
int:subscribe(),
%% Create attach process window
Win1 = dbg_ui_trace_win:create_win(GS, Title, ['Code Area'], menus()),
Window = dbg_ui_trace_win:get_window(Win1),
dbg_ui_winman:insert(Title, Window),
Win2 = gui_load_module(Win1, Mod),
Win3 =
lists:foldl(fun(Break, Win) ->
dbg_ui_trace_win:add_break(Win, 'Break', Break)
end,
Win2,
int:all_breaks(Mod)),
loop(#state{gs=GS, win=Win3, coords={0,0}, mod=Mod}).
loop(State) ->
receive
%% From the GUI main window
GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs ->
Cmd = dbg_ui_trace_win:handle_event(GuiEvent, State#state.win),
State2 = gui_cmd(Cmd, State),
loop(State2);
%% From the GUI help windows
{gui, Cmd} ->
State2 = gui_cmd(Cmd, State),
loop(State2);
%% From the interpreter
{int, Cmd} ->
State2 = int_cmd(Cmd, State),
loop(State2);
%% From the dbg_ui_winman process (Debugger window manager)
{dbg_ui_winman, update_windows_menu, Data} ->
dbg_ui_winman:update_windows_menu(Data),
loop(State);
{dbg_ui_winman, destroy} ->
stop();
%% Help window termination -- ignore
{'EXIT', _Pid, _Reason} ->
loop(State)
end.
%%--Commands from the GUI---------------------------------------------
gui_cmd(ignore, State) ->
State;
gui_cmd({win, Win}, State) ->
State#state{win=Win};
gui_cmd(stopped, _State) ->
stop();
gui_cmd({coords, Coords}, State) ->
State#state{coords=Coords};
gui_cmd({shortcut, Key}, State) ->
case shortcut(Key) of
false -> State;
Cmd -> gui_cmd(Cmd, State)
end;
%% File menu
gui_cmd('Close', _State) ->
stop();
%% Edit menu
gui_cmd('Go To Line...', State) ->
%% Will result in message handled below: {gui, {gotoline, Line}}
dbg_ui_trace_win:helpwin(gotoline, State#state.win,
State#state.gs, State#state.coords),
State;
gui_cmd({gotoline, Line}, State) ->
Win = dbg_ui_trace_win:select_line(State#state.win, Line),
State#state{win=Win};
gui_cmd('Search...', State) ->
dbg_ui_trace_win:helpwin(search, State#state.win,
State#state.gs, State#state.coords),
State;
%% Break menu
gui_cmd('Line Break...', State) ->
add_break(State#state.gs, State#state.coords, line,
State#state.mod,
dbg_ui_trace_win:selected_line(State#state.win)),
State;
gui_cmd('Conditional Break...', State) ->
add_break(State#state.gs, State#state.coords, conditional,
State#state.mod,
dbg_ui_trace_win:selected_line(State#state.win)),
State;
gui_cmd('Function Break...', State) ->
add_break(State#state.gs, State#state.coords, function,
State#state.mod, undefined),
State;
gui_cmd('Enable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.mod,
lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
int:enable_break(Mod, Line);
(_Break) ->
ignore
end,
Breaks),
State;
gui_cmd('Disable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.mod,
lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
int:disable_break(Mod, Line);
(_Break) ->
ignore
end,
Breaks),
State;
gui_cmd('Delete All', State) ->
int:no_break(State#state.mod),
State;
gui_cmd({break, {Mod, Line}, What}, State) ->
case What of
add -> int:break(Mod, Line);
delete -> int:delete_break(Mod, Line);
{status, inactive} -> int:disable_break(Mod, Line);
{status, active} -> int:enable_break(Mod, Line);
{trigger, Action} -> int:action_at_break(Mod, Line, Action)
end,
State;
%% Help menu
gui_cmd('Debugger', State) ->
Window = dbg_ui_trace_win:get_window(State#state.win),
HelpFile = filename:join([code:lib_dir(debugger),
"doc", "html", "part_frame.html"]),
tool_utils:open_help(Window, HelpFile),
State.
add_break(GS, Coords, Type, undefined, _Line) ->
dbg_ui_break:start(GS, Coords, Type);
add_break(GS, Coords, Type, Mod, undefined) ->
dbg_ui_break:start(GS, Coords, Type, Mod);
add_break(GS, Coords, Type, Mod, Line) ->
dbg_ui_break:start(GS, Coords, Type, Mod, Line).
%%--Commands from the interpreter-------------------------------------
int_cmd({new_break, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) ->
Win = dbg_ui_trace_win:add_break(State#state.win, 'Break', Break),
State#state{win=Win};
int_cmd({delete_break, {Mod,_Line}=Point}, #state{mod=Mod}=State) ->
Win = dbg_ui_trace_win:delete_break(State#state.win, Point),
State#state{win=Win};
int_cmd({break_options, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) ->
Win = dbg_ui_trace_win:update_break(State#state.win, Break),
State#state{win=Win};
int_cmd(no_break, State) ->
Win = dbg_ui_trace_win:clear_breaks(State#state.win),
State#state{win=Win};
int_cmd({no_break, _Mod}, State) ->
Win = dbg_ui_trace_win:clear_breaks(State#state.win),
State#state{win=Win};
int_cmd(_, State) ->
State.
%%====================================================================
%% GUI auxiliary functions
%%====================================================================
menus() ->
[{'File', [{'Close', 0}]},
{'Edit', [{'Go To Line...', 0},
{'Search...', 0}]},
{'Break', [{'Line Break...', 5},
{'Conditional Break...', 13},
{'Function Break...', 0},
separator,
{'Enable All', no},
{'Disable All', no},
{'Delete All', 0},
separator]},
{'Help', [{'Debugger', no}]}].
shortcut(c) -> 'Close';
shortcut(g) -> 'Go To Line...';
shortcut(s) -> 'Search...';
shortcut(b) -> 'Line Break...';
shortcut(r) -> 'Conditional Break...';
shortcut(f) -> 'Function Break...';
shortcut(d) -> 'Delete All';
shortcut(_) -> false.
gui_load_module(Win, Mod) ->
dbg_ui_trace_win:display({text, "Loading module..."}),
Contents = int:contents(Mod, any),
Win2 = dbg_ui_trace_win:show_code(Win, Mod, Contents),
dbg_ui_trace_win:display({text, ""}),
Win2.