function bst_layout(varargin);
%BST_LAYOUT - Manage all BST windows in a standard layout
% function bst_layout(varargin);
%function bst_layout(action,h,trow,tcol,tnum);
% bst_layout with no arguments will "snap" all windows to the proper alignment.
%  Field tiles will be shaped to the nearest obvious size.
%
% bst_layout('align',h) will snap only the given window
% bst_layout('align',h,row,col,num) will snap field tiles in h to
%  the desired row and col format, in the num position, using the matlab
%  convention established in subplot, for sizes 1x1, 1x2, 2x1, and 2x2.
%  If h is empty, then all tile windows will be snapped into that shape.
% Examples:
% bst_layout or bst_layout('align')% clean up all windows in display
% bst_layout('align',figh); % set specific tile or Dynamic Toolbar into place
% bst_layout('align',[],2,2,4); % set all tiles into the lower right position
%
% OTHER Actions:
% bst_layout('close') closes all field tiles but not other windows
% bst_layout('quit') closes all field tiles and message windows, ending BST
% bst_layout('margins') interactively sets the margins of the BST screen
% bst_layout('full') sets the margins to nearly full screen
%
% Explanation
%  BrainStorm has four "tile" types. 
%  The first is the Message Tile, found in the upper right corner.
%
%  Next is the StaticToolbar tile, found in the upper left.
%  The Message and StaticToolbar tiles are always present.
%
%  The third tile type is the DynamicToolbar in the lower left,
%  which is overlaid with various toolbars as driven by the actions.
%
%  The final tile type are the "field tiles" or just "tiles" that fill
%  most of the screen. The field tiles can be arranged 
%  as 1x1, 2x1, 1x2, or 2x2.
%
% The TileType must be written into the figure's appdata using name 'TileType'.
%  Valid types are 'Message','Static','Dynamic','Tile' (only first letter
%  counts, case sensitive).

%<autobegin> ---------------------- 02-Nov-2004 16:14:15 -----------------------
% --------- Automatically Generated Comments Block Using AUTO_COMMENTS ---------
%
% CATEGORY: GUI and Related
%
% Alphabetical list of external functions (non-Matlab):
%   toolbox\bst_layout.m  NOTE: Routine calls itself explicitly
%
% Subfunctions in this file, in order of occurrence in file:
%   ScreenSize = get_screensize;
%   align(hf,trow,tcol,tnum);
%   [winpos,tilepos,allpos] = make_positions(STYLE);
%
% Group : Preference data and their calls in this file:
%   'BrainStorm' : 'Layout'
%   
%   setpref('BrainStorm','Layout',L);
%   setpref('BrainStorm','Layout',Layout);
%   
%   L = getpref('BrainStorm','Layout');
%
% Application data and their calls in this file:
%   'TileType'
%   
%   setappdata(hf(i),'TileType',TileType);
%   
%   TileType = getappdata(hf(i),'TileType');
%
% At Check-in: $Author: Mosher $  $Revision: 26 $  $Date: 11/02/04 2:35p $
%
% This software is part of BrainStorm Toolbox Version 2.0 (Alpha) 02-Nov-2004
% 
% Principal Investigators and Developers:
% ** Richard M. Leahy, PhD, Signal & Image Processing Institute,
%    University of Southern California, Los Angeles, CA
% ** John C. Mosher, PhD, Biophysics Group,
%    Los Alamos National Laboratory, Los Alamos, NM
% ** Sylvain Baillet, PhD, Cognitive Neuroscience & Brain Imaging Laboratory,
%    CNRS, Hopital de la Salpetriere, Paris, France
% 
% See BrainStorm website at http://neuroimage.usc.edu for further information.
% 
% Copyright (c) 2004 BrainStorm by the University of Southern California
% This software distributed  under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPL
% license can be found at http://www.gnu.org/copyleft/gpl.html .
% 
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%<autoend> ------------------------ 02-Nov-2004 16:14:15 -----------------------

% History
% ------------------------------------------------------------
% 13-May-2003 JCM  First build of new layout criterion for BST V2
% 14-May-2003 JCM  Second build moving static and message to the bottom of the window
% 15-May-2003 JCM  Making layout more versatile with its own structure
% 6-Aug-2003  JCM  If the computer screen size changes, the default is reinstated. Added
%                  new field "ScreenSize" to the layout structure
% 11-Nov-2003 JCM  Added new style for static in the upper left
% 29-Sep-2004 EK   Added a new style 'd' for lower left - half size of the dynamic style 
% ------------------------------------------------------------

% look at 'defaults' section for hardwiring

