%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-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_break_win).
-compile([{nowarn_deprecated_function,{gs,button,2}},
{nowarn_deprecated_function,{gs,config,2}},
{nowarn_deprecated_function,{gs,create,3}},
{nowarn_deprecated_function,{gs,entry,2}},
{nowarn_deprecated_function,{gs,frame,2}},
{nowarn_deprecated_function,{gs,label,2}},
{nowarn_deprecated_function,{gs,listbox,2}},
{nowarn_deprecated_function,{gs,menu,2}},
{nowarn_deprecated_function,{gs,menuitem,2}},
{nowarn_deprecated_function,{gs,radiobutton,2}},
{nowarn_deprecated_function,{gs,read,2}},
{nowarn_deprecated_function,{gs,window,2}}]).
%% External exports
-export([create_win/5,
update_functions/2,
handle_event/2]).
-record(winInfo, {type, % line | conditional | function
win, % gsobj()
packer, % gsobj() | undefined
entries, % [{atom|integer, GSobj()}]
trigger, % enable | disable | delete
ok, % gsobj()
cancel, % gsobj()
listbox, % gsobj()
funcs=[] % [[Name, Arity]]
}).
%%====================================================================
%% External exports
%%====================================================================
%%--------------------------------------------------------------------
%% create_win(GS, Pos, Type, Mod, Line) -> #winInfo{}
%% GS = graphics system identifier
%% Pos = {X, Y}
%% X = Y = integer()
%% Type = line | conditional | function
%% Mod = atom() | ""
%% Line = integer() | ""
%%--------------------------------------------------------------------
create_win(GS, {X, Y}, function, Mod, _Line) ->
Pad = 8,
W = 230,
Font = dbg_ui_win:font(normal),
%% Window
Win = gs:window(GS, [{title, "Function Break"}, {x, X}, {y, Y},
{destroy, true}, {configure, true},
{keypress, true}, {data, window}]),
%% Frame
Frm = gs:frame(Win, [{x, 0}, {y, 0}, {width, W}, {height, 190},
{packer_x, [{fixed, 70}, {stretch, 1, W-80},
{fixed, 10}]},
{packer_y, [{fixed, 10}, {fixed, 30},
{stretch, 1, 100}, {fixed, 40}]}]),
%% Create input field (label+entry)
gs:label(Frm, [{label, {text,"Module:"}}, {font, Font}, {align, e},
{pack_x, 1}, {pack_y, 2}]),
Ent = gs:entry(Frm, [{text, Mod},
{pack_x, 2}, {pack_y, 2},
{keypress, true}, {setfocus, true},
{buttonpress, true}]),
Entries = [{Ent, atom}],
%% Create a listbox containing the functions of the module
gs:label(Frm, [{label, {text,"Function:"}}, {font, Font}, {align, ne},
{pack_x, 1}, {pack_y, 3}]),
Lb = gs:listbox(Frm, [{bw, 2}, {relief, ridge}, {vscroll, right},
{pack_x, 2}, {pack_y, 3},
{selectmode, multiple}]),
%% Add OK and Cancel buttons
{Wbtn, Hbtn} = dbg_ui_win:min_size(["OK","Cancel"], 70, 30),
Bot = gs:frame(Frm, [{pack_x, {1, 3}}, {pack_y, 4}]),
OK = gs:button(Bot, [{x, Pad}, {y, Pad},
{width, Wbtn}, {height, Hbtn},
{label, {text,"OK"}}, {font, Font}]),
Cancel = gs:button(Bot, [{x, W-Pad-Wbtn}, {y, Pad},
{width, Wbtn}, {height, Hbtn},
{label, {text,"Cancel"}}, {font, Font}]),
Wfrm = gs:read(Frm, width), Hfrm = gs:read(Frm, height),
gs:config(Win, [{width, Wfrm}, {height, Hfrm}, {map, true}]),
#winInfo{type=function, win=Win,
packer=Frm, entries=Entries, trigger=enable,
ok=OK, cancel=Cancel, listbox=Lb, funcs=[]};
create_win(GS, {X, Y}, Type, Mod, Line) ->
Pad = 8,
W = 230,
Font = dbg_ui_win:font(normal),
%% Window
Title = case Type of
line -> "Line Break";
conditional -> "Conditional Break"
end,
Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y},
{destroy, true}]),
%% Create input fields (label+entry)
{Wlbl, Hlbl} = dbg_ui_win:min_size(["C-Function:"], 10, 30),
Went = W-Wlbl-2*Pad,
Labels = case Type of
line ->
[{atom,"Module:",Mod}, {integer,"Line:",Line}];
conditional ->
[{atom,"Module:",Mod}, {integer,"Line:",Line},
{atom,"C-Module:",""}, {atom,"C-Function:",""}]
end,
Fun = fun({DataType, Label, Default}, Yin) ->
gs:create(label, Win, [{x, Pad}, {y, Yin},
{width,Wlbl}, {height,Hlbl},
{label, {text,Label}},
{font, Font}, {align, e}]),
Ent = gs:create(entry, Win, [{x, Pad+Wlbl}, {y, Yin},
{width, Went},
{height, Hlbl},
{text, Default},
{keypress, true}]),
{{Ent, DataType}, Yin+Hlbl}
end,
{Entries, Yacc} = lists:mapfoldl(Fun, Pad, Labels),
{First, _DataType} = hd(Entries),
gs:config(First, [{buttonpress, true}, {setfocus, true}]),
%% Add 'trigger action' buttons
{Wlbl2, Hlbl2} = dbg_ui_win:min_size(["Trigger Action"], 100, 20),
Wfrm = Wlbl2+8, Hfrm = Hlbl2*4+4,
Grp = erlang:now(),
Frm = gs:frame(Win, [{x, W/2-Wfrm/2-2}, {y, Yacc+Pad-2},
{width, Wfrm}, {height, Hfrm}, {bw, 2}]),
gs:label(Frm, [{label, {text, "Trigger Action"}}, {font, Font},
{x, 2}, {y, 0}, {width, Wlbl2}, {height, Hlbl2}]),
gs:radiobutton(Frm, [{label, {text, "Enable"}}, {font, Font},
{x, 10}, {y, Hlbl2},
{width, Wlbl2-10}, {height, Hlbl2},
{align, w}, {group, Grp},
{data, {trigger, enable}},
{select, true}]),
gs:radiobutton(Frm, [{label, {text, "Disable"}}, {font, Font},
{x, 10}, {y, Hlbl2*2},
{width, Wlbl2-10}, {height, Hlbl2},
{align, w}, {group, Grp},
{data, {trigger, disable}}]),
gs:radiobutton(Frm, [{label, {text, "Delete"}}, {font, Font},
{x, 10}, {y, Hlbl2*3},
{width, Wlbl2-10}, {height, Hlbl2},
{align, w}, {group, Grp},
{data, {trigger, delete}}]),
%% Add OK and Cancel buttons
{Wbtn, Hbtn} = dbg_ui_win:min_size(["OK","Cancel"], 70, 30),
Ybtn = Yacc + Pad + Hfrm + Pad,
OK = gs:button(Win, [{x, Pad}, {y, Ybtn},
{width, Wbtn}, {height, Hbtn},
{label, {text,"OK"}}, {font, Font}]),
gs:button(Win, [{x, W-Pad-Wbtn}, {y, Ybtn},
{width, Wbtn}, {height, Hbtn},
{label, {text,"Cancel"}}, {font, Font}]),
Hwin = Ybtn + Hbtn + Pad,
gs:config(Win, [{width, W}, {height, Hwin}, {map, true}]),
#winInfo{type=Type, win=Win,
entries=Entries, trigger=enable, ok=OK}.
%%--------------------------------------------------------------------
%% update_functions(WinInfo, Funcs) -> WinInfo
%% WinInfo = #winInfo{}
%% Funcs = [{Name, Arity}]
%% Name = atom()
%% Arity = integer()
%%--------------------------------------------------------------------
update_functions(WinInfo, Funcs) ->
Items = lists:map(fun([N, A]) -> io_lib:format("~p/~p", [N, A]) end,
Funcs),
gs:config(WinInfo#winInfo.listbox, [{items, Items},
{setfocus, true}]),
WinInfo#winInfo{funcs=Funcs}.
%%--------------------------------------------------------------------
%% handle_event(GSEvent, WinInfo) -> Command
%% GSEvent = {gs, Id, Event, Data, Arg}
%% WinInfo = #winInfo{}
%% Command = ignore
%% | stopped
%% | {win, WinInfo}
%% | {module, Mod}
%% | {break, [[Mod, Line]], Action}
%% | {break, [[Mod, Line, CMod, CFunc]], Action}
%% | {break, [[Mod, Func, Arity]], Action}
%%--------------------------------------------------------------------
handle_event({gs, _Id, destroy, _Data, _Arg}, _WinInfo) ->
stopped;
handle_event({gs, _Id, configure, _Data, [W, H|_]}, WinInfo) ->
gs:config(WinInfo#winInfo.packer, [{width, W-10}, {height, H-10}]),
gs:config(WinInfo#winInfo.cancel, [{x, W-80}]),
ignore;
handle_event({gs, Ent, buttonpress, _,[N,X0,Y0|_]}, WinInfo) when N>1 ->
%% Right (middle) mouse button click in module entry, display a
%% menu containing all interpreted modules
Mods = int:interpreted(),
X = gs:read(Ent, x) + X0,
Y = gs:read(Ent, y) + Y0,
Menu = gs:menu(WinInfo#winInfo.win, [{post_at,{X,Y}}]),
lists:foreach(fun(Mod) ->
gs:menuitem(Menu, [{label,{text,Mod}},
{data,{module,Mod}}])
end,
Mods),
ignore;
handle_event({gs, LB, keypress, window, [Key|_]}, WinInfo) ->
%% Used for functional break window, since listboxes for some
%% reason doesn't generate keypress events
if
Key/='Tab', Key/='Return' ->
ignore;
true ->
handle_event({gs, LB, click, listbox, ["OK"]}, WinInfo)
end;
handle_event({gs, Ent, keypress, Data, [Key|_]}, WinInfo) ->
case WinInfo#winInfo.type of
function when Key/='Tab', Key/='Return' ->
case gs:read(WinInfo#winInfo.listbox, items) of
[] -> ignore;
_Items ->
gs:config(WinInfo#winInfo.listbox, clear),
{win, WinInfo#winInfo{funcs=[]}}
end;
function -> % 'Return' | 'Tab' pressed in Module entry
case check_input(WinInfo#winInfo.entries) of
error -> ignore;
[Mod] -> {module, Mod}
end;
_Type when Key=='Tab'; Key=='Return' ->
case next_entry(Ent, WinInfo#winInfo.entries) of
last ->
gs:config(WinInfo#winInfo.ok, flash),
handle_event({gs, Ent, click, Data, ["OK"]}, WinInfo);
Next ->
gs:config(Next, {setfocus, true}),
ignore
end;
_Type -> ignore
end;
handle_event({gs, _Id, click, _Data, ["OK"|_]}, WinInfo) ->
case check_input(WinInfo#winInfo.entries) of
error -> ignore;
Data when WinInfo#winInfo.type/=function ->
{break, [Data], WinInfo#winInfo.trigger};
[Mod] -> % Function break window
case gs:read(WinInfo#winInfo.listbox, selection) of
[] ->
{module, Mod};
IndexL ->
Funcs = WinInfo#winInfo.funcs,
Breaks =
[[Mod|lists:nth(Index+1, Funcs)] || Index <- IndexL],
{break, Breaks, enable}
end
end;
handle_event({gs, _Id, click, _Data, ["Cancel"|_]}, _WinInfo) ->
stopped;
handle_event({gs, _Id, click, {trigger,Trigger}, _Arg}, WinInfo) ->
{win, WinInfo#winInfo{trigger=Trigger}};
handle_event({gs, _Id, click, {module, Mod}, _Arg}, WinInfo) ->
{Ent, _DataType} = hd(WinInfo#winInfo.entries),
gs:config(Ent, {insert,{0,Mod}}),
ignore;
handle_event(_GSEvent, _WinInfo) ->
ignore.
check_input(Entries) ->
check_input(Entries, []).
check_input([{Entry, Type} | Entries], Data) ->
Str = gs:read(Entry, text),
case erl_scan:string(Str) of
{ok, [{Type, _Line, Val}], _EndLine} ->
check_input(Entries, [Val|Data]);
_Error -> error
end;
check_input([], Data) -> lists:reverse(Data).
next_entry(Entry, [{Entry, _Type}]) ->
last;
next_entry(Entry, [{Entry, _Type1}, {Next, _Type2}|_]) ->
Next;
next_entry(Entry, [_|Entries]) ->
next_entry(Entry, Entries).