%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2010. 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_win).
%% External exports
-export([init/0,
font/1, min_size/3, min_size/4,
create_menus/2, select/2, selected/1,
add_break/2, update_break/2, delete_break/1,
motion/2
]).
-record(break, {mb, smi, emi, dimi, demi}).
%%====================================================================
%% External exports
%%====================================================================
%%--------------------------------------------------------------------
%% init() -> GS
%% GS = term()
%%--------------------------------------------------------------------
init() ->
gs:start([{kernel, true}]).
%%--------------------------------------------------------------------
%% font(Style) -> Font
%% Style = normal | bold
%% Select a suitable font. Defaults to {screen,12} and, if it does not
%% exist, {courier,12}.
%%--------------------------------------------------------------------
font(Style) ->
GS = init(),
Style2 = if
Style =:= normal -> [];
true -> [Style]
end,
case gs:read(GS, {choose_font, {screen,Style2,12}}) of
Font when element(1, Font) =:= screen ->
Font;
_ ->
gs:read(GS, {choose_font, {courier,Style2,12}})
end.
%%--------------------------------------------------------------------
%% min_size(Strings, MinW, MinH) -> {W, H}
%% min_size(Font, Strings, MinW, MinH) -> {W, H}
%% Font = GS font - defaults to dbg_ui_win:font(normal)
%% Strings = [string()]
%% MinW = MinH = int()
%% W = H = int()
%%--------------------------------------------------------------------
min_size(Strings, MinW, MinH) ->
min_size(font(normal), Strings, MinW, MinH).
min_size(Font, Strings, MinW, MinH) ->
GS = init(),
min_size(GS, Font, Strings, MinW, MinH).
min_size(GS, Font, [String|Strings], MinW, MinH) ->
{W, H} = gs:read(GS, {font_wh, {Font, String}}),
min_size(GS, Font, Strings, erlang:max(MinW, W), erlang:max(MinH, H));
min_size(_GS, _Font, [], W, H) ->
{W, H}.
%%--------------------------------------------------------------------
%% create_menus(MenuBar, [Menu])
%% MenuBar = gsobj()
%% Menu = {Name, [Item]}
%% Name = atom()
%% Item = {Name, N} | {Name, N, Type} | {Name, N, cascade, [Item]}
%% | separator
%% N = no | integer()
%% Type = check | radio
%% Create the specified menus and menuitems.
%%
%% Normal menuitems are specified as {Name, N}. Generates the event:
%% {gs, _Id, click, {menuitem, Name}, _Arg}
%%
%% Check and radio menuitems are specified as {Name, N, check|radio}.
%% They are assumed to be children to a cascade menuitem! (And all children
%% to one cascade menuitem are assumed to be either check OR radio
%% menuitems)!
%% Selecting a check/radio menuitem generates the event:
%% {gs, _Id, click, {menu, Menu}, _Arg}
%% where Menu is the name of the parent, the cascade menuitem.
%% Use selected(Menu) to retrieve which check/radio menuitems are
%% selected.
%%--------------------------------------------------------------------
create_menus(MenuBar, [{Title, Items}|Menus]) ->
Title2 = " "++(atom_to_list(Title))++" ",
MenuBtn = gs:menubutton(MenuBar, [{label, {text,Title2}},
{font, font(normal)}]),
case Title of
'Help' -> gs:config(MenuBtn, {side, right});
_ -> ignore
end,
Menu = gs:menu(Title, MenuBtn, []),
create_items(Menu, Items, Title),
create_menus(MenuBar, Menus);
create_menus(_MenuBar, []) ->
done.
create_items(Menu, [Item|Items], Group) ->
create_item(Menu, Item, Group),
create_items(Menu, Items, Group);
create_items(_Menu, [], _Group) ->
done.
create_item(Menu, {Name, _N, cascade, Items}, _Group) ->
MenuBtn = gs:menuitem(Menu, [{label, {text,Name}},
{font, font(normal)},
{itemtype, cascade}]),
SubMenu = gs:menu(Name, MenuBtn, []),
create_items(SubMenu, Items, Name);
create_item(Menu, separator, _Group) ->
gs:menuitem(Menu, [{itemtype, separator}]);
create_item(Menu, MenuItem, Group) ->
Options = case MenuItem of
{Name, N} ->
[{data, {menuitem,Name}}];
{Name, N, check} ->
[{itemtype, check}, {data, {menu, Group}}];
{Name, N, radio} ->
[{itemtype, radio}, {data, {menu, Group}},
{group, group(Group)}]
end,
gs:menuitem(Name, Menu, [{label, {text,Name}},
{font, font(normal)} | Options]),
if
is_integer(N) -> gs:config(Name, {underline, N});
true -> ignore
end.
%% When grouping radio buttons, the group id must be an atom unique for
%% each window.
group(Group) ->
list_to_atom(atom_to_list(Group)++pid_to_list(self())).
%%--------------------------------------------------------------------
%% select(MenuItem, Bool)
%% MenuItem = atom()
%% Bool = boolean()
%%--------------------------------------------------------------------
select(MenuItem, Bool) ->
gs:config(MenuItem, {select, Bool}).
%%--------------------------------------------------------------------
%% selected(Menu) -> [Name]
%% Menu = Name = atom()
%%--------------------------------------------------------------------
selected(Menu) ->
Children = gs:read(Menu, children),
Selected = [gs:read(Child, select) || Child <- Children],
lists:map(fun(Child) ->
{text, Name} = gs:read(Child, label),
list_to_atom(Name)
end,
Selected).
%%--------------------------------------------------------------------
%% add_break(Name, Point) -> #break{}
%% Name = atom()
%% Point = {Mod, Line}
%% The break will generate the following events:
%% {gs, _Id, click, {break, Point, Event}, _Arg}
%% Event = delete | {trigger, Action} | {status, Status}
%% Action = enable | disable | delete
%% Status = active | inactive
%%--------------------------------------------------------------------
add_break(Menu, Point) ->
Font = font(normal),
%% Create a name for the breakpoint
{Mod, Line} = Point,
Label = io_lib:format("~w ~5w", [Mod, Line]),
%% Create a menu for the breakpoint
MenuBtn = gs:menuitem(Menu, [{label, {text,Label}}, {font, Font},
{itemtype, cascade}]),
SubMenu = gs:menu(MenuBtn, []),
SMI = gs:menuitem(SubMenu, [{data, {break,Point,null}}]),
gs:menuitem(SubMenu, [{label, {text,"Delete"}}, {font, Font},
{data, {break,Point,delete}}]),
TriggerMenuBtn = gs:menuitem(SubMenu,
[{label,{text,"Trigger Action"}},
{font, Font},
{itemtype, cascade}]),
TriggerMenu = gs:menu(TriggerMenuBtn, []),
Group = element(3, erlang:now()),
EMI = gs:menuitem(TriggerMenu, [{label, {text,"Enable"}},
{font, Font},
{itemtype, radio}, {group, Group},
{data,
{break,Point,{trigger,enable}}}]),
DiMI = gs:menuitem(TriggerMenu, [{label, {text,"Disable"}},
{font, Font},
{itemtype, radio}, {group, Group},
{data,
{break,Point,{trigger,disable}}}]),
DeMI = gs:menuitem(TriggerMenu, [{label, {text,"Delete"}},
{font, Font},
{itemtype, radio}, {group, Group},
{data,
{break,Point,{trigger,delete}}}]),
#break{mb=MenuBtn, smi=SMI, emi=EMI, dimi=DiMI, demi=DeMI}.
%%--------------------------------------------------------------------
%% update_break(Break, Options)
%% Break = #break{}
%% Options = [Status, Action, Mods, Cond]
%% Status = active | inactive
%% Action = enable | disable | delete
%% Mods = null (not used)
%% Cond = null | {Mod, Func}
%%--------------------------------------------------------------------
update_break(Break, Options) ->
[Status, Trigger|_] = Options,
{break, Point, _Status} = gs:read(Break#break.smi, data),
{Label, Data} = case Status of
active ->
{"Disable", {break,Point,{status,inactive}}};
inactive ->
{"Enable", {break,Point,{status,active}}}
end,
gs:config(Break#break.smi, [{label, {text,Label}},
{font, font(normal)},
{data, Data}]),
TriggerMI = case Trigger of
enable -> Break#break.emi;
disable -> Break#break.dimi;
delete -> Break#break.demi
end,
gs:config(TriggerMI, {select, true}).
%%--------------------------------------------------------------------
%% delete_break(Break)
%% Break = #break{}
%%--------------------------------------------------------------------
delete_break(Break) ->
gs:destroy(Break#break.mb).
%%--------------------------------------------------------------------
%% motion(X, Y) -> {X, Y}
%% X = Y = integer()
%%--------------------------------------------------------------------
motion(X, Y) ->
receive
{gs, _Id, motion, _Data, [NX,NY]} ->
motion(NX, NY)
after 0 ->
{X, Y}
end.