%% ``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 via the world wide web 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.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
-module(toolbar_graphics).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Erlang Toolbar
%
%%% Description %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Toolbar graphics.
% The Toolbar window looks something like this:
%
%  |-----------------------------|
%  | File Tools             Help |
%  |-----------------------------|
%  | |-----|  |-----|   |-----|  |
%  | |     |  |     |   |     |  |
%  | |Icon1|  |Icon2|...|IconN|  |
%  | |-----|  |-----|   |-----|  |
%  |-----------------------------|
%  | Help text area              |
%  |-----------------------------|
%
%%% Includes %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-include("toolbar.hrl").
%
%%% Internal data structures %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Toolbar window record
-record(tbwindow,
	{window,menubar,canvas,labelframe,
	 label,helpmenu,
	 no_of_buttons,
	 min_height,min_width,cur_height,icons}).
%
%%% Constants %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Window width
-define(width,215).
%
% Icon width and height
-define(icon,34).
%
% Margin around icons
-define(pad,0).
%
% Default label width and height
-define(wlabel,50).
-define(hlabel,15).
%
% Default button width and height
-define(wbutton,50).
-define(hbutton,30).
%
%%% Exports %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-export([event/4]).
-export([draw_window/1,redraw_window/2,already_added/2,add_icon/2]).
-export([get_window/1]).
-export([cursor/2]).
-export([listen_configure/1]).
-export([display_show/2,display_clear/1]).
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%% Exported functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%----------------------------------------
% event(Data,GsEvent,Data,Args) => Event
%   Data - term()
%   GsEvent - GS event
%   Data, Args - Data and Arg fields associated with the GS event
%   Event - {display,String} | display_clear | noevent | {start,{M,F,A}} |
%           update_toolbar | create_tool_file | add_gs_contribs |
%           {help,Html} | about_help | {redraw,{Width,Height}} | quit |
%           {newData,NewData}
%   String - string()
%   M, F - atom() Module and function name
%   A - [term()] Function argument
%   Html - string() HTML file | nofile
%   Width, Height - integer()
%   NewData - term()
% Tries to convert a GS event to an internal toolbar event. The separation
% is intented to keep the implementation details of the graphics hidden
% for toolbar.erl. Pure graphical events triggered by the GS event will
% be executed by this function.
% The Data field is used for saving information between different events
% (without having to use put/get or ets). Right now it is only used to save
% the coordinates of the last canvasbutton pressed, making it possible to
% check if the canvasbutton is released with the mouse moved outside the
% button (= no action) or with the mouse still inside the button (= action).
%----------------------------------------
%%% Mouse enters a icon, display short help message
event(_LoopData,enter,{canvasbutton,_Cbtn,{_Start,{message,String}}},_Args) ->
    {display,String};

%% Mouse leaves a icon, clear display area
event(_LoopData,leave,{canvasbutton,_Cbtn,_Data},_Args) ->
    display_clear;

%% An icon is pressed, create graphical illusion of this
event(_LoopData,buttonpress,{canvasbutton,Canvasbutton,_},_Args) ->
    canvasbutton:press(Canvasbutton),
    {newData,canvasbutton:read(Canvasbutton,coords)};

%% An icon is released, create graphical illusion of this
event(LoopData,buttonrelease,{canvasbutton,Cbtn,{{start,Start},_Msg}},
      [_,X,Y|_]) ->
    canvasbutton:release(Cbtn),
    case within(X,Y,LoopData) of
	true ->
	    {start,Start};
	false ->
	    noevent
    end;

%%% Update Toolbar button pressed
event(_LoopData,click,_Data,["Update Toolbar"|_]) ->
    update_toolbar;

%%% Tool configuration button pressed
event(_LoopData,click,_Data,["Create Tool File..."|_]) ->
    create_tool_file;

%%% Add GS contribution button pressed
event(_LoopData,click,_Data,["Add GS Contributions"|_]) ->
    add_gs_contribs;

%%% Help menu button selected
event(_LoopData,click,{help,Html},_Args) ->
    {help,Html};

%%% About Help menu button selected
event(_LoopData,click,about_help,_Args) ->
    about_help;

%% Window resized, redraw it
event(_LoopData,configure,_Data,[Width,Height|_]) ->
    {redraw,{Width,Height}};
	    
%%% Quit button pressed
event(_LoopData,click,_Data,["Quit"|_]) ->
    quit;

