%%
%% %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.