global STYLE_BOTTOM STYLE_TOP

STYLE_BOTTOM = 1; % Our Summer 2003 version, with static on bottom right
STYLE_TOP = 2; % New Fall 2003 version, with static in upper left


STYLE = STYLE_TOP; % hardwire the style version

if(nargin == 0),
    varargin{1} = 'align'; % default action
end

switch varargin{1}
    
case {'default','full'} % same thing
    % set the margins to the full desktop

    % ------------ Hardwiring -------------------

    % ----- define the BrainStorm screen area in the user's desktop --------
    LEFT = .05;
    BOTTOM = .1;
    WIDTH = .94;
    HEIGHT = .89;
    
    % all units are relative to the BrainStorm screen area
    % Message is along left bottom, Static is along right bottom, Dynamic up the right
    MessageWidth = .35; % fraction of screen width
    MessageHeight = .15; % fraction of screen height
        
    DynamicWidth = .2; % fraction of screen margin width;
    DynamicHeight = 1 - MessageHeight; % all available height

    % static is same height, rest of screen across bottom
    StaticHeight = MessageHeight;
    StaticWidth = 1 - MessageWidth;
    
    
    % --------- Hardwire for figure margins --------------
    NameMargin = .032; % adds to the height of a figure
    SideMargin = .0075; % adds to the width
    MenuMargin = .025; % additional height if used
    % see also make_positions below for any possible final tinkering with these values
    

    % -------- end of hardwire, should be automatic below ----------
    
    ScreenSize = get_screensize;

    Layout = struct('ScreenSize',[],... % the 4 param screen size (e.g. [1 1 1024 768]
    'ScreenPosition',[],... % the 4 param location of BST
        'MessageHeight',[],'MessageWidth',[],... % the message window in lower left
        'DynamicHeight',[],'DynamicWidth',[],... % the changing toolbar in upper right
        'StaticHeight',[],'StaticWidth',[],... % lower right
        'SideMargin',[],... % allowance for additional right side space needed
        'NameMargin',[],... % allowance for the Name region at top of windows
        'MenuMargin',[],... % allowance for a file menu at top of window
        'winpos',[],... % calculated by make_positions, true positions
        'tilepos',[],... % calculated by make_positions
        'allpos',[]... % calculated by make_positions
        ); 
    
    Layout.ScreenSize = ScreenSize;
    Layout.ScreenPosition = [LEFT BOTTOM WIDTH HEIGHT];
    Layout.MessageWidth = MessageWidth;
    Layout.MessageHeight = MessageHeight;
    Layout.DynamicWidth = DynamicWidth;
    Layout.DynamicHeight = DynamicHeight;
    Layout.StaticHeight = StaticHeight;
    Layout.StaticWidth = StaticWidth;
    Layout.SideMargin = SideMargin;
    Layout.NameMargin = NameMargin;
    Layout.MenuMargin = MenuMargin;
    
    setpref('BrainStorm','Layout',Layout);
    make_positions(STYLE); % generate new positions from above information, update Layout
    
    bst_layout('align')
    
    
    case {'close','quit','close all'}
        set(0,'ShowHidden','on');
        hf = findobj(0,'type','figure'); % all of the figure handles

        for i = 1:length(hf),
            TileType = getappdata(hf(i),'TileType');
            if(~isempty(TileType)),
                TileType = TileType(1); % only care about first letter
                switch TileType
                    case 'T' % tiles
                        delete(hf(i));
                    case {'D'}
                        if strcmp(varargin{1},'close all'),
                            % closing all dynamic toolbars
                            delete(hf(i))
                        end
                    case {'d'}
                        if strcmp(varargin{1},'close all'),
                            % closing all dynamic toolbars
                            delete(hf(i))
                        end
                    case {'M','S'}
                        if strcmp(varargin{1},'quit'),
                            % closing everything in full shutdown
                            delete(hf(i))
                        end
                end % switch case
            end % if a BST TileType
        end % for all figures

case 'margins'
    % set new margins based on current window setting
    ScreenSize = get_screensize;
    
    if(~ispref('BrainStorm','Layout')),
        bst_layout('default');
    end
    L = getpref('BrainStorm','Layout');
    
    % find the dynamic window
    DynamicPos = get(gcbf,'Position'); % position of calling figure
    
    % find the message window
    set(0,'showhidden','on');
    hm = findobj(0,'Name','Messages');
    MessagePos = get(hm,'position');

    MessageWidth = MessagePos(3) + L.SideMargin;
    MessageHeight = MessagePos(4) + L.NameMargin;
    
    DynamicWidth = DynamicPos(3) + L.SideMargin;
    DynamicHeight = DynamicPos(4) + L.NameMargin;
 
    switch STYLE
    case STYLE_BOTTOM
       
       LEFT = MessagePos(1); % the lower corner of the message window
       BOTTOM = MessagePos(2);
       
       % now use upper right edge of dynamic window for other measurement
       WIDTH = (DynamicPos(1) + DynamicWidth) - LEFT; % the right edge of the dynamic window
       HEIGHT = (DynamicPos(2) + DynamicHeight) - BOTTOM;
       
    case STYLE_TOP
       
       LEFT = DynamicPos(1); % the lower corner of the dynamic window
       BOTTOM = DynamicPos(2);
       
       % use the upper right of the message window
       WIDTH = (MessagePos(1) + MessageWidth) - LEFT; % the right edge of the message window
       HEIGHT = (MessagePos(2) + MessageHeight) - BOTTOM;
       
    end
    
    L.ScreenSize = ScreenSize; % present screen size
    L.ScreenPosition = [LEFT BOTTOM WIDTH HEIGHT];
    
    % now set the size of the message and dynamic windows based on current relative settings
    L.MessageWidth = MessageWidth/WIDTH; % relative width
    L.MessageHeight = MessageHeight/HEIGHT;
    L.StaticHeight = L.MessageHeight; % stays same
    L.StaticWidth = 1 - L.MessageWidth; % rest of space
 
    % dynamic height cannot overlap the static toolbar
    L.DynamicHeight = DynamicHeight/HEIGHT;
    switch STYLE
    case STYLE_BOTTOM
       L.DynamicHeight = min(L.DynamicHeight,(1-L.MessageHeight));
    case STYLE_TOP
       L.DynamicHeight = min(L.DynamicHeight,(1-L.MessageHeight));
    end
    L.DynamicWidth = DynamicWidth/WIDTH;
    
    setpref('BrainStorm','Layout',L);
    make_positions(STYLE); % calculate new positions
    
    bst_layout('align'); % align everything to them
    
    
case {'align','snap'} % same thing
    varargin{1} = 'align'; % same thing either way
    feval(varargin{:});
end





% ------------ screensize in pixels
function ScreenSize = get_screensize;
ScreenUnits = get(0,'units'); % what are the current units
set(0,'units','pixels'); % set to pixels
ScreenSize = get(0,'ScreenSize'); % what is the existing screen size (1x4 vec)
set(0,'units',ScreenUnits); % return to original units




% ----------- align (snap) ------------------
function align(hf,trow,tcol,tnum);

if(~ispref('BrainStorm','Layout')),
    bst_layout('default'); % make it
end

L = getpref('BrainStorm','Layout');

ScreenSize = get_screensize;

% double check that L.ScreenSize exists
if(~isfield(L,'ScreenSize')), % it's missing
    bst_layout('default'); % make new structure
end

if(~all(ScreenSize == L.ScreenSize)), % new screen size
    bst_layout('default'); % make a default
    L = getpref('BrainStorm','Layout'); % new values
end


winpos = L.winpos;
tilepos = L.tilepos;
allpos = L.allpos;

% Retrieve sizes of tiles
FullHeight = tilepos{1,1}(1,4); % full height
HalfHeight = tilepos{2,1}(1,4);
FullWidth = tilepos{1,1}(1,3);
HalfWidth = tilepos{1,2}(1,3);


if (~exist('hf','var')),
    hf = []; % set missing to default
end
if(isempty(hf)),
    % user did not give, so get all of them
    set(0,'ShowHidden','on');
    hf = findobj(0,'type','figure'); % all of the figure handles
end

if(~exist('tnum','var')),
    tnum = []; % set missing to default
end    

for i = 1:length(hf),
    TileType = getappdata(hf(i),'TileType');
    if(~isempty(TileType)),
        % one of the tile windows
        set(hf(i),'units','normalized'); % just in case
        if length(TileType) > 1
           TileType = TileType(1); % only want a single letter
           setappdata(hf(i),'TileType',TileType); % correct it
        end        
        
        switch TileType
        case 'M'
            set(hf(i),'position',winpos(1,:));
        case 'S'
            set(hf(i),'position',winpos(2,:));
        case 'D'
            set(hf(i),'position',winpos(3,:));
        case 'd'
            newType = winpos(3,:);
            newType(4) = newType(4)/1.5;;
            set(hf(i),'position',newType);
            case 'T'
            if(~isempty(tnum)),
                % user requested specific setting
                set(hf(i),'position',tilepos{trow,tcol}(tnum,:));
            else
                % snap the tile into the nearest corner and apparent size
                tpos = get(hf(i),'position');
                % distance to each corner
                tdist = sum((allpos(:,1:2) - repmat(tpos(1:2),9,1)).^2,2);
                [ignore,ipos] = min(tdist); % which is the nearest corner
                
                % is it tall or wide?
                isTall = abs(tpos(4) - FullHeight) < abs(tpos(4) - HalfHeight);
                isWide = abs(tpos(3) - FullWidth) < abs(tpos(3) - HalfWidth);
                
                switch ipos % using matlab subplot convention
                    % set the size dependent on where it is and *maybe*
                    %  on what its Tall and Wide was. Some positions
                    %  must be made shorter or narrower.
                case {1,3,4,8} % all same lower left, same as [223]
                    if isTall & isWide
                        set(hf(i),'position',tilepos{1,1}(1,:));
                    elseif isTall & ~isWide
                        set(hf(i),'position',tilepos{1,2}(1,:));
                    elseif ~isTall & isWide
                        set(hf(i),'position',tilepos{2,1}(2,:));
                    else
                        set(hf(i),'position',tilepos{2,2}(3,:));
                    end
                case {2,6} % same as [221]
                    if isWide,
                        set(hf(i),'position',tilepos{2,1}(1,:));
                    else
                        set(hf(i),'position',tilepos{2,2}(1,:));
                    end
                case 7 % same as [222]
                    % must be made not wide and not tall
                    set(hf(i),'position',tilepos{2,2}(2,:));
                case {5,9} % same as [224]
                    if isTall
                        set(hf(i),'position',tilepos{1,2}(2,:));
                    else
                        set(hf(i),'position',tilepos{2,2}(4,:));
                    end
                end % setting the final position and shape
                
                
            end % if snapping or setting the position
        end % for the case of Tiles
        
        % if the menu bar is on, then subtract a little from the height
        MENUBAR = 0; % default is to assume no menubar
        if(strcmp(get(hf(i),'menubar'),'figure')),
            MENUBAR = 1;
        end
        if(~isempty(findobj(get(hf(i),'children'),'flat','type','uimenu'))),
            % one or more children is a uimenu, assume a menubar in place
            MENUBAR = 1;
        end
        if(MENUBAR), % if there is a menubar, allow for space
            final_pos = get(hf(i),'position'); % the final position of this window
            set(hf(i),'position',[final_pos(1:3) ...
                    final_pos(4)-L.MenuMargin]);
        end
        
    end % if it is one of our BrainStorm tiles in the workspace
end % for each of the figure windows

set(0,'ShowHidden','off'); % hide them again



% ---------------- Function to make position information ---------
function [winpos,tilepos,allpos] = make_positions(STYLE);
% Given Layout information, generate the actual positions of the windows and tiles
% winpos is the window position of the message, static, and dynamic windows
% tilepos is the positions for the different types of window sizes
% allpos is concatenation of all of them for convenience

global STYLE_BOTTOM STYLE_TOP % set in the above function

% index numbers for types of windows, for readability
Message = 1; 
Static = 2;
Dynamic = 3;

% Matlab's set('pos') function is set to normalized units. On a 1024 x 768
%  screen size, the name region and menu region each add .025 to the size,
%  i.e. set('pos') only takes into account the region below this.
% similarly the width is slightly wider

if(~ispref('BrainStorm','Layout')),
    bst_layout('Default')
end

L = getpref('BrainStorm','Layout');

ScreenSize = get_screensize;

% double check that L.ScreenSize exists
if(~isfield(L,'ScreenSize')), % it's missing
    bst_layout('default'); % make new structure
end


if(~all(ScreenSize == L.ScreenSize)), % new screen size
    bst_layout('default'); % make a default
    L = getpref('BrainStorm','Layout'); % new values
end


% Let each type be possibly unique for final manipulations
topmargin_tile = L.NameMargin; % total top margin to allow for each tile's height
sidemargin_tile = L.SideMargin; % additional right side for each tile's width

topmargin_toolbar = L.NameMargin; % for active and static toolbars
sidemargin_toolbar = L.SideMargin;

topmargin_message = L.NameMargin; % for message window
sidemargin_message = L.SideMargin;


% define the full BrainStorm area on the screen
BSTx = L.ScreenPosition(1); % lower corner
BSTy = L.ScreenPosition(2);
BSTwidth = L.ScreenPosition(3); % usuable width
BSTheight = L.ScreenPosition(4); % usuable height

% now scale requests into the usuable area
MessageWidth = L.MessageWidth*BSTwidth; % actual width
MessageHeight = L.MessageHeight*BSTheight; % actual height
DynamicWidth = L.DynamicWidth*BSTwidth;
DynamicHeight = L.DynamicHeight*BSTheight;
StaticWidth = L.StaticWidth*BSTwidth;
StaticHeight = L.StaticHeight*BSTheight;

winpos = zeros(3,4); % lower x, lower y, width, height, all normalized
% Tile Positions in the Field
tilepos = cell(2); % 2 x 2 cell array

switch STYLE
case STYLE_BOTTOM
   
   % Message window across the bottom, to the Dynamic Toolbar
   winpos(Message,:) = [BSTx, BSTy, ...
         MessageWidth - sidemargin_message, MessageHeight - topmargin_message];
   
   % Static Toolbar to right of Message
   winpos(Static,:) = [BSTx + MessageWidth, BSTy,...
         StaticWidth-sidemargin_toolbar,... % remaining width
         StaticHeight-topmargin_toolbar]; % height
   
   % Dynamic ToolBar up the right side above Dynamic, at top of field
   winpos(Dynamic,:) = [BSTx + BSTwidth - DynamicWidth,...
         BSTy + BSTheight - DynamicHeight,...
         DynamicWidth-sidemargin_toolbar,DynamicHeight-topmargin_toolbar];
   
   % the available full tile field
   fieldx = BSTx;
   fieldy = BSTy + max(MessageHeight,StaticHeight); % just above Message or Static
   
case STYLE_TOP
    
   % Message window across the Top upper right
   winpos(Message,:) = [BSTx + StaticWidth, ...
         BSTy + BSTheight - MessageHeight, MessageWidth - sidemargin_message, MessageHeight - topmargin_message];
   
   % Static Toolbar in upper left
   winpos(Static,:) = [BSTx, BSTy + BSTheight - StaticHeight,...
         StaticWidth-sidemargin_toolbar,... % remaining width
         StaticHeight-topmargin_toolbar]; % height
   
   % Dynamic ToolBar up the right side above Dynamic, at top of field
   winpos(Dynamic,:) = [BSTx,...
         BSTy,...
         DynamicWidth-sidemargin_toolbar,DynamicHeight-topmargin_toolbar];
   
   % the available full tile field
   fieldx = BSTx + DynamicWidth;
   fieldy = BSTy; % just above Message or Static
   
end
  
fieldwidth = BSTwidth - DynamicWidth; % as wide as Message
fieldheight = BSTheight - max(MessageHeight,StaticHeight); % full remaining space above Message
% not including margins, this is just the field

% a single large tile
tilewidth = fieldwidth - sidemargin_tile;
tileheight = fieldheight - topmargin_tile;
tilepos{1,1} = [fieldx,fieldy,tilewidth,tileheight];

% two wide tiles, i.e. 2 rows by 1 col
% first window, second window
tilewidth = fieldwidth - sidemargin_tile;
tileheight = fieldheight/2 - topmargin_tile;

tilepos{2,1} = zeros(2,4);
tilepos{2,1} = [fieldx,fieldy + fieldheight/2,tilewidth,tileheight;...
        fieldx,fieldy,tilewidth,tileheight];

% two tall tiles, 1,2
tilewidth = fieldwidth/2 - sidemargin_tile;
tileheight = fieldheight - topmargin_tile;
tilepos{1,2} = zeros(2,4);
tilepos{1,2} = [fieldx,fieldy,tilewidth,tileheight;...
        fieldx + fieldwidth/2,fieldy,tilewidth,tileheight];

% four tiles, 2,2
tilewidth = fieldwidth/2 - sidemargin_tile;
tileheight = fieldheight/2 - topmargin_tile;
tilepos{2,2} = zeros(4,4);
tilepos{2,2} = [fieldx,fieldy + fieldheight/2,tilewidth,tileheight;...
        fieldx + fieldwidth/2,fieldy + fieldheight/2,tilewidth,tileheight;...
        fieldx, fieldy, tilewidth, tileheight;...
        fieldx + fieldwidth/2,fieldy, tilewidth, tileheight];

% ------- End of creating winpos and tilepos matrices

% allpos is a handy matrix for the positions of all field tiles
allpos = zeros(9,4);
allpos(1,:) = tilepos{1};
allpos(2:3,:) = tilepos{2,1};
allpos(4:5,:) = tilepos{1,2};
allpos(6:9,:) = tilepos{2,2};

L.winpos = winpos;
L.tilepos = tilepos;
L.allpos = allpos;

setpref('BrainStorm','Layout',L); % write back into structure