%%% Window closed
event(_LoopData,destroy,_Data,_Args) ->
    quit;

event(_LoopData,_GsEvent,_Data,_Args) ->
    noevent.

%=============================================================================
% Main window functions
%=============================================================================

%----------------------------------------
% draw_window(S) => Window
%   S - pid() GS
%   Window - tbwindow record
% This functions create the main window, initially without any tool icons
%----------------------------------------
draw_window(S) ->

    Norm = ?icon + 2*?pad,

    %% Main window
    Win = gs:create(window,S,[{title,"Erlang Tools"},{width,?width}]),

    %% Menu bar with menu buttons
    Menubar = gs:create(menubar,Win,[]),

    %% File menu
    File = gs:create(menubutton,Menubar,[{label,{text,"File"}},{side,left}]),
    FileM = gs:create(menu,File,[]),
    gs:create(menuitem,FileM,[{label,{text,"Update Toolbar"}}]),
    gs:create(menuitem,FileM,[{label,{text,"Quit"}}]),

    %% Tools menu
    Tool = gs:create(menubutton,Menubar,[{label,{text,"Tools"}},{side,left}]),
    ToolM = gs:create(menu,Tool,[]),
    gs:create(menuitem,ToolM,[{label,{text,"Create Tool File..."}}]),
    gs:create(menuitem,ToolM,[{label,{text,"Add GS Contributions"}}]),

    %% Help menu
    Help = gs:create(menubutton,Menubar,[{label,{text,"Help"}},{side,right}]),
    HelpM = gs:create(menu,Help,[]),
    gs:create(menuitem,HelpM,[{label,{text,"About..."}},
			      {data,about_help}]),
    gs:create(menuitem,HelpM,[{label,{text,"Toolbar"}},
			      {data,{help,toolbar_lib:help_file()}}]),
    gs:create(menuitem,HelpM,[{label,{text,"OTP"}},
			      {data,{help,toolbar_lib:otp_file()}}]),
    gs:create(menuitem,HelpM,[{itemtype,separator}]),
    
    %% Check height of menu bar
    H = gs:read(Menubar,height),

    %% Now the height of the window can be computed
    Height = H+Norm+?hlabel+2*?pad,
    gs:config(Win,{height,Height}),

    %% Canvas, here will the Tool canvasbuttons be inserted
    Canvas = gs:create(canvas,Win,[{width,?width},{height,Norm},{x,0},{y,H}]),

    %% Label for displaying help messages and the frame containing it
    LabelF = gs:create(frame,Win,[{bg,green},{bw,1},
				  {width,?width},{height,?hlabel+2*?pad},
				  {x,0},{y,H+Norm}]),
    Label = gs:create(label,LabelF,[{align,w},{height,?hlabel},
				    {width,?width},{x,?pad},{y,?pad},
				    {label,{text,string:copies(" ",30)}}]),

    gs:config(Win,{map,true}),

    #tbwindow{window=Win,
	      menubar=Menubar,canvas=Canvas,labelframe=LabelF,
	      label=Label,helpmenu=HelpM,
	      no_of_buttons=0,
	      min_height=Height,min_width=?width,cur_height=Height,
	      icons=[]}.

