function chaingui(action)
%CHAINGUI Chain matrix multiply optimization GUI.
%   CHAINGUI performs optimization of a specified matrix multiply
%   chain by determining the parenthesization of multiplicands that
%   best minimizes two metrics:
%   - the maximum size of all temporary matrices that must be
%     stored in memory at any time during the evaluation, and
%   - the total number of floating point operations (flops)
%     required to compute the result.
%
%   The GUI allows you to specify an N-element cell array,
%   where each entry in the array contains the size of each
%   matrix in the multiplication chain.  For the chain multiply
%   A*B*C, the input argument could be specified as
%   "{size(A) size(B) size(C)}" assuming that A, B, and C are
%   matrices in the workspace.
%
%   Alternatively, if the "Specify MATLAB Expression" menu item
%   is selected from the options menu, the program accepts simple
%   MATLAB expressions indicating a chain multiplication. For
%   example, "U'*V*U" or "rand(3,5)*magic(5)*rand(5)".
%
%   See the context-sensitive help system (accessed by a right
%   mouse click while hovering over a GUI element) for more
%   details on program features.
%
% See also CHAINMULT.

% Author: D. Orofino
% Copyright 1984-2000 The MathWorks, Inc.
% $Revision: 1.9 $ $Date: 2000/06/01 03:16:20 $

InitGUI;


% ---------------------------------------------------------------
function reuse = ReuseOutput(ud)
% Determine if user output buffer may be reused
% Returns true if menu option checked

reuse = strcmpi(get(ud.h.ReuseOutput,'checked'),'on');


% ---------------------------------------------------------------
function str = ReuseOutputOpt(ud)

if ReuseOutput(ud),
   str='reuse';
else
   str='noreuse';
end


% ---------------------------------------------------------------
function ConfigGUI_ReuseState(ud, reuseIsOn)

if nargin<2,
    reuseIsOn = ReuseOutput(ud);
end

% Get list of handles to this menu AND all peer context
% menu items:
h = get(ud.h.ReuseOutput,'userdata');
if reuseIsOn, newState='on'; else newState='off'; end
set(h,'checked',newState);

% Update toolbar button:
set(ud.h.ReuseOutputButton,'state',newState);

UpdateGUI(ud.h.Fig);


% ---------------------------------------------------------------
function ReuseOutputToggle(hco, eventStruct)
% Toggle state of ReuseOutput menu option

hfig = gcbf;
ud = get(hfig,'userdata');
ConfigGUI_ReuseState(ud, ~ReuseOutput(ud));


% ---------------------------------------------------------------
function mode = isSizeMode(ud)
% Determine user input mode from checked menu item
% Returns true if in "matrix size list" mode,
% false if MATLAB "expression" mode

mode = strcmpi(get(ud.h.SpecifySizes,'checked'),'on');


% ---------------------------------------------------------------
function ConfigGUI_SizeAndExpressionState(ud, sizeMode)

if nargin<2, sizeMode = isSizeMode(ud); end

% Configure prompts:
%
if sizeMode,
   % Matrix size mode:
   prompt = 'Matrix size list:';
   RandomEna = 'on';
else
   % MATLAB Expression mode:
   prompt = 'Expression:';
   RandomEna = 'off';
end
set(ud.h.MatrixSizesText,'string',prompt);
set([ud.h.RandomButton ud.h.RandomMenu],'enable',RandomEna);

% Update size/expression menu checks and button states:
%
h=get(ud.h.SpecifyExpression,'userdata');
hExpr = h(1:2);
hSizes = h(3:4);
if sizeMode,
   set(hSizes,'checked','on');
   set(ud.h.SpecifySizesButton,'state','on');
   
   set(hExpr,'checked','off');
   set(ud.h.SpecifyExpressionButton,'state','off');
   
else
   set(hExpr,'checked','on');
   set(ud.h.SpecifyExpressionButton,'state','on');
   
   set(hSizes,'checked','off');
   set(ud.h.SpecifySizesButton,'state','off');
end


% ---------------------------------------------------------------
function SpecifyExpression(hco, eventStruct, hfig)

if nargin<3, hfig = gcbf; end
ud = get(hfig,'userdata');
sizeMode = isSizeMode(ud);