%----------------------------------------
% redraw_window(Window,{NewWidth,NewHeight}) => NewWindow
%   Window, NewWindow - tbwindow record
%   NewWidth, NewHeight - integer()
% Redraw main window contents according to a new size
%----------------------------------------
redraw_window(Window,{NewWidth,NewHeight}) ->
    
    MinWidth = Window#tbwindow.min_width,
    if
	NewWidth=<MinWidth ->
	    true;
	true ->
	    gs:config(Window#tbwindow.canvas,{width,NewWidth}),
	    gs:config(Window#tbwindow.labelframe,{width,NewWidth}),
	    gs:config(Window#tbwindow.label,{width,NewWidth-2*?pad})
    end,

    MinHeight = Window#tbwindow.min_height,
    if
	NewHeight=<MinHeight ->
	    Window;
	true ->

	    %% Compute size difference
	    Diff = NewHeight - Window#tbwindow.cur_height,

	    %% Resize button frame
	    Canvas = Window#tbwindow.canvas,
	    gs:config(Canvas,{height,gs:read(Canvas,height)+Diff}),

	    %% Move label frame accordingly
	    LabelF = Window#tbwindow.labelframe,
	    gs:config(LabelF,{y,gs:read(LabelF,y)+Diff}),

	    %% Return updated tbwindow record
	    Window#tbwindow{cur_height=NewHeight}
    end.

%----------------------------------------
% already_added(Window,ToolInfo) => true | false
%   Window - tbwindow record
%   ToolInfo - toolinfo record
% Returns true if ToolInfo contains information about a tool that
% is already included in Window
%----------------------------------------
already_added(Window,ToolInfo) ->
    already_added2(Window#tbwindow.icons,ToolInfo#toolinfo.tool).

%----------------------------------------
% already_added2(ToolInfos,Tool) => true | false
%   ToolInfos - [toolinfo record]
%   Tool - atom() Tool name
%----------------------------------------
already_added2([#toolinfo{tool=Tool}|_Rest],Tool) ->
    true;
already_added2([_|Rest],Tool) ->
    already_added2(Rest,Tool);
already_added2([],_ToolInfo) ->
    false.

%----------------------------------------
% add_icon(Window,ToolInfo) => NewWindow
%   Window, NewWindow - tbwindow record
%   ToolInfo - toolinfo record
% Add an icon to the main window
%----------------------------------------
add_icon(Window,ToolInfo) ->
    Norm = ?icon + 2*?pad,
    
    %% Extend window if necessary
    N = Window#tbwindow.no_of_buttons,
    ReqWidth = N*Norm+Norm,
    CurWidth = gs:read(Window#tbwindow.window,width),
    if
	ReqWidth>CurWidth ->
	    gs:config(Window#tbwindow.window,{width,ReqWidth}),
	    gs:config(Window#tbwindow.canvas,{width,ReqWidth}),
	    gs:config(Window#tbwindow.labelframe,{width,ReqWidth}),
	    gs:config(Window#tbwindow.label,{width,ReqWidth-2*?pad});
	true ->
	    true
    end,
    
    %% Insert icon into button frame
    canvasbutton:create(Window#tbwindow.canvas,
			[{image,ToolInfo#toolinfo.icon},
			 {x,N*Norm+?pad},{y,?pad},
			 {width,?icon},{height,?icon},
			 {data,{{start,ToolInfo#toolinfo.start},
				{message,ToolInfo#toolinfo.message}}}]),
    
    %% Insert tool name into help menu (if there is any help available)
    case ToolInfo#toolinfo.html of 
	nofile ->
	    true;
	Html ->
	    gs:create(menuitem,Window#tbwindow.helpmenu,
		      [{label,{text,ToolInfo#toolinfo.tool}},
		       {data,{help,Html}}])
    end,
    
    MinWidth = gs:read(Window#tbwindow.window,width),
    Window#tbwindow{no_of_buttons=N+1,min_width=MinWidth,
		    icons=[ToolInfo|Window#tbwindow.icons]}.

%----------------------------------------
% get_window(Window) -> gs_obj()
%   Window - tbwindow record
%----------------------------------------
get_window(Window) ->    
    Window#tbwindow.window.

%----------------------------------------
% cursor(Window,Cursor)
%   Window - tbwindow record
%   Cursor - arrow | busy
%----------------------------------------
cursor(Window,Cursor) ->
    gs:config(Window#tbwindow.window,{cursor,Cursor}).

%----------------------------------------
% listen_configure(Window)
%   Window - tbwindow record
% Configure Window to listen for configure events
%----------------------------------------
listen_configure(Window) ->
    gs:config(Window#tbwindow.window,{configure,true}).

%----------------------------------------
% display_show(Window,Text)
%   Window - tbwindow record
%   Text - string()
% Display text in the help text area
%----------------------------------------
display_show(Window,Text) ->
    gs:config(Window#tbwindow.label,{label,{text,Text}}).

%----------------------------------------
% display_clear(Window)
%   Window - tbwindow record
% Clear the help text area
%----------------------------------------
display_clear(Window) ->
    display_show(Window,"").

%%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%----------------------------------------
% within(X,Y,[{Left,Top},{Right,Bot}]) => true | false
% Return true if {X,Y} is within the given rectangle.
%----------------------------------------
within(X,Y,[{L,T},{R,B}]) ->
    if
	X>=L,
	X=<R,
	Y>=T,
	Y=<B ->
	    true;
	true ->
	    false
    end.