% Always put into selected mode, in case toggle
% button pressed a second time:
ConfigGUI_SizeAndExpressionState(ud, 0);

% If already in this mode, skip out:
if ~sizeMode, return; end

% Put in default expression:
%
defstr='rand(4,5)*magic(5)*rand(5,3)';
set(ud.h.MatrixSizes,'string',defstr);
UpdateGUI(ud.h.Fig);


% ---------------------------------------------------------------
function SpecifySizes(hco, eventStruct, hfig)

if nargin<3, hfig = gcbf; end
ud = get(hfig,'userdata');
sizeMode = isSizeMode(ud);

ConfigGUI_SizeAndExpressionState(ud, 1);

% If already in this mode, skip out:
if sizeMode, return; end

% Put in default expression:
%
ChangeRandomMatrixSizes;

%defstr='{[4 5][5 5][5 3]}';
%set(ud.h.MatrixSizes,'string',defstr);
%UpdateGUI(ud.h.Fig);


% ---------------------------------------------------------------
function ConfigGUIforUserSpec(hfig)
% Configure the GUI for the selected user specification mode
% The two modes are:
%  - Specify MATLAB Expression, and
%  - Specify Matrix Sizes

ud = get(hfig,'userdata');
ConfigGUI_SizeAndExpressionState(ud);
ConfigGUI_ReuseState(ud);


% ---------------------------------------------------------------
function ChangeRandomMatrixSizes(hco, eventStruct)
% Generate list of random matrix sizes,
% then perform optimization.

hfig = gcbf;
ud = get(hfig,'userdata');

% Generate random integers in range [2,5].
% This yields 2 to 5 matrix size entries:
N = 2+round(4*rand+.5);
% Generate random size elements in range [1,20]
S = 1+round(19 * rand(1,N));
xstr='{';
for i=1:N-1,
   xstr=[xstr mat2str(S([i i+1])) ',' ];
end
xstr=[xstr(1:end-1) '}'];

% Put string into GUI, as if user typed expression:
set(ud.h.MatrixSizes,'string',xstr);

% Perform optimization:
UpdateGUI(ud.h.Fig);


% ---------------------------------------------------------------
function ChangeMatrixSizes(hco, eventStruct)

hfig = gcbf;
ud = get(hfig,'userdata');
UpdateGUI(ud.h.Fig);


% ---------------------------------------------------------------
function ListCallback(hco, eventStruct, hfig)

if nargin<3, hfig = gcbf; end

ud = get(hfig,'userdata');
i = get(ud.h.ResultList,'value');
if i==1,
   ena='off';
else
   ena='on';
end
set([ud.h.BuildButton ud.h.BuildMenu],'enable',ena);


% ---------------------------------------------------------------
function GenProgram(hco, eventStruct)
% Generate program to execute the selected solution
% using 2-matrix multiplies.

hfig = gcbf;
ud = get(hfig,'userdata');
i = get(ud.h.ResultList,'value');

% First line of list box is a title line, and we cannot
% generate a program based on that.
if i>1,
	i=i-1;
	UpdateStatus(hfig,'Generating program...');    
    yc = chainmultprog(ud.Results,i);
    tname = [tempname '.m'];
    fid=fopen(tname,'wt');
    for i=1:size(yc,1),
        fprintf(fid,'%s\n',yc(i,:));
    end
    fclose(fid);
    edit(tname);
	UpdateStatus(hfig,'Ready.');
end

% ---------------------------------------------------------------
function ResizeFigure(hfig, eventData)
% ResizeFigure GUI layout manager.
    
if nargin<1, hfig = gcbf; end

ud = get(hfig,'userdata');
if isempty(ud), return; end

figpos = get(hfig,'pos');

% Resize list box:
%
relOffset = get(ud.h.ResultList,'userdata');
orig_list_pos = relOffset(5:8);
relOffset = relOffset(1:4);
newPos = [relOffset(1:2) max(60,figpos(3:4)-relOffset(3:4)) ];
set(ud.h.ResultList, 'pos', newPos);
list_end_y = newPos(2)+newPos(4);

% Resize matrix-size text:
%
u = get(ud.h.MatrixSizesText,'userdata');
orig_figpos  = u(1:4);
orig_textpos = u(5:8);
newPos = get(ud.h.MatrixSizesText,'pos');
%
% Adjust starting height:
new_start_y = figpos(4) - orig_figpos(4) + orig_textpos(2);
%
% Maintain vertical gutter between text and list box top:
orig_gutter = orig_textpos(2) - (orig_list_pos(2)+orig_list_pos(4));
curr_gutter = new_start_y - list_end_y;
if curr_gutter < orig_gutter,  % adjust y start
    new_start_y = orig_gutter + list_end_y;
end
newPos(2) = new_start_y;
set(ud.h.MatrixSizesText,'pos',newPos);

% Resize edit box:
%
u = get(ud.h.MatrixSizes,'userdata');
orig_figpos  = u(1:4);
orig_editpos = u(5:8);
orig_edittop        = orig_editpos(2) + orig_editpos(4);
desired_edit_top    = figpos(4) - orig_figpos(4) + orig_edittop;
desired_edit_bottom = desired_edit_top - orig_editpos(4);
fig_edit_gutter     = orig_figpos(3) - (orig_editpos(1)+orig_editpos(3));
desired_edit_end_x  = figpos(3) - fig_edit_gutter;
curr_editpos        = get(ud.h.MatrixSizes,'pos');
desired_width       = max(120, desired_edit_end_x - curr_editpos(1));
newPos = [orig_editpos(1) desired_edit_bottom desired_width orig_editpos(4)];
%
% Maintain vertical gutter between edit and list box:
orig_gutter = orig_editpos(2) - (orig_list_pos(2)+orig_list_pos(4));
curr_gutter = desired_edit_bottom - list_end_y;
if curr_gutter < orig_gutter,  % adjust y start
    desired_edit_bottom = orig_gutter + list_end_y;
    newPos(2) = desired_edit_bottom;
end	
curr_editpos = newPos;
set(ud.h.MatrixSizes, 'pos', newPos);

if 0 % xxx
    
% Random button:
%
figOffset      = [figpos(3:4) 0 0];
relOffset      = get(ud.h.RandomButton,'userdata');
orig_buttonpos = relOffset(5:8);
relOffset      = relOffset(1:4);
newPos         = relOffset+figOffset;
curr_start_x   = newPos(1);
%
% Maintain horiz gutter between end of edit box and buttons:
orig_gutter    = orig_buttonpos(1) - (orig_editpos(1)+orig_editpos(3));
curr_edit_dx   = curr_editpos(1)+curr_editpos(3);
curr_gutter    = curr_start_x - curr_edit_dx;
if curr_gutter < orig_gutter,
    curr_start_x = orig_gutter + curr_edit_dx;
    newPos(1)    = curr_start_x;
end
save_start_x = newPos(1);
save_orig_y  = orig_buttonpos(2);
%
% Maintain vertical gutter between button and list box:
desired_button_bottom = newPos(2);
orig_gutter = orig_buttonpos(2) - (orig_list_pos(2)+orig_list_pos(4));
curr_gutter = desired_button_bottom - list_end_y;
if curr_gutter < orig_gutter,  % adjust y start
    desired_button_bottom = orig_gutter + list_end_y;
    newPos(2) = desired_button_bottom;
end	
save_start_y = newPos(2);
set(ud.h.RandomButton, 'pos', newPos);

% Build button:
%
relOffset      = get(ud.h.BuildButton,'userdata');
orig_buttonpos = relOffset(5:8);
relOffset      = relOffset(1:4);
newPos         = relOffset+figOffset;
%
% Maintain horiz gutter between end of edit box and buttons:
newPos(1) = save_start_x;
%
% Maintain vertical gutter between button and list box:
newPos(2) = save_start_y + orig_buttonpos(2) - save_orig_y;
set(ud.h.BuildButton, 'pos', newPos);

end


% ---------------------------------------------------------------
function InitGUI
%InitGUI Create and initialize new GUI window

h=CreateGUI;
UpdateGUI(h.Fig);


% ---------------------------------------------------------------
function h=CreateGUI
% CreateGUI Create new GUI window

bgc=get(0,'defaultuicontrolback');
cbh = @ResizeFigure;
h.Fig=figure('numbertitle','off', ...
   'menu','none', ...
   'name','Chain Multiplication Optimization', ...
   'integerhandle','off', ...
   'units','norm', ...
   'resizefcn', cbh, ...
   'color',bgc, ...
   'pos',[.1 .2 .55 .35]);
set(h.Fig,'units','pix');
figpos=get(h.Fig,'pos');
%bgc=get(h.Fig,'color');


% GUI Elements:
%
h.ResultList = uicontrol('style','listbox', ...
   'units','norm', ...
   'backgr', 'w', ...
   'fontname','courier', ...
   'callback', @ListCallback, ...
   'pos',[.025 .1 .965 .68]);
set(h.ResultList,'units','pix');
% Relative offset/size from top right
set(h.ResultList, 'units','pix');
pos=get(h.ResultList,'pos');
relOffset = [pos(1:2) figpos(3:4)-pos(3:4) pos];
set(h.ResultList,'userdata',relOffset);

h.MatrixSizesText = uicontrol('style','text', ...
   'horiz','right', ...
   'units','norm', ...
   'backgr', bgc, ...
   'string','Matrix size list:', ...
   'pos',[.01 .835 .18 .08]);
set(h.MatrixSizesText,'units','pix');
% Relative offset from left, top
set(h.MatrixSizesText, 'units','pix');
pos = get(h.MatrixSizesText,'pos');
relOffset = [figpos pos];
set(h.MatrixSizesText, 'userdata',relOffset);

h.MatrixSizes = uicontrol('style','edit', ...
   'horiz','left', ...
   'units','norm', ...
   'backgr', 'w', ...
   'callback', @ChangeMatrixSizes, ...
   'interruptible','off', ...
   'string','{[8 2], [2 4], [4 1], [1 12], [12 3]}', ...
   'pos',[.2 .85 .55 .085]);
% Relative offset/size from top right
set(h.MatrixSizes, 'units','pix');
pos=get(h.MatrixSizes,'pos');
relOffset = [figpos pos];
set(h.MatrixSizes, 'userdata',relOffset);


% Status bar:
h.ax_status = axes('pos',[0.0175 0.015 .45 .08]);
set(h.ax_status,'vis','off','xlim',[0 1],'ylim',[0 1]);
% Main status:
h1=line([.02 .98 .98],[.02 .02 .98],'color','w');
h2=line([.02 .02 .98],[.02 .98 .98],'color',[1 1 1]*0);
h.Status = uicontrol('parent',h.Fig, ...
   'style','text', ...
   'units','norm', ...
   'pos',[.0375 .025 .3 .055], ...
   'horiz','left', ...
   'backgr',bgc, ...
   'string','<status line>');
% Prevent status line from moving with resize:
set([h.Status h.ax_status],'units','pix');

% ---------------------------------------
% Toolbar
% --------------------------------------
icons = load('chainmult_icons.mat');

h.SpecifyExpressionButton = uitoggletool( ...
    'tooltip','Specify MATLAB Expression', ...
    'cdata',icons.mlmode, ...
    'clickedcallback',@SpecifyExpression);
h.SpecifySizesButton = uitoggletool( ...
    'tooltip','Specify Matrix Sizes', ...
    'cdata',icons.sizmode, ...
    'clickedcallback',@SpecifySizes);

h.RandomButton = uipushtool('tooltip','Generate Random Matrices', ...
    'separator','on', ...
    'cdata',icons.randsiz, ...
    'clickedcallback',@ChangeRandomMatrixSizes);
h.BuildButton = uipushtool('tooltip','Build M-File Program', ...
    'cdata',icons.mfile, ...
    'clickedcallback',@GenProgram);

h.ReuseOutputButton = uitoggletool('tooltip','Reuse output buffer', ...
    'separator','on', ...
    'cdata',icons.reuse, ...
    'clickedcallback',@ReuseOutputToggle);

uipushtool('tooltip','What''s This?', ...
    'separator','on', ...
    'cdata',icons.whatsthis, ...
    'clickedcallback',@HelpWhatsThisCB);


% ---------------------------------------
% Menus
% --------------------------------------
% File:
%
mFile = uimenu('parent',h.Fig,'label','&File');
uimenu('parent',mFile,'label','&Print','callback','printdlg(gcbf);');
uimenu('parent',mFile,'label','&Close', ...
   'callback','close(gcbf);','separator','on');

% Options:
%
mOpts = uimenu('parent',h.Fig,'label','&Options');

h.RandomMenu = uimenu('parent',mOpts, ...
   'label','Generate &Random Matrices', ...
   'accel','R', ...
   'callback', @ChangeRandomMatrixSizes);
h.BuildMenu = uimenu('parent',mOpts, ...
    'label','&Build M-File Program', ...
    'accel','B', ...
    'callback',@GenProgram);

h.SpecifyExpression = uimenu('parent',mOpts, ...
   'label','Specify &MATLAB Expression', ...
   'callback',@SpecifyExpression, ...
   'accel','M', ...
   'checked','off','separator','on');
h.SpecifySizes = uimenu('parent',mOpts, ...
   'label','Specify Matrix &Sizes', ...
   'accel','S', ...
   'callback', @SpecifySizes, ...
   'checked','on');

h.ReuseOutput = uimenu('parent',mOpts, ...
    'label','Reuse output buffer', ...
    'callback', @ReuseOutputToggle, ...
    'separator','on', ...
    'checked','on');

% Window:
%
uimenu('parent',h.Fig, 'label','&Window', ...
   'tag','winmenu', ...
   'callback', winmenu('callback'));
winmenu(h.Fig);  % Initialize the submenu

% Help:
%
mHelp = uimenu('parent',h.Fig,'label','&Help');

% Help->Help Topics:
uimenu('parent',mHelp, ...
   'label','&Help Topics', ...
   'callback',@HelpTopicsCB);

% Help->What's This? submenu:
uimenu('parent',mHelp, ...
   'label','&What''s This?', ...
   'callback', @HelpWhatsThisCB);


% ---------------------------------------

% Store GUI data:
ud.h = h;
set(h.Fig,'userdata', ud);

% Protect GUI from user plots, etc:
set(h.Fig,'handlevis','callback');

% After GUI has all elements in it, install context menu support:
install_context_menus(h.Fig);

% Setup GUI prompt/callback based on default user spec mode
% (checked menu item)
ConfigGUIforUserSpec(h.Fig);


% ----------------------------------------------
function UpdateGUI(hfig)
%UpdateGUI Compute problem and update GUI with new results

ud = get(hfig,'userdata');
h = ud.h;

UpdateStatus(h.Fig,'Computing...');
origPtr = get(h.Fig,'pointer');
set(h.Fig,'pointer','watch');

try,
   [optArgs,xstr] = GenerateOptimizationArgs(ud);
   % xxx varList is unused at present
   
   set(h.MatrixSizes,'string',xstr);
   drawnow;
   
   % Compute chain multiplication optimization:
   y = chainmult(optArgs{:}, ReuseOutputOpt(ud), 'opt');
   
   % Update user data:
   ud.Results = y;
   set(hfig,'userdata',ud);
   
   set(h.MatrixSizes,'foregroundcolor','k');
   set(h.ResultList,'string',y.ResultList,'value',1);
   UpdateStatus(h.Fig,'Ready.');
   
catch
   ud.Results = [];
   set(hfig,'userdata',ud);
    
   set(h.MatrixSizes,'foregroundcolor','r');
   set(h.ResultList,'string','');
   UpdateStatus(h.Fig,'Invalid entry specification.');

   % Show error to user:
   errordlg(lasterr, 'Matrix Entry Error','modal');
end

set(h.Fig,'pointer',origPtr);
ListCallback([],[],h.Fig);


% ----------------------------------------------
function [optArgs,xstr] = GenerateOptimizationArgs(ud)
%GenerateOptimizationArgs
%  Generate args for optimization engine (optArgs)
%  and deblanked version of input string (xstr).
%  The arguments are generated from the user input.
%
%  There are two basic user entry modes to consider:
%  - Matrix size list
%  - MATLAB expression

% Get input string from edit box:
xstr = get(ud.h.MatrixSizes,'string');

% Deblank string at both ends
xstr = fliplr(deblank(fliplr(deblank(xstr))));

if isSizeMode(ud),
   % Matrix size specification mode:
   %
   % Simply evaluate the expression in the base workspace,
   % as it is a direct argument syntax for the opt engine:
   sizeArgs = evalin('base',xstr);
   cplxArgs = zeros(size(sizeArgs));
   %varList = num2cell(char('A'+(0:length(optArgs)-1)),1);
   varList = cellstr(char(double('A') + (0:length(cplxArgs)-1))');
   
   optArgs = {sizeArgs, cplxArgs, varList};
   
else
   % MATLAB Expression mode:
   %
   optArgs = ParseMATLABExpr(xstr);
end


% ----------------------------------------------
function optArgs = ParseMATLABExpr(str)
% ParseMATLABExpr
% Returns cell-array list of arguments to chainmult.
% Also returns variable name list.

% A*B*C -> {'A','B','C'}
% U'*V*U -> { 'U''', 'V', 'U'}
% sqrt(var1)*ones(5,4)*hamming(8) -> {'sqrt(var1)', 'ones(5,4)', 'hamming(8)'}
%
% NOTE: Will not work with:
%  - parentheses surrounding *'s
%  - math operators other than * unless they occur in parens

% Find *'s:
imul = find(str=='*');

% Create cell-array of variables/expressions
if isempty(imul),
   c = {str};
else
   c{1}=str(1:imul(1)-1);
   for i=2:length(imul),
      c{i}=str(imul(i-1)+1:imul(i)-1);
   end
   c{end+1}=str(imul(end)+1:end);
end

% Eval each expression
for i=length(c):-1:1,
   try,
      d=evalin('base',c{i});
   catch
      d=[];
   end
   sizeArgs{i} = size(d);
   cplxArgs(i) = ~isreal(d);
end

varList = c;
optArgs = {sizeArgs, cplxArgs, varList};


% ----------------------------------------------
function UpdateStatus(hfig,str)
% UPDATE_STATUS Update status text.

ud = get(hfig,'userdata');
set(ud.h.Status,'string',str);



% ---------------------------------------------------------------
% C O N T E X T   M E N U    S Y S T E M
% --------------------------------------------------------------

% ----------------------------------------------
function install_context_menus(hfig)

install_context_help(hfig);  % Help system must be installed first
install_result_list_context_menus(hfig);
install_matrix_sizes_context_menus(hfig);


% ----------------------------------------------
function install_matrix_sizes_context_menus(hfig)

ud = get(hfig,'userdata');

% Get main list context menu:
hc = get(ud.h.MatrixSizes,'uicontext');

hEntry=[];  % holds handles to each menu item
cbh = @SpecifyExpression;
opts={hc,'Specify MATLAB Expression',cbh, 'checked','on'};
hEntry(end+1) = createContext(opts);
cbh = @SpecifySizes;
opts={hc,'Specify Matrix Sizes',cbh};
hEntry(end+1) = createContext(opts);

% Append handle to main menu item to list of newly created context menus,
% and give this list to all peers:
hEntry = [hEntry(1) ud.h.SpecifyExpression ...
          hEntry(2) ud.h.SpecifySizes];
set(hEntry,'userdata',hEntry);

fixup_context_order(hc);


% ----------------------------------------------
function install_result_list_context_menus(hfig)

ud = get(hfig,'userdata');

% Get main list context menu:
hc = get(ud.h.ResultList,'uicontext');

hEntry=[];  % holds handles to each menu item
cbh = @ReuseOutputToggle;
opts={hc,'Reuse output buffer',cbh, 'checked','on'};
hEntry(end+1) = createContext(opts);

% Append handle to main menu item to list of newly created context menus,
% and give this list to all peers:
hEntry = [hEntry ud.h.ReuseOutput];
set(hEntry,'userdata',hEntry);

fixup_context_order(hc);


% -----------------------------------------------------------------
function hMenu=createContext(opts)
% Helper function to append additional context menus
args = {'parent',opts{1}, 'label',opts{2}, 'callback',opts{3:end}};
hMenu=uimenu(args{:});


% -----------------------------------------------------------------
function fixup_context_order(hContext)
% Put the first context menu entry (the "What's This?" entry)
%  last in the context menu list, and turn on the separator
%  for the "What's This?" entry
childList = get(hContext,'children');
childList = childList([end 1:end-1]);
set(hContext,'children',childList);
set(childList(1),'separator','on');



% ---------------------------------------------------------------
% H E L P    S Y S T E M
% --------------------------------------------------------------

% --------------------------------------------------------------
function HelpWhatsThisBDown(hco, eventStruct)
% HelpWhatsThisBDown Button-down function called from the
%   menu-based "What's This?" function

hfig = gcbf;
hOver = hco;  % handle to object under pointer

DeactivateHelpWhatsThis(hfig);

% Dispatch to context help:
hc = get(hOver,'uicontextmenu');
hm = get(hc,'children');  % menu pointed to by context menu
tag = get(hm,'tag');

% Get the first context menu entry with the 'WT?' prefix:
%
if ~iscell(tag), tag={tag}; end
strIdx = find(strncmp(tag,'WT?',3));
if isempty(strIdx),
    tag='';
elseif length(strIdx)>1,
    warning('More than one "What''s This?" item in context menu; using the first.');
    tag=tag{strIdx(1)};
else
    tag=tag{strIdx};
end
HelpGeneral([],[],tag);


% --------------------------------------------------------------
function HelpWhatsThisCB(hco, eventStruct)
% HelpWhatsThisCB Toggle the "What's This?" help state
%   This mimics the context-menu help selection, but allows
%   cursor-selection of the help topic

hfig = gcbf;
ud = get(hfig,'UserData');

if ud.Help.IsActive,
    DeactivateHelpWhatsThis(hfig);
else
    ActivateHelpWhatsThis(hfig);
end


% --------------------------------------------------------------
function DeactivateHelpWhatsThis(hfig)

ud = get(hfig,'userdata');
if ~ud.Help.IsActive, return; end

ud.Help.IsActive = 0;

% Restore pointer icon quickly:
setptr(hfig,'arrow');

% Shut off button-down functions for uicontrols and the figure:
set(ud.Help.hChildren, 'ButtonDownFcn','');
set(ud.Help.hEnabled, 'enable','on');

ListCallback([],[],hfig);  % Update GUI enables, etc

set(hfig,'userdata',ud);


% --------------------------------------------------------------
function ActivateHelpWhatsThis(hfig)

% Get state:
ud = get(hfig,'userdata');
if ud.Help.IsActive, return; end

ud.Help.IsActive = 1;

% Change pointer icon:
setptr(hfig,'help');

% Install button-down functions on all uicontrols,
%  plus the figure itself:
% uicontrol, axes, line, patch, text
ud.Help.hChildren = findobj(hfig);
set(ud.Help.hChildren, 'ButtonDownFcn', @HelpWhatsThisBDown);

% NOTE: Enabling context-help "destroys" the enable-state
%  of all uicontrols in the GUI.  When the callback completes,
%  we must restore the enable states.
%
hEna=findobj(ud.Help.hChildren,'enable','on');
ud.Help.hEnabled=hEna;

for i=1:length(hEna),
   try, set(hEna(i),'enable','inactive');
   catch,
   end
end

set(hfig,'userdata',ud);


% --------------------------------------------------------------
function HelpTopicsCB(hco, eventStruct)
% HelpTopicsCB Get reference-page help

helpwin(mfilename);


% --------------------------------------------------------------
function HelpGeneral(hco, eventStruct, tag)
% HelpGeneral

hfig = gcbf;
%hco = gcbo;

if nargin<3,
   tag = get(hco,'tag');
end
if ~ischar(tag),
   tag = 'WT?';
end
msg = '';

% Strip off the 'WT?' tag prefix:
tag=tag(4:end);

switch tag
case ''
   msg = {'';
      'No help available on selected item.'};
case 'Chain Multiplication GUI'
    msg = {'';
        'This is the Chain Multiplication GUI.';
        '';
        'Performs optimization of a specified matrix multiply';
        'chain by determining the parenthesization of';
        'multiplicands that best minimizes two metrics:';
        '';
        '- the maximum size of all temporary matrices that';
        '  must be stored in memory at any time during the';
        '  evaluation, and';
        '- the total number of floating point operations';
        '  (flops) required to compute the result.';
        '';
        'See the Help menu for more information';
        'on CHAINGUI and CHAINMULT.'};
case 'Result List'
   msg = {'';
      'Displays a list of optimization results for all';
      'possible parenthesizations of the specified';
      'matrix multiply problem.  Three possible';
      'optimization results may appear:'
      '';
      '   - flops & storage (jointly minimized)';
      '   - flops then storage (minimized in order)';
      '   - storage then flops (minimized in order)';
      ''};
case 'Matrix Sizes'
   % This help is selected for both the "Specify matrix sizes"
   % and the "MATLAB Expression modes.  However, specific
   % help is desired for each mode
   %
   ud = get(hfig,'userdata');
   
   if isSizeMode(ud),
      % "Specify matrix sizes" mode
      tag='Matrix size list';
      msg = {'';
         'This is where the list of matrix sizes is specified';
         'prior to optimization.  This information must be';
         'entered as a cell-array of size vectors.';
         '';
         'For example, if you have 3 matrices with the sizes';
         '4x5, 5x5, and 5x4, you would enter the expression:';
         '       { [4 5][5 5][5 4] }';
         '';
         'Only real matrices may be specified in this mode.';
         'For complex matrices or general expressions, use';
         '"Specify MATLAB Expression" mode.';
      };
   else
      % "Specify MATLAB Expression" mode
      tag='MATLAB Expression' ;
      msg = {'';
         'This is where the matrix expression is specified';
         'prior to optimization.  The expression may contain';
         'expressions and multiplication (*) symbols.';
         'Note that a distinction is made between real and';
         'complex matrices and will affect the optimization.';
         '';
         'For example, if you wish to multiply the matrices';
         'U'' * V * U, enter the expression exactly as it appears.';
         'Only simple chain matrix expressions will be accepted';
         'in the form: expr1 * expr2 * expr3 * ...';
         '';
         'For entry of matrix size information without the';
         'corresponding MATLAB matrices in the workspace,';
         'use "Specify matrix sizes" mode.';
      };
   end
   
case 'Status Bar'
   msg = {'';
      'The status bar displays information about a command';
      'or toolbar button, or an operation in progress.'};
case 'Random Matrices'
    msg = {'';
        'Generates a list of random matrix sizes.'};
case 'Build Program'
    msg = {'';
        'Creates a new M-file that will simulate the chain';
        'multiply result selected from the results list.';
        '';
        'The program multiplies two matrices at a time, and';
        'includes explicit usage of temporary matrices and';
        're-use of output storage area when appropriate.'};
end

if isempty(msg),
   msg = {'';
      ['This is the ' tag '.']};
end

% Put up message box for help:
%
title = ['Help: ' tag];
helpwin(msg,title);

% Old message box:
% hmsg = msgbox(msg,title, 'help','modal');
% CenterFigOnFig(hfig, hmsg);


% --------------------------------------------------------------
function CenterFigOnFig(hfig,hmsg)
% CenterFigOnFig Center hMsg figure on top of hFig figure

set(hfig,'units','pix');
figPos = get(hfig,'pos');
figCtr = [figPos(1)+figPos(3)/2 figPos(2)+figPos(4)/2];

set(hmsg,'units','pix');
msgPos = get(hmsg,'position');
msgCtr = [msgPos(1)+msgPos(3)/2 msgPos(2)+msgPos(4)/2];

movePos = figCtr - msgCtr;

new_msgPos = msgPos;
new_msgPos(1:2) = msgPos(1:2) + movePos;
set(hmsg,'Position',new_msgPos);

return

% --------------------------------------------------------------
function install_context_help(hfig)

ud = get(hfig,'userdata');

cbh = @HelpGeneral;
main = {'label','&What''s This?', 'callback',@HelpGeneral, 'parent'};

setWTC(hfig,main, ud.h.Fig, 'Chain Multiplication GUI');
setWTC(hfig,main, ud.h.ResultList, 'Result List');
setWTC(hfig,main, [ud.h.MatrixSizes ud.h.MatrixSizesText], 'Matrix Sizes');
setWTC(hfig,main, [ud.h.Status ud.h.ax_status], 'Status Bar');
setWTC(hfig,main, ud.h.RandomButton, 'Random Matrices');
setWTC(hfig,main, ud.h.BuildButton, 'Build Program');

ud.Help.IsActive = 0;
set(hfig,'userdata',ud);



% --------------------------------------------------------------
function setWTC(hfig,main,hItem,tagStr)
% setWT Set the "What's This?" context menu and callback:
hc = uicontextmenu('parent',hfig);
uimenu(main{:},hc, 'tag', ['WT?' tagStr]);
set(hItem,'uicontextmenu',hc);


% ----------------------------------------------
% [EOF] chaingui.m
