function varargout = GFtbox(varargin)
% GFTBOX M-file for GFtbox.fig
%      GFTBOX, by itself, creates a new GFTBOX or raises the existing
%      singleton*.
%
%      H = GFTBOX returns the handle to a new GFTBOX or the handle to
%      the existing singleton*.
%
%      GFTBOX('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GFTBOX.M with the given input arguments.
%
%      GFTBOX('Property','Value',...) creates a new GFTBOX or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before GFtbox_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to GFtbox_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help GFtbox

% Last Modified by GUIDE v2.5 20-Jan-2012 17:57:28

%   numvarargin = length(varargin)
%   if numvarargin > 0
%       varargin{1}
%   end

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @GFtbox_OpeningFcn, ...
                   'gui_OutputFcn',  @GFtbox_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);

if nargin==1
    % The single argument is expected to be a file name.
    % The following will have the effect of inserting it into the UserData
    % field of the GUI window, from where it will be retrieved by
    % GFtbox_OpeningFcn, which will then load a leaf from the file.
    varargin = { 'UserData', varargin{1} };
end

if nargin && ischar(varargin{1})
  % varargin{1}
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


function InitialiseCommandPath()
    whereami = fileparts(mfilename('fullpath'));
    olddir = pwd;
    try
        cd(whereami);
        didcd = true;
    catch
        didcd = false;
    end
    InitGFtboxPath();
    if didcd
        cd(olddir);
    end


% --- Executes just before GFtbox is made visible.
function GFtbox_OpeningFcn(hObject, eventdata, handles, varargin)
UNWARN(eventdata, varargin);
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to GFtbox (see VARARGIN)

% Test to see if GFtbox has already been initialised, by looking for one of
% the dynamically created Help menus.
pc = get( handles.projectsMenu, 'Children' );
if strcmp( get( pc(1), 'Label' ), 'Help' )
    % Already made the help menus.
    return;
end

% Set the GFtbox command path.
    InitialiseCommandPath();

global gMIN_MGENVERSION gMAX_MGENVERSION EXTERNMESH GFtboxFigure
global gGlobalProps gDefaultPlotOptions CANUSEGPUARRAY
GFtboxFigure = hObject;

EXTERNMESH = [];
CANUSEGPUARRAY = canUseGPUArray();

useAllProcessors( mfilename() );

resetGlobals();

% Choose default command line output for GFtbox
handles.output = hObject;

handles = readGFtboxConfig( handles );
handles.dlgchanges = struct([]);
handles.processdlgqueue = @processDlgQueue;
handles.mesh = [];
handles.rotating = 0;
handles.rotuprighting = 0;
handles.zooming = 0;
handles.panning = 0;
handles.boingNeeded = 0;
handles = clearImageData( handles );

stdmargin = 6;
mainpanelpos = get( handles.mainpanel, 'Position' );
handles.interfacestate.initmainpanelposition = mainpanelpos;
handles.interfacestate.margin = handles.interfacestate.initmainpanelposition(1);
handles = setFixedMode( handles );

toolSelectPos = get( handles.toolSelect, 'Position' );
set( handles.toolSelect, 'SelectionChangeFcn', @toolSelect_SelectionChangeFcn );
set( handles.splitMgenButtonGroup, 'SelectionChangeFcn', @splitMgenButtonGroup_SelectionChangeFcn );
set( handles.bioAsplitTypeSelect, 'SelectionChangeFcn', @bioAsplitTypeSelect_SelectionChangeFcn );
set( handles.output, 'KeyPressFcn', @GFTwindow_KeyPressFcn );
set( handles.singlestep, 'ButtonDownFcn', @singlestep_Callback );
set( handles.thicknessRadioGroup, 'SelectionChangeFcn', @thicknessRB_Callback );
axis( handles.picture, 'equal' );
set( handles.picture, 'CameraViewAngle', 9 );
set( handles.picture, 'CameraViewAngleMode', 'manual' );
resetView( handles.picture );
if strcmp( get( handles.lightMenuItem, 'Label' ), 'Turn Light Off' )
    light( 'Parent', handles.picture );
end
drawThumbnail( handles );
set( handles.picture, 'ButtonDownFcn', @dragviewButtonDownFcn, 'Visible', 'off' );
setPictureColorContrast( handles, gDefaultPlotOptions.bgcolor );
set( handles.splitMgenAverageButton, 'UserData', 'mid' );
set( handles.splitMgenMinButton, 'UserData', 'min' );
set( handles.splitMgenMaxButton, 'UserData', 'max' );
set( handles.pictureBackground, 'XTick', [], 'XTickMode', 'manual', ...
                                'XTickLabel', [], 'XTickLabelMode', 'manual' );
set( handles.pictureBackground, 'YTick', [], 'YTickMode', 'manual', ...
                                'YTickLabel', [], 'YTickLabelMode', 'manual' );
set( handles.pictureBackground, 'ZTick', [], 'ZTickMode', 'manual', ...
                                'ZTickLabel', [], 'ZTickLabelMode', 'manual' );
set( handles.pictureBackground, 'Visible', 'on' );
uistack( handles.pictureBackground, 'bottom' );
set( handles.opacityItem, ...
     'UserData', struct( 'currentvalue', gDefaultPlotOptions.alpha )  );
set( handles.ambientItem, ...
     'UserData', struct( 'currentvalue', gDefaultPlotOptions.ambientstrength )  );
manageMutantControls( handles );
set( handles.colortexthi, 'String', '', 'Visible', 'on' );
set( handles.colortextlo, 'String', '', 'Visible', 'on' );

handles.commandKeyFns = cell(1,127);
handles.panelSwitchInfo.e = 'editor';
handles.panelSwitchInfo.m = 'morphdist';
handles.panelSwitchInfo.a = 'bio1';
handles.panelSwitchInfo.s = 'runsim';
handles.panelSwitchInfo.t = 'growthtensors';
%handles.commandKeyFns{ int8('e') } = @keyPanelSwitch;
%handles.commandKeyFns{ int8('m') } = @keyPanelSwitch;
%handles.commandKeyFns{ int8('a') } = @keyPanelSwitch;
% handles.commandKeyFns{ int8('b') } = @keyPanelSwitch;
handles.commandKeyFns{ int8('s') } = @savemodelover_KeyCallback;

%handles.commandKeyFns{ int8('t') } = @keyPanelSwitch;

fnames = fieldnames(handles);
for i=1:length(fnames)
    if ishandle(handles.(fnames{i}))
        try
            style = get( handles.(fnames{i}), 'Style' );
        catch
            style = '';
        end
        try
            get( handles.(fnames{i}), 'KeyPressFcn' );
            haveKeyPress = true;
        catch
            haveKeyPress = false;
        end
        if haveKeyPress && ~isempty(style)
            switch get( handles.(fnames{i}), 'Style' )
                case 'pushbutton', needKeyPress = true;
                case 'togglebutton', needKeyPress = true;
                case 'radiobutton', needKeyPress = true;
                case 'slider', needKeyPress = true;
                case 'checkbox', needKeyPress = true;
                case 'text', needKeyPress = true;
                case 'popupmenu', needKeyPress = true;
                otherwise, needKeyPress = false;
            end
            if needKeyPress
                set( handles.(fnames{i}), 'KeyPressFcn', @GFTwindow_KeyPressFcn );
            end
        end
    end
end

handles.panels.editor = 1;
handles.panels.morphdist = 1;
handles.panels.growthtensors = 1;
handles.panels.runsim = 1;
handles.panels.bio1 = 1;
panelnames = fieldnames(handles.panels);
for i=1:length(panelnames)
    pname = strcat( panelnames{i}, 'panel' );
    set( handles.(pname), 'Parent', handles.mainpanel );
    pos = get( handles.(pname), 'Position' );
    newpos = [ stdmargin, toolSelectPos(2) - pos(4) - stdmargin, pos(3:4) ];
    set( handles.(pname), 'Position', newpos );
end
set( handles.picturepanel, 'BorderWidth', 0, 'BorderType', 'none' );
set( handles.scalebar, 'Parent', handles.picturepanel );
handles.guicolors.greenBack = [0.4 0.8 0.4];
handles.guicolors.greenFore = [0.9 1 0.9];
handles.guicolors.yellowBack = [0.8 0.8 0.2];
handles.guicolors.yellowFore = handles.guicolors.greenFore;
setGUIColors( handles.output, handles.guicolors.greenBack, handles.guicolors.greenFore );
handles.guicolors.mainokcolor = get( handles.mainpanel, 'BackgroundColor' );
handles.guicolors.mainbadcolor = [0.65 0.65 0.2];
% setGUIColors( handles.mainpanel, handles.guicolors.mainbadcolor, handles.guicolors.greenFore );
% set( handles.mainpanel, 'BackgroundColor', handles.guicolors.mainbadcolor );

% The following setting of panel properties should be done in GUIDE, but
% GUIDE is running really slowly on Mac OS.
set( handles.cellColorIndicator1, 'BorderType', 'line','BackgroundColor',[0.1 1 0.1], 'HighlightColor', [0 0 0]);
set( handles.cellColorIndicator2, 'BorderType', 'line','BackgroundColor',[1 0.1 0.1], 'HighlightColor', [0 0 0]);
set( handles.mgenColorChooser, 'BorderType', 'line','BackgroundColor',[1 0 0], 'HighlightColor', [0 0 0], ...
    'ButtonDownFcn', 'mgenColorPick( gcbo, ''Cell color'', true )' );
set( handles.mgenNegColorChooser, 'BorderType', 'line','BackgroundColor',[0 0 1], 'HighlightColor', [0 0 0], ...
    'ButtonDownFcn', 'mgenColorPick( gcbo, ''Negative cell color'', false )' );
set( handles.mgenNegColorChooser, 'BorderType', 'line','BackgroundColor',[1 0 0], 'HighlightColor', [0 0 0]);
set( handles.rollzeroControl, 'BorderType', 'line','BackgroundColor',[1 1 1], 'HighlightColor', [0 0 0]);
set( handles.resetViewControl, 'BorderType', 'line','BackgroundColor',[1 1 1], 'HighlightColor', [0 0 0]);

setPlotBackground( handles, [1 1 1] );

ic = get( handles.interactionPanel, 'Children' );
for i=1:length(ic)
    saveColor( ic(i) );
end
enableInteractionFunction( handles, ischeckedMenuItem( handles.enabledisableIFitem ) )

rc = get( handles.runPanel, 'BackgroundColor' );
handles.runColors.okColor = rc;
wc = [ (rc(1)+rc(2))/2*ones(1,2), rc(3) ];
wch = rgb2hsv(wc);
moresat = 1.5;
moreval = 1.5;
wc = hsv2rgb( [wch(1), (moresat-1+wch(2))/moresat, (moreval-1+wch(3))/moreval] );
handles.runColors.warningColor = wc;
r = rc(1);
r = (1+r)/2;
handles.runColors.runningColor = [ r, rc( [3 1] )*0.7 ];
handles.runColors.readyColor = handles.runColors.okColor;

setGFtboxBusy( handles, false );
set( handles.busyPanel, 'BackgroundColor', handles.runColors.runningColor );
c = get( handles.busyPanel, 'Children' );
set(c,'BackgroundColor',handles.runColors.runningColor);
set(c,'ForegroundColor',[1 1 1]);

selectCurrentTool( handles );
movegui(hObject, 'center');
windowPos = get(hObject,'Position');
ss = get(0,'screensize');
preferredSize = floor(max( ss([3 4])*0.7, [1024,768] - [10,20] ));
d1 = round( (windowPos([3 4]) - preferredSize)/2 );
windowpos = [(windowPos([1 2]) + d1), preferredSize ];
set(hObject,'Position',windowpos);
movegui(hObject, 'onscreen');

handles.defaultMgenVersion = gMAX_MGENVERSION;
mgenItemNames = { 'K/BEND', 'A/B' };
for i=gMIN_MGENVERSION:gMAX_MGENVERSION
    si = sprintf( '%d', i );
    tag = [ 'newversion', si, 'Item' ];
    h = uimenu( 'Parent', handles.mgenVersionMenu, ...
        'Label', mgenItemNames{i+1}, 'Tag', tag, ...
        'Callback', @mgenVersionItem_Callback );
    if i==handles.defaultMgenVersion
        set( h, 'Checked', 'on' );
    end
    handles.(tag) = h;
    tag = [ 'curversion', si, 'Item' ];
    h = uimenu( 'Parent', handles.mgenUpgradeMenu, ...
        'Label', mgenItemNames{i+1}, 'Tag', tag, ...
        'Callback', @mgenUpgradeItem_Callback );
    handles.(tag) = h;
end

set( handles.commandFlag, 'UserData', struct([]) );
set( handles.numsaddle, 'UserData', 'Saddle Z' );
set( handles.geomparam12, 'UserData', 'Rings' );
set( handles.geomparam21, 'UserData', 'X width' );
set( handles.geomparam31, 'UserData', 'Y width' );
set( handles.poissonsRatio, 'UserData', 'Poisson''s ratio' );

connectTextAndSlider( ...
    handles.refineproptext, handles.refinepropslider, 'refine', @emptyCallback, false );
connectTextAndSlider( ...
    handles.rotatetext, handles.rotateslider, 'rotate', @emptyCallback, false );
connectTextAndSlider( ...
    handles.freezetext, handles.freezeslider, 'freezing', @meshTextSliderCallback, false );
connectTextAndSlider( ...
    handles.mutanttext, handles.mutantslider, 'mutant', @mutantValueCallback, true );
connectTextAndSlider( ...
    handles.paintamount, handles.paintslider, '', [], true );
connectTextAndSlider( ...
    handles.shockAtext, handles.shockAslider, '', [], false );
generatetype_Callback(handles.generatetype, [], handles);
mouseeditmodeMenu_Callback(handles.mouseeditmodeMenu, [], handles);
normTolMethod = strcmp( gGlobalProps.solvertolerancemethod, 'norm' );
checkErrorItems( handles, normTolMethod )

set( handles.colorbar, 'Visible', 'on' );
axis( handles.colorbar, 'off' );
fillAxes( handles.colorbar, [1 1 1] );

set( handles.picture, 'UserData', struct( 'mousemode', 'off' ) );

makeHelpMenu( handles ); % Must precede installTooltips().
handles = makeStageMenu( handles );
guidata( handles.output, handles );
installTooltips( handles );
addHelpMenuHelp( handles );
getHelpText( handles.help );

set( handles.hiresdpiItem, 'UserData', gDefaultPlotOptions.hiresdpi );

GFTwindow_ResizeFcn(handles.output, [], handles);
handles = guidata( handles.output );

handles.dragend_Callback = @dragend_Callback;
handles.fps = 10;
set( handles.fpsText, 'String', sprintf( '%d', handles.fps ) );
handles.quality = 75;
set( hObject, 'CloseRequestFcn', 'GFtboxCloseRequestFcn' );
guidata(hObject, handles);

% GFtboxUserData = get( hObject, 'UserData' );


function handles = makeStageMenu( handles )
    handles.recomputeStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'recomputeStagesItem', ...
        'Label', 'Recompute Stages', ...
        'Callback', @recomputeStagesItem_Callback );
    handles.moreStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'moreStagesItem', ...
        'Label', 'Compute More Stages...', ...
        'Callback', @moreStagesItem_Callback );
    handles.requestStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'requestStagesItem', ...
        'Label', 'Request More Stages...', ...
        'Callback', @requestStagesItem_Callback );
    handles.importRemoteStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'importRemoteStagesItem', ...
        'Label', 'Import Experiment Stages...', ...
        'Callback', @importRemoteStagesItem_Callback );
    handles.saveExperimentStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'saveExperimentStagesItem', ...
        'Label', 'Save Experiment Stages...', ...
        'Callback', @saveExperimentStagesItem_Callback );
    handles.deleteUnusedStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'deleteUnusedStagesItem', ...
        'Label', 'Delete Unused Stage Times', ...
        'Separator', 'on', ...
        'Callback', @deleteUnusedStagesItem_Callback );
    handles.deleteAllStagesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'deleteAllStagesItem', ...
        'Label', 'Delete All Stages...', ...
        'Callback', @deleteAllStagesItem_Callback );
    handles.deleteStagesAndTimesItem = uimenu( handles.stagesMenu, ...
        'Tag', 'deleteStagesAndTimesItem', ...
        'Label', 'Delete All Stages and Times...', ...
        'Callback', @deleteAllStagesItem_Callback );


function checkErrorItems( handles, isnorm )
    checkMenuItem( handles.normErrorItem, isnorm );
    checkMenuItem( handles.maxabsErrorItem, ~isnorm );


function dragend_Callback( hObject )
  % fprintf( 1, 'dragend_Callback\n' );
    handles = guidata( hObject );
    if isfield( handles, 'mesh' ) && ~isempty( handles.mesh )
        handles.mesh = getDraggedView( handles.mesh );
        guidata( hObject, handles );
    end

function addHelpMenuHelp( handles )
    c = get( handles.output, 'Children' );
    % Find all the top-level menus in order from left to right.
    topmenus = [];
    for i=1:length(c)
        if strcmp( get( c(i), 'Type' ), 'uimenu' )
            topmenus( get(c(i),'Position') ) = c(i);
        end
    end
    % Add help submenus for all of the top level menus to the help menu.
    for i=1:length(topmenus)
        m = topmenus(i);
        % add corresponding item to Help menu
        copyMenuToHelpMenu( m, m, true );
    end

function copyMenuToHelpMenu( hm, m, first )
    if ~ishandle(m), return; end
	mud = get( m, 'UserData' );
    mtag = get( m, 'Tag' );
    if isempty(mud) || ~isfield( mud, 'helptext' )
        % Do not add a help menu item for this or its children.
        return;
    end
    c = get( m, 'Children' );
    if isempty(c)
        tag = [ 'help_' mtag ];
        label = get( m, 'Label' );
        label = regexprep( label, '^Show ', 'Show/Hide ' );
        label = regexprep( label, '^Hide ', 'Show/Hide ' );
    else
        tag = [ 'helpmenu_' mtag ];
        if hm==m
            label = 'Help';
        else
            label = [ get( m, 'Label' ), ' Menu' ];
        end
    end
    haveSeparator = first || strcmp( get( m, 'Separator' ), 'on' );
    newmenu = uimenu( 'Parent', hm, ...
                      'Tag', tag, ...
                      'Label', label );
    if isempty(c)
        set( newmenu, ...
             'UserData', mud, ...
             'Callback', @menuTooltipCallback );
    else
        haveMenuHelp = ~strcmp( mud.helptext, '*' );
        if haveMenuHelp
            uimenu( 'Parent', newmenu, ...
                    'Label', get( m, 'Label' ), ...
                    'Tag', [ 'help_' mtag ], ...
                    'UserData', mud, ...
                    'Callback', @menuTooltipCallback );
        end
        for i=length(c):-1:1
            copyMenuToHelpMenu( newmenu, c(i), haveMenuHelp && (i==length(c)) );
        end
    end
    % Due to a Matlab R2011a bug in Mac OS, setting the separator property
    % must be done after the children of newmenu have been added, not when
    % newmenu is created.
    if haveSeparator
        forceMenuSeparator( newmenu );
    end
    removeUserDataFields( m, 'helptext', 'helptitle' );

function menuTooltipCallback( hObject, eventData )
    s = get( hObject, 'UserData' );
    if isfield( s, 'helpfig' ) && ishandle( s.helpfig )
        figure(s.helpfig);
    elseif isfield( s, 'helptext' ) && ~isempty( s.helptext )
        s.helpfig = displayTextInDlg( s.helptitle, s.helptext );
        set( hObject, 'UserData', s );
    end

function installTooltips( handles )
    tooltipbasename = [ mfilename(), '_tooltips.txt' ];
    tooltipfilename = fullfile( handles.codedirectory, tooltipbasename );
    fid = fopen( tooltipfilename, 'r' );
    if fid==-1
        fprintf( 1, 'Cannot find tooltips file %s in\n  %s\n', ...
            tooltipbasename, handles.codedirectory );
        return;
    end
    currentName = '';
    currentTip = '';
    havetooltip = struct();
    while true
        s = fgets( fid );
        if (length(s)==1) && (s == -1)
            setTooltip( handles, currentName, currentTip );
            break;
        end
        if (~isempty(s)) && (s(1)=='#')
            setTooltip( handles, currentName, currentTip );
            currentName = regexprep( s(2:end), '^\s*', '' );
            currentName = regexprep( currentName, '\s.*$', '' );
            currentTip = '';
            havetooltip.(currentName) = true;
        else
            currentTip = [currentTip, s];
        end
    end
    fclose( fid );
    REGENERATE_TOOLTIPS = false;
    if REGENERATE_TOOLTIPS
        % Print the names of all handles that may have tooltips, and warn
        % of those which may but don't have them.
        newttfile = regexprep( tooltipfilename, '\.txt$', 'REGEN.txt' )
        newttfid = fopen( newttfile, 'w' );
        if newttfile == -1
            fprintf( 1, 'Cannot regenerate tooltip file %s.\n', newttfile );
        else
            tooltipitems = fieldnames(havetooltip);
            for i=1:length(tooltipitems)
                if ~isfield( handles, tooltipitems{i} )
                    fprintf( 1, 'Tooltip provided for missing item %s.\n', ...
                        tooltipitems{i} );
                end
            end
            fn = fieldnames(handles);
            for i=1:length(fn)
                try
                    h = handles.(fn{i});
                    if ishandle( h )
                        n = get( h, 'Tag' );
                        if strcmp( get( h, 'Type' ), 'uimenu' )
                            % THIS CODE MAY BE OUT oF DATE.
                            ud = get( h, 'UserData' );
                            label = get( h, 'Label' );
                            if isempty(ud)
                                % TO APPEAR
                            else
                                % TO APPEAR
                            end
                        elseif isempty( regexp( n, '^text[0-9][0-9]*$' ) )
                            try
                                s = get( h, 'TooltipString' );
                                sawitem = isfield( havetooltip, n );
                                label = '';
                                try
                                    label = get( h, 'String' );
                                catch %#ok<*CTCH>
                                    try
                                        label = get( h, 'Label' );
                                    catch
                                    end
                                end
                                if ~sawitem
                                    fprintf( 1, 'Item %s (%s) has no tooltip string.\n', ...
                                        fn{i}, label );
                                end
                                if isempty(label)
                                    fprintf( newttfid, '#%s\n%s\n\n', fn{i}, s );
                                else
                                    fprintf( newttfid, '#%s %s\n%s\n\n', fn{i}, label, s );
                                end
                            catch
                                % Ignore items without the TooltipString
                                % attribute.
                            end
                        end
                    end
                catch
                end
            end
            fclose( newttfid );
        end
    end

function setTooltip( handles, name, s )
    if isempty(name)
        return;
    end
    if ~isfield( handles, name )
        fprintf( 1, 'Tooltip provided for non-existent item "%s".\n', name );
        return;
    end
    itemHandle = handles.(name);
    if ishandle(itemHandle)
        s = regexprep( s, [ char(10), '*$' ], '' );
        if ~isempty(s)
            if strcmp( get( itemHandle, 'Type' ), 'uimenu' )
                % Install the tooltip as the helptext in the userdata.
                addUserData( itemHandle, ...
                    'helptext', reformatText( s ), ...
                    'helptitle', menuPath( itemHandle ) );
            else
                try
                    set( itemHandle, 'TooltipString', s );
                catch
                    err = lasterror();
                    fprintf( 1, 'Could not set tooltip for item %s: %s.\n', ...
                        name, err.message );
                end
            end
        end
    end

function processDlgQueue( h )
    handles = guidata( h );
    while ~isempty( handles.dlgchanges )
        dlgchange = handles.dlgchanges(1);
        fprintf( 1, 'processDlgQueue: dialog %f, tag %s, value:', ...
            dlgchange.Dialog, dlgchange.Tag );
        handles.dlgchanges = handles.dlgchanges(2:end);
    end

function meshTextSliderCallback( hObject, name, val )
    meshSetProperty( guidata(hObject), name, val );

    
function handles = readSVNversion( handles )
    [revnum,revdate] = svnrevision( handles.codedirectory, false );
    handles.GFtboxRevision = revnum;
    handles.GFtboxRevisionDate = revdate;
    if handles.GFtboxRevision==0
        aboutstring = 'About';
    else
        aboutstring = sprintf( 'About rev. %d', handles.GFtboxRevision );
    end
    set( handles.aboutMenu, 'Label', aboutstring );
    if isempty(handles.GFtboxRevisionDate)
        revdatestring = 'Unknown Date';
    else
        revdatestring = regexprep( handles.GFtboxRevisionDate, 'T', ' ' );
        revdatestring = regexprep( revdatestring, '\.[0-9]*Z', '' );
    end
    set( handles.dateItem, 'Label', revdatestring );

        
function handles = readGFtboxConfig( handles )
% Establish where we are and add all necessary directories to the Matlab
% command path.  Get config info.

% Get this global, because we need to set a property of it from the config
% info.
global GFtboxFigure

    handles.codedirectory = fileparts(mfilename('fullpath'));
    handles.systemProjectsDir = fullfile( handles.codedirectory, 'Models' );
    handles = readSVNversion( handles );
    % addpath(genpath(handles.codedirectory));
    handles.configFilename = fullfile( handles.codedirectory, ...
        [ mfilename(), '_config.txt' ] );
    % Set default config info.
    configStruct = structFromFile( handles.configFilename );
    if isfield( configStruct, 'revnum' )
        [v,n] = sscanf( configStruct.revnum, '%d', 1 );
        if n ~= 1, v = 0; end
        configStruct.revnum = v;
    end
    if isfield( configStruct, 'FontSize' )
        [v,n] = sscanf( configStruct.FontSize, '%f', 1 );
        if n ~= 1, v = 0; end
        configStruct.FontSize = v;
    end
    if isfield( configStruct, 'dir' )
        configStruct.projectsdir = configStruct.dir;
        configStruct = rmfield( configStruct, 'dir' );
    end
    configStruct = defaultFromStruct( configStruct, ...
        struct(  ...
            'compressor', '', ...
     'defaultprojectdir', '', ...
         'recentproject', [], ...
                'revnum', 0, ...
               'revdate', '', ...
      'bioedgethickness', 1, ...
         'biovertexsize', 1, ...
              'FontName', '', ...
             'FontUnits', '', ...
              'FontSize', 0, ...
            'FontWeight', '', ...
             'FontAngle', '', ...
              'Renderer', 'OpenGL' ) );
    if ~isfield( configStruct, 'projectsdir' )
        % Because of stupid MATLAB syntax, struct() cannot handle
        % cell-type arguments.
        configStruct.projectsdir = {};
    end
    % If there is just one projectsdir, it will have been returned as a
    % string.  Make it a cell array with that string as its only element.
    if ischar( configStruct.projectsdir )
        configStruct.projectsdir = { configStruct.projectsdir };
    end
    % Set defaultprojectdir to the last projectsdir with a * if any,
    % otherwise the first projectsdir.
    if ~isempty( configStruct.projectsdir )
        configStruct.defaultprojectdir = configStruct.projectsdir{1};
    end
    for i=1:length( configStruct.projectsdir )
        if regexp( configStruct.projectsdir{i}, '^\*\s+' )
            configStruct.projectsdir{i} = regexprep( configStruct.projectsdir{i}, '^\*\s+', '' );
            configStruct.defaultprojectdir = configStruct.projectsdir{i};
        end
    end
    % Delete any nonexistent directories from projectsdir and defaultprojectdir.
    okdir = false( length(configStruct.projectsdir) );
    for i=1:length( configStruct.projectsdir )
        okdir(i) = exist( configStruct.projectsdir{i}, 'dir' );
        if ~okdir(i)
            fprintf( 1, 'Projects directory %s does not exist.\n', configStruct.projectsdir{i} );
            if strcmp( configStruct.projectsdir, configStruct.defaultprojectdir )
                configStruct.defaultprojectdir = '';
            end
        end
    end
    configStruct.projectsdir = configStruct.projectsdir(okdir);
    if isempty( configStruct.projectsdir )
        % If there is no projectsdir, make one in the user's home directory.
        userhome = userHomeDirectory();
        defaultuserdir = fullfile( userhome, 'GFtbox_Projects' );
        ok = mkdir( defaultuserdir );
        if ok
            fprintf( 1, 'No default user directory, created %s.\n', defaultuserdir );
            configStruct.projectsdir = { defaultuserdir };
            configStruct.defaultprojectdir = defaultuserdir;
        else
            fprintf( 1, 'No default user directory, and failed to create %s.\n', defaultuserdir );
        end
    elseif isempty( configStruct.defaultprojectdir )
        % The defaultprojectdir didn't exist.  Set it to the first
        % projectsdir.
        configStruct.defaultprojectdir = configStruct.projectsdir{1};
    end

    defaultconfig = struct( ...
            'compressor', 'None', ...
                'revnum', handles.GFtboxRevision, ...
               'revdate', handles.GFtboxRevisionDate );
    typicalHandle = handles.restartButton;
    defaultconfig = setFromStruct( defaultconfig, getFontDetails( typicalHandle ) );
    configchanged = false;
    fontdetailschanged = false;
    if isempty(configStruct.compressor)
        configStruct.compressor = defaultconfig.compressor;
        configchanged = true;
    end
    if configStruct.revnum ~= defaultconfig.revnum
        configStruct.revnum = defaultconfig.revnum;
        configStruct.revdate = defaultconfig.revdate;
        configchanged = true;
    end
    if isempty(configStruct.FontName)
        configStruct.FontName = defaultconfig.FontName;
        configchanged = true;
    elseif ~strcmp(configStruct.FontName,defaultconfig.FontName)
        fontdetailschanged = true;
    end
    if isempty(configStruct.FontUnits)
        configStruct.FontUnits = defaultconfig.FontUnits;
        configchanged = true;
    elseif ~strcmp(configStruct.FontUnits,defaultconfig.FontUnits)
        fontdetailschanged = true;
    end
    if configStruct.FontSize==0
        configStruct.FontSize = defaultconfig.FontSize;
        configchanged = true;
    elseif configStruct.FontSize ~= defaultconfig.FontSize
        fontdetailschanged = true;
    end
    if isempty(configStruct.FontWeight)
        configStruct.FontWeight = defaultconfig.FontWeight;
        configchanged = true;
    elseif ~strcmp(configStruct.FontWeight,defaultconfig.FontWeight)
        fontdetailschanged = true;
    end
    if isempty(configStruct.FontAngle)
        configStruct.FontAngle = defaultconfig.FontAngle;
        configchanged = true;
    elseif ~strcmp(configStruct.FontAngle,defaultconfig.FontAngle)
        fontdetailschanged = true;
    end
    setNamedCompressor( handles.codecMenu, configStruct.compressor );
    handles.fontdetails.FontName = configStruct.FontName;
    handles.fontdetails.FontUnits = configStruct.FontUnits;
    handles.fontdetails.FontSize = configStruct.FontSize;
    handles.fontdetails.FontWeight = configStruct.FontWeight;
    handles.fontdetails.FontAngle = configStruct.FontAngle;
    setDoubleInTextItem( handles.bioAlinesizeText, configStruct.bioedgethickness );
    setDoubleInTextItem( handles.bioApointsizeText, configStruct.biovertexsize );
    handles.userProjectsDirs = configStruct.projectsdir;
    handles.userProjectsDir = configStruct.defaultprojectdir;
    handles.Renderer = configStruct.Renderer;
    
    % Set the rendering method.
    set( GFtboxFigure, 'Renderer', handles.Renderer );
    checkRendererItemFromName( handles );
    fprintf( 1, 'Rendering method is "%s".\n', get( GFtboxFigure, 'Renderer' ) );
    
    % Create the Recent Items item on the Projects menu.
    recentHpos = get( handles.refreshProjectsMenu, 'Position' ) + 1;
    handles.recentprojectsMenu = uimenu( handles.projectsMenu, ...
            'Label', 'Recent Projects', ...
            'Tag', 'recentprojectsMenu', ...
            'Position', recentHpos );
    % Add all recent items to the Recent Items menu.  If one of those is the
    % default, add a checkmark.  Disable those that don't exist.
    if isempty( configStruct.recentproject )
        uimenu( handles.recentprojectsMenu, 'Label', 'None', 'Enable', 'off' );
    else
        if ischar( configStruct.recentproject )
            configStruct.recentproject = { configStruct.recentproject };
        end
        for i=1:length(configStruct.recentproject)
            fullname = configStruct.recentproject{i};
            [path,base,ext] = fileparts( fullname ); %#ok<ASGLU>
            uimenu( handles.recentprojectsMenu, ...
                'Label', [base ext], ...
                'Enable', boolchar( isGFtboxProjectDir( fullname ), 'on', 'off' ), ...
                'UserData', struct( 'modeldir', fullname, 'readonly', false ), ...
                'Callback', @recentprojectsMenuItemCallback );
        end
    end
    uimenu( handles.recentprojectsMenu, ...
        'Label', 'Clear Recent Projects', ...
        'Enable', 'on', ...
        'Separator', 'on', ...
        'Callback', @clearRecentProjectsMenu );
	forceMenuSeparator( handles.recentprojectsMenu );
    
    % Add all project dirs to the Projects menu and select the default one.
    for i=1:length( handles.userProjectsDirs )
        handles = addProjectsMenu( handles, handles.userProjectsDirs{i}, false, @projectMenuItemCallback );
    end
    handles = addProjectsMenu( handles, handles.systemProjectsDir, true, @projectMenuItemCallback );
    if isempty( handles.userProjectsDir )
        handles.userProjectsDir = handles.systemProjectsDir;
    end
    handles = selectDefaultProjectsMenu( handles );

    % Set the font properties in the GUI if they have been changed.
    if fontdetailschanged
        GFtboxUpdateFonts( handles.fontdetails, handles.GFTwindow, ...
            getFontDetails( handles.restartButton ) );
    end

    if configchanged
        saveGFtboxConfig( handles );
    end

function clearRecentProjectsMenu(hObject, eventdata)
    recentprojectsMenu = get( hObject, 'Parent' );
    set( recentprojectsMenu, 'Separator', 'off' );
    handles = guidata( recentprojectsMenu );
    delete( get( recentprojectsMenu, 'Children' ) );
    modeldir = getModelDir( handles.mesh );
    if ~isempty( modeldir )
        [path,base,ext] = fileparts( modeldir );
        uimenu( recentprojectsMenu, ...
            'Label', [base ext], ...
            'Enable', boolchar( isGFtboxProjectDir( modeldir ), 'on', 'off' ), ...
            'UserData', struct( 'modeldir', modeldir, 'readonly', false ), ...
            'Callback', @recentprojectsMenuItemCallback );
    end
    uimenu( recentprojectsMenu, ...
        'Label', 'Clear Recent Projects', ...
        'Enable', 'on', ...
        'Separator', 'on', ...
        'Tag', 'clearrecentprojects', ...
        'Callback', @clearRecentProjectsMenu );
    drawnow;  % Workaround for Matlab bug on Mac OS.
    set( recentprojectsMenu, 'Separator', 'on' );

function handles = refreshProjectsMenu( handles )
    wasBusy = setGFtboxBusy( handles, true );
    c = getMenuChildren( handles.projectsMenu );
    [firstProjectsMenu,lastProjectsMenu] = findProjectDirMenuItems( handles );
    for i=firstProjectsMenu:lastProjectsMenu
        ud = get( c(i), 'UserData' );
        if isfield( ud, 'modeldir' )
            handles = addProjectsMenu( handles, ud.modeldir, ud.readonly, @projectMenuItemCallback );
        end
    end
    rescanRecentProjectsMenu( handles );
    c = getMenuChildren( handles.projectsMenu );
    forceMenuSeparator( c(end) );
    handles = selectDefaultProjectsMenu( handles, getModelDir( handles.mesh ) );
    setGFtboxBusy( handles, wasBusy );
        
function rescanRecentProjectsMenu( handles )
    c = get( handles.recentprojectsMenu, 'Children' );
    for i=1:length(c)
        ud = get( c(i), 'UserData' );
        if isfield( ud, 'modeldir' )
            is = isGFtboxProjectDir( ud.modeldir );
            set( c(i), 'Enable', boolchar( is, 'on', 'off' ) );
        end
    end

% --- Outputs from this function are returned to the command line.
function varargout = GFtbox_OutputFcn(hObject, eventdata, handles) 
UNWARN(hObject, eventdata, handles);
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;


function toolSelect_SelectionChangeFcn( hObject, eventdata )
UNWARN(eventdata);
    selectCurrentTool( guidata(hObject) );


% --- Executes on button press in clearButton.
function closeProjectItem_Callback(hObject, eventdata, handles)
% Close the current project and delete the current mesh.
    makeDefaultThumbnail( handles.mesh );
    handles.mesh = [];
    handles.runColors.readyColor = handles.runColors.okColor;
    resetButton_Callback(hObject, eventdata, handles);
    enableMenus( handles );
    handles = remakeStageMenu( handles );
    enableMutations( handles );
    setToolboxName( handles );
    updateGUIFromMesh( handles );
    set( handles.siminfoText, 'String', '' );
  %  setMorphogenPanelLabel( handles )
    cla( handles.picture );
    resetView( handles.picture );
    axis( handles.picture, 'off' );
    drawThumbnail( handles );
    guidata( hObject, handles );


% --- Executes on button press in resetButton.
function resetButton_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
% Click this button if GFtbox gets confused and thinks the simulation is running
% when it isn't.  Do not click it if the simulation really is running.
    resetGlobals();
    setRunning( handles, 0 );
    clearFlag( handles, 'stopButton' );
    clearFlag( handles, 'plotFlag' );
    set( handles.plotFlag, 'UserData', [] );
    clearFlag( handles, 'commandFlag' );
    handles = indicateInteractionValidity( handles, true );
    if ~isempty( handles.mesh )
        handles.mesh = derotate( handles.mesh );
    end
    if isfield( handles, 'trackballData' )
        handles = rmfield( 'trackballData' );
    end
    set(handles.output,'WindowButtonMotionFcn','','WindowButtonUpFcn','')
    handles = readSVNversion( handles );
    guidata( hObject, handles );
    set( handles.commandFlag, 'UserData', [] );
    enableMenus( handles );
    set(gcf,'Pointer','arrow');
    set( handles.picture, ...
        'ButtonDownFcn', @dragviewButtonDownFcn, ...
        'CameraPositionMode', 'manual', ...
        'CameraTargetMode', 'manual', ...
        'CameraUpVectorMode', 'manual', ...
        'CameraViewAngleMode', 'manual', ...
        'DataAspectRatio', [1 1 1], ...
        'DataAspectRatioMode', 'manual', ...
        'PlotBoxAspectRatio',[1 1 1], ...
        'PlotBoxAspectRatioMode', 'manual' );
    setGFtboxBusy( handles, false );


function y = askAllowUnflat( handles )
    if handles.mesh.globalProps.alwaysFlat
        answer = questdlg('Remove flatness constraint?', ...
                           '', ...
                           'Yes','No','No' );
        y = strcmp(answer,'Yes');
        if y
            set( handles.alwaysFlat, 'Value', 0 );
        end
    else
        y = 1;
    end

    
% --- Executes on button press in perturbz.
function perturbz_Callback(hObject, eventdata, handles, mustExecute)
UNWARN(hObject, eventdata, handles);
% hObject    handle to perturbz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

    if isempty( handles.mesh )
        fprintf( 1, 'leaf_perturbz: No mesh.\n' );
    else
        y = askAllowUnflat( handles );
        if ~y, return; end
        [perturbzamt,ok1] = getDoubleFromDialog( handles.zamount );
        if ok1 && (perturbzamt ~= 0)
            attemptCommand( handles, false, true, ...
                'perturbz', perturbzamt );
        end
    end


% --- Executes on button press in zeroz.
function zeroz_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
% hObject    handle to zeroz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

    if isempty( handles.mesh )
        fprintf( 1, 'leaf_setzeroz: No mesh.\n' );
    else
        attemptCommand( handles, false, true, ...
            'setzeroz' );
    end

    
% --------------------------------------------------------------------
function saveProjectAsItem_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if get( handles.runFlag, 'Value' )
        simRunningDialog( 'Cannot save model while simulation is in progress.' );
        return;
    end
    if isempty( handles.mesh )
        return;
    end
    doSaveModel( handles, true );
    
    
function doSaveModel( h, asNew )
    wasBusy = setGFtboxBusy( h, true );
    projectdir = h.mesh.globalProps.projectdir;
    if isempty(projectdir)
        projectdir = h.userProjectsDir;
    end
    if asNew
        modelname = '';
    else
        modelname = h.mesh.globalProps.modelname;
    end
    [h.mesh,ok] = leaf_savemodel( h.mesh, modelname, projectdir, ...
        'strip', ischeckedMenuItem( h.stripsaveItem ) );
    guidata( h.mesh.pictures(1), h );
    if asNew
        h = refreshProjectsMenu( h );
        % Would be faster to just insert the new project dir into the
        % Projects menu.  That would also cope with the situation where the
        % new project is not stored in any of the user project directories.
        h = remakeStageMenu( h );
    end
    setMeshFigureTitle( h.output, h.mesh );
    if h.mesh.globalProps.mgen_interactionName
        set( h.mgenInteractionName, 'String', h.mesh.globalProps.mgen_interactionName );
    else
        set( h.mgenInteractionName, 'String', '(none)' );
    end

    if asNew
        h = selectDefaultProjectsMenu( h, fullfile( projectdir, modelname ) );
    end
    guidata( h.output, h );
    setGFtboxBusy( h, wasBusy );

    
% --- Executes on button press in savemodelover.
function savemodelover_Callback(hObject, eventdata, handles)
    savemodelover( handles );

function savemodelover_KeyCallback( handles, keystroke, modbits )
    savemodelover( handles );


function savemodelover( handles )
fprintf( 1, 'savemodelover\n' );
    if isempty( handles.mesh )
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot save the mesh while the simulation is running.' );
        return;
    end
    doSaveModel( handles, false );


% --------------------------------------------------------------------
function openProjectItem_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot load a new mesh while the simulation is busy.' );
        return;
    end
    wasBusy = setGFtboxBusy( handles, true );
    if isempty( handles.mesh ) || isempty( handles.mesh.globalProps.projectdir )
        projectdir = handles.userProjectsDir;
    else
        projectdir = handles.mesh.globalProps.projectdir;
    end
    [m,ok] = leaf_loadmodel( handles.mesh, '', projectdir, 'interactive', true );
    if ok && ~isempty(m)
        % Unselect old project menu item.
        % unselectProjectMenu( handles );
        handles = installNewMesh( handles, m );
        handles = selectDefaultProjectsMenu( handles, getModelDir( m ) );
        guidata(handles.output, handles);
    end
	setGFtboxBusy( handles, wasBusy );

% --- Executes on button press in restartButton.
function restartButton_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot reload the initial mesh while the simulation is busy.' );
        return;
    end
    reloadMesh( handles, 'restart' )


% --- Executes on button press in reloadmodel.
function reloadmodel_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot reload the mesh while the simulation is busy.' );
        return;
    end
    reloadMesh( handles, 'reload' );

function cmdname = makeCommandName( username )
    cmdname = lower( regexprep( username, ' ', '' ) );

function ps = paramSpec( username, paramname, default, type )
    if nargin==3
        type = default;
        default = paramname;
        paramname = makeCommandName( username );
    end
    ps = struct( 'username', username, ...
                 'paramname', paramname, ...
                 'default', default, ...
                 'lastvalue', default, ...
                 'type', type );


function mgs = makeMeshGenSpec( GUIcommand, varargin )
    mgs.GUIcommand = GUIcommand;
    mgs.scriptcommand = makeCommandName( GUIcommand );
    for i=1:length(varargin)
        mgs.paramSpec(i) = paramSpec( varargin{i}{:} );
    end


% --- Executes on button press in replacemeshbutton.
function replacemeshbutton_Callback(hObject, eventdata, handles)
    newmesh( handles, 0 );


% --- Executes on button press in replacemeshbutton.
function generatemesh_Callback(hObject, eventdata, handles)
    newmesh( handles, 1 );


% --- Executes on button press in generatemesh.
function newmesh(handles,replace)
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot create a new mesh while the simulation is busy.' );
        return;
    end
    wasBusy = setGFtboxBusy( handles, true );

    resetGlobals();
    [mp,ok] = getMeshParams( handles );
    constructor = mp.constructor;
    mp = rmfield( mp, 'constructor' );
    mpa = struct2args( mp );
    if replace
        makeDefaultThumbnail( handles.mesh );
    end
    [handles,okmesh] = attemptNewMeshCommand( handles, replace, ...
        constructor, ...
        mpa{:}, ...
        'version', handles.defaultMgenVersion );
    if okmesh
        % We know the simulation is not running at this point, so we can
        % safely modify handles.
        guidata(handles.output, handles);
        announceSimStatus( handles );
    end
    setGFtboxBusy( handles, wasBusy );
    
    
function showGrowthMenu( menu )  % NOT NEEDED?
    s = get( menu, 'String' );
    v = get( menu, 'Value' );
    fprintf( 1, 'Menu value %d, strings', v );
    for i=1:length(s)
        fprintf( 1, ' "%s"', s{i} );
    end
    fprintf( 1, '\n' );

    
function menu = setGrowthMenu( menu, length, current )  % NOT NEEDED?
    s = cell( 1, length );
    for i=1:length
        s{i} = num2str(i);
    end
    set( menu, 'String', s );
    set( menu, 'Value', current );

    
% --- Executes during object deletion, before destroying properties.
function deadcanary_DeleteFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);

% --- Executes on button press in bowlz.
function bowlz_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
% hObject    handle to bowlz (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [zamount,ok1] = getDoubleFromDialog( handles.zamount );
        if ok1 && (zamount ~= 0)
            y = askAllowUnflat( handles );
            if ~y, return; end
            attemptCommand( handles, false, true, ...
                'bowlz', zamount );
        end
    end


% --- Executes on button press in saddlez.
function saddlez_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
% hObject    handle to saddlez (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [zamount,ok1] = getDoubleFromDialog( handles.zamount );
        [numsaddle,ok2] = getIntFromDialog( handles.numsaddle, 2 );
        if ok1 && ok2
            y = askAllowUnflat( handles );
            if ~y, return; end
            attemptCommand( handles, false, true, ...
                'saddlez', 'amount', zamount, 'lobes', numsaddle );
        end
    end


function diffusionEnabled_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);


% --- Executes on button press in diffusionEnabled.
function diffusionEnabled_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    meshSetBoolean( handles, 'do_diffusion', hObject );


function growthEnabled_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);

% --- Executes on button press in growthenabled.
function growthEnabled_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh ), return; end
    
    elastic = get(hObject,'Value') ~= 0;
    plastic = get(handles.plasticGrowthEnabled,'Value') ~= 0;
    
    if elastic
        set(handles.plasticGrowthEnabled,'Value',0);
        meshSetProperty( handles, 'do_growth', 1, 'plastic', 0 );
    else
        if plastic
            meshSetProperty( handles, 'do_growth', 1, 'plastic', 1 );
        else
            attemptCommand( handles, false, false, ...
                'setproperty', 'do_growth', 0 );
            meshSetProperty( handles, 'do_growth', 0 );
        end
    end


function plasticGrowthEnabled_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh ), return; end

    elastic = get(hObject,'Value') ~= 0;
    plastic = get(handles.plasticGrowthEnabled,'Value') ~= 0;
    
    if plastic
        set(handles.growthEnabled,'Value',0);
        meshSetProperty( handles, 'do_growth', 1, 'plastic', 1 );
    else
        if elastic
            meshSetProperty( handles, 'do_growth', 1, 'plastic', 0 );
        else
            meshSetProperty( handles, 'do_growth', 0 );
        end
    end


function conductivityText_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh ), return; end
    [ conductivity, ok1 ] = ...
        getDoubleFromDialog( handles.conductivityText, 0 );
    if ok1
        attemptCommand( handles, false, false, ...
            'mgen_conductivity', ...
            getDisplayedMgenIndex( handles ), ...
            conductivity );
    end


function absorptionText_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh ), return; end
    [ absorption, ok1 ] = ...
        getDoubleFromDialog( handles.absorptionText, 0 );
    if ok1
        attemptCommand( handles, false, false, ...
            'mgen_absorption', ...
            getDisplayedMgenIndex( handles ), ...
            absorption );
    end


function absorptionText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function enabledisableIFitem_Callback(hObject, eventdata, handles)
    enable = toggleCheckedMenuItem( hObject );
    enableInteractionFunction( handles, enable );


function morpheditmodemenu_Callback(hObject, eventdata, handles)
    label = getMenuSelectedLabel( handles.morpheditmodemenu );
    fprintf( 1, 'morpheditmodemenu_Callback %s\n', label );
    handles.mesh = establishInteractionMode( handles.mesh, ...
        ['morph' label], ...
        getDisplayedMgenIndex( handles ), ...
        getDoubleFromDialog( handles.paintamount ) );
    guidata( handles.output, handles );


function morpheditmodemenu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in zeroall.
function zeroall_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        needReplot = plottingMorphogen( handles.mesh );
        attemptCommand( handles, false, needReplot, ...
            'mgen_reset' );
    end


% --- Executes on button press in zerogf.
function zerogf_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        needReplot = plottingMorphogen( handles.mesh );
        attemptCommand( handles, false, needReplot, ...
            'mgen_zero', ...
            getDisplayedMgenIndex( handles ) );
    end

function pm = plottingMorphogen( m )
    pm = ~(isempty( m.plotdefaults.morphogen ) ...
           && isempty( m.plotdefaults.morphogenA ) ...
           && isempty( m.plotdefaults.morphogenB ) ...
          );
    
function plotMgensFromGUI( h )
    if ~isempty( h.mesh )
        mgens = [];
        if get( h.inputSelectButton, 'Value' )
            if get( h.drawmulticolor, 'Value' )
                mgens = h.mesh.plotdefaults.defaultmultiplot;
%                 ud = get( h.drawmulticolor, 'Userdata' );
%                 if ~isempty(ud) && isfield( ud, 'morphogens' )
%                     mgens = ud.morphogens;
%                 end
            else
                mgens = mgenNameFromMgenMenu( h );
            end
        end
        fig = h.GFTwindow;
        
        if isempty( mgens )
            notifyPlotChange( h, 'blank', true );
        else
            notifyPlotChange( h, 'morphogen', mgens );
        end
        h = guidata( fig );
        setMyLegend( h.mesh );
    end

% --- Executes on button press in gfradial.
function gfradial_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [maxgf,ok1] = getDoubleFromDialog( handles.paintamount );
        [radialx,ok2] = getDoubleFromDialog( handles.radialx );
        [radialy,ok3] = getDoubleFromDialog( handles.radialy );
        [radialz,ok4] = getDoubleFromDialog( handles.radialz );
        if ok1 && ok2 && ok3 && ok4
            needReplot = plottingMorphogen( handles.mesh );
            attemptCommand( handles, false, needReplot, ...
                'mgen_radial', ...
                getDisplayedMgenIndex( handles ), ...
                maxgf, ...
                'x', radialx, ...
                'y', radialy, ...
                'z', radialz, ...
                'power', 1 );
        end
    end

    
% --- Executes on button press in invertGrowth.
function invertGrowth_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        needReplot = plottingMorphogen( handles.mesh );
        attemptCommand( handles, false, needReplot, ...
            'mgen_scale', ...
            getDisplayedMgenIndex( handles ), ...
            -1 );
    end
    

% --- Executes on button press in linearGrowth.
function linearGrowth_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
% hObject    handle to linearGrowth (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [maxgf,ok1] = getDoubleFromDialog( handles.paintamount );
        [lindir,ok2] = getDoubleFromDialog( handles.linearDirection );
        if ok1 && ok2
            needReplot = plottingMorphogen( handles.mesh );
            attemptCommand( handles, false, needReplot, ...
                'mgen_linear', ...
                getDisplayedMgenIndex( handles ), ...
                maxgf, ...
                'direction', lindir );
        end
    end


function linearDirection_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);

function linearDirection_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in edgeGrowth.
function edgeGrowth_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [maxgf,ok1] = getDoubleFromDialog( handles.paintamount );
        if ok1
            needReplot = plottingMorphogen( handles.mesh );
            attemptCommand( handles, false, needReplot, ...
                'mgen_edge', ...
                getDisplayedMgenIndex( handles ), ...
                maxgf );
        end
    end


% --- Executes on button press in constantGrowth.
function constantGrowth_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [maxgf,ok1] = getDoubleFromDialog( handles.paintamount );
        if ok1
            needReplot = plottingMorphogen( handles.mesh );
            attemptCommand( handles, false, needReplot, ...
                'mgen_const', ...
                getDisplayedMgenIndex( handles ), ...
                maxgf );
          % handles = guidata( hObject );
          % newmgen = handles.mesh.morphogens( :, getDisplayedMgenIndex( handles ) )'
        end
    end


% --- Executes on button press in randomGrowth.
function randomGrowth_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        [maxgf,ok1] = getDoubleFromDialog( handles.paintamount );
        if ok1
            needReplot = plottingMorphogen( handles.mesh );
            attemptCommand( handles, false, needReplot, ...
                'mgen_random', ...
                getDisplayedMgenIndex( handles ), ...
                maxgf );
        end
    end


function simsteps_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    % No action.

function GFTwindow_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);


% --- Executes during object deletion, before destroying properties.
function GFTwindow_DeleteFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);


% --- Executes on mouse press over axes background.
function picture_ButtonDownFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);


function poissonsRatio_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    [ poissonsRatio, ok1 ] = ...
        getDoubleFromDialog( handles.poissonsRatio, -1 );
    if ok1 && ~isempty( handles.mesh )
        meshSetProperty( handles, 'poisson', poissonsRatio );
    end


function poissonsRatio_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
        set(hObject,'BackgroundColor','white');
    end
    

function timestep_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        return;
    end
    [ ts, ok1 ] = getDoubleFromDialog( hObject, 0 );
    if ok1 && (ts > 0)
        meshSetProperty( handles, 'timestep', ts );
    end

function timestep_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --- Executes on button press in movieButton.
function movieButton_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh )
        wasBusy = setGFtboxBusy( handles, true );
        if handles.mesh.globalProps.makemovie
            attemptCommand( handles, false, false, ...
                'movie', 0 );
        else
            attemptCommand( handles, false, false, ...
                'movie', ...
                'fps', handles.fps, ...
                'quality', handles.quality, ...
                'compression', getSelectedCompressor( handles.codecMenu ) );
        end
        handles = guidata( hObject );
        if handles.mesh.globalProps.makemovie
            set( handles.movieButton, 'String', 'Stop movie' );
        else
            set( handles.movieButton, 'String', 'Record movie...' );
        end
        enableHandle( handles.addFrameItem, handles.mesh.globalProps.makemovie );
        setGFtboxBusy( handles, wasBusy );
    end

% --- Executes on button press in showHistoryButton.
function showHistoryButton_Callback(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh )
        for i=1:length(handles.mesh.scripthistory)
            fprintf( 1, '%s', handles.mesh.scripthistory{i} );
        end
    end



function paintslider_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function paintamount_CreateFcn(hObject, eventdata, handles)
UNWARN(hObject, eventdata, handles);
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function newplotQuantityMenu_Callback(hObject, eventdata, handles)
    notifyPlotChange( handles, ...
        'outputquantity', plottedOutputQuantity( handles ) );
    handles = guidata( hObject );
    set( handles.inputSelectButton, 'Value', 0 );
    set( handles.outputSelectButton, 'Value', 1 );
    setMyLegend( handles.mesh );

function oq = plottedOutputQuantity( handles )
    oq = lower(unspace(getMenuSelectedLabel( handles.newplotQuantityMenu )));

function newplotQuantityMenu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function notifyPlotChangeFromGUIBool( handles, fn, hObject )
    notifyPlotChange( handles, fn, get(hObject,'Value') ~= 0 );

function notifyPlotChangeShowHide( handles, fn, hObject )
    toggleShowHideMenuItem( hObject );
    notifyPlotChange( handles, fn, valueShowHideMenuItem( hObject ) );

function notifyPlotChangeCheckedMenuItem( handles, fn, hObject )
    toggleCheckedMenuItem( hObject );
    notifyPlotChange( handles, fn, ischeckedMenuItem( hObject ) );

function showEdges_Callback(hObject, eventdata, handles)
    showEdgesValue = get(handles.showEdges,'Value');
    if showEdgesValue
        notifyPlotChange( handles, 'drawedges', 2 );
    else
        notifyPlotChange( handles, 'drawedges', 1 );
    end

function showSecondLayer_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'drawsecondlayer', hObject )

function showPolariser_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'drawgradients', hObject )
    
function showTensorAxes_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'drawtensoraxes', hObject )

function drawmulticolor_Callback(hObject, eventdata, handles)
    plotMgensFromGUI( handles );

function axisRangeFromPictureButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh) && ~isempty(handles.mesh.pictures)
        a = get( handles.picture, 'XLim' );
        setDoubleInTextItem( handles.xaxislo, a(1) );
        setDoubleInTextItem( handles.xaxishi, a(2) );
        a = get( handles.picture, 'YLim' );
        setDoubleInTextItem( handles.yaxislo, a(1) );
        setDoubleInTextItem( handles.yaxishi, a(2) );
        a = get( handles.picture, 'ZLim' );
        setDoubleInTextItem( handles.zaxislo, a(1) );
        setDoubleInTextItem( handles.zaxishi, a(2) );
    end


% --- Executes on button press in autoScale.
function autoScale_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );
  % notifyPlotChangeFromGUIBool( handles, 'autoScale', hObject );


function monoCheckbox_Callback(hObject, eventdata, handles)
%   notifyPlotChangeFromGUIBool( handles, 'monochrome', hObject );
    if get( hObject, 'Value' )
        notifyPlotChange( handles, 'cmaptype', 'monochrome' );
    else
        notifyPlotChange( handles, 'cmaptype', 'rainbow' );
    end


% --- Executes on button press in autoColorRange.
function autoColorRange_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'autoColorRange', hObject );


function autoColorRangeMintext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function autoColorRangeMintext_Callback(hObject, eventdata, handles)
    [minr,ok1] = getDoubleFromDialog( handles.autoColorRangeMintext );
    [maxr,ok2] = getDoubleFromDialog( handles.autoColorRangeMaxtext );
    if ok1 && ok2
        notifyPlotChange( handles, 'crange', [ minr, maxr ] );
    end


function autoColorRangeMaxtext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function autoColorRangeMaxtext_Callback(hObject, eventdata, handles)
    [minr,ok1] = getDoubleFromDialog( handles.autoColorRangeMintext );
    [maxr,ok2] = getDoubleFromDialog( handles.autoColorRangeMaxtext );
    if ok1 && ok2
        notifyPlotChange( handles, 'crange', [ minr, maxr ] );
    end
    
    
function colorRangeFromPictureButton_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [minr,ok1] = getDoubleFromDialog( handles.colortextlo, 0, 0, false );
        [maxr,ok2] = getDoubleFromDialog( handles.colortexthi, 0, 0, false );
        if ok1 && ok2
            setDoubleInTextItem( handles.autoColorRangeMintext, minr );
            setDoubleInTextItem( handles.autoColorRangeMaxtext, maxr );
            notifyPlotChange( handles, 'crange', [ minr, maxr ] );
        end
    end

    
% --- Executes on slider movement.
function azimuth_Callback(hObject, eventdata, handles)
    viewScroll_Callback(hObject, eventdata)


function azimuth_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


% --- Executes on slider movement.
function elevation_Callback(hObject, eventdata, handles)
    viewScroll_Callback(hObject, eventdata)


function elevation_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function elevation_ButtonDownFcn(hObject, eventdata, handles)
fprintf( 1, 'elevation_ButtonDownFcn\n' );


function roll_Callback(hObject, eventdata, handles)
    viewScroll_Callback(hObject, eventdata)

function roll_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


% --- Executes on button press in refinemesh.
function refinemesh_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    else
        refineProportion = ...
            get( handles.refinepropslider, 'Value' );
        attemptCommand( handles, false, true, ...
            'refineFEM', refineProportion );
    end


% --- Executes on slider movement.
function refinepropslider_Callback(hObject, eventdata, handles)
    handleSliderToText( ...
        handles.refineproptext, ...
        handles.refinepropslider );
    guidata(hObject, handles);


function refinepropslider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function refineproptext_Callback(hObject, eventdata, handles)
    ud = get(handles.refineproptext, 'UserData' );
    handleTextToSlider( ...
        handles.refineproptext, ...
        handles.refinepropslider, ...
        ud);
    guidata(hObject, handles);


function refineproptext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes when mainpanel is resized.
function mainpanel_ResizeFcn(hObject, eventdata, handles)
% hObject    handle to mainpanel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)



% --- Executes when GFTwindow is resized.
function GFTwindow_ResizeFcn(hObject, eventdata, handles)
if ~isfield( handles, 'interfacestate' )
    fprintf( 1, 'GFtbox warning: GFTwindow_ResizeFcn called prematurely during startup.\n' );
    return;
end
    standardResize(hObject, handles);
    set( handles.deadcanary, 'Position', [ -10, -10, 1, 1 ] );


% --- Executes on button press in rotateXYZ.
function rotateXYZ_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, true, ...
        'rotatexyz' );


% --- Executes on button press in allowSplitLongFEM.
function allowSplitLongFEM_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'do_splitlongfem', hObject );


% --- Executes on button press in allowSplitLongFEM.
function allowSplitBentFEM_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'do_splitbentfem', hObject );


% --- Executes on button press in allowSplitBio.
function allowSplitBio_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'do_splitbio', hObject );


% --- Executes on button press in useTensors.
function useGrowthTensors_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'usetensors', hObject );

    
function allowRetriangulate_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'allowElideEdges', hObject );


% --- Executes on button press in allowFlipEdges.
function allowFlipEdges_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'do_flip', hObject );


% --- Executes on button press in snapshot.
function snapshot_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if ischeckedMenuItem( handles.hiresToggleItem )
        attemptCommand( handles, false, false, 'snapshot', '', ...
            'resolution', handles.mesh.plotdefaults.hiresdpi );
    else
        attemptCommand( handles, false, false, 'snapshot' );
    end
    handles = guidata( hObject );
    if handles.mesh.globalProps.makemovie
        % Add frame to movie
        handles.mesh = recordframe( handles.mesh );
        guidata( hObject, handles );
    end


function hiresToggleItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );


% --------------------------------------------------------------------
function hiresdpiItem_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        dpi = get( hObject, 'UserData' );
    else
        dpi = handles.mesh.plotdefaults.hiresdpi;
    end
    s = performRSSSdialogFromFile( 'hires.txt', struct( 'dpi', dpi ) );
    if ~isempty(s)
        dpi = sscanf( s.dpi, '%d' );
        if (numel(dpi)==1) && (dpi > 0)
            if ~isempty( handles.mesh )
                attemptCommand( handles, false, false, ...
                    'plotoptions', ...
                    'hiresdpi', dpi );
            end
            % Should store this in handles also
            set( hObject, 'UserData', dpi );
        end
    end


% --- Executes on button press in flipTCbutton.
function flipTCbutton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    elseif get( handles.runFlag, 'Value' )
        fprintf( 1, 'Not allowed while simulation is running.\n' );
    else
        % Flip signs of all growth tensors.
        handles.mesh.growthTensorPerVertex = -handles.mesh.growthTensorPerVertex;
        handles = setFlipText( handles );
        guidata(hObject, handles);
    end

    
function handles = setFlipText( handles )
    if isempty( handles.mesh )
        set( handles.growingText, 'String', '' );
    else
        sumgrowth = sum(sum(handles.mesh.growthTensorPerVertex,2),1);
        if sumgrowth >= 0
            set( handles.growingText, 'String', 'Growing' );
        else
            set( handles.growingText, 'String', 'Shrinking' );
        end
    end



function displayedGrowthMenu_Callback(hObject, eventdata, handles)
    whichMgen = getDisplayedMgenIndex( handles );
    attemptCommand( handles, false, false, ...
        'setproperty', ...
        'displayedGrowth', whichMgen );
    handles = guidata( hObject );
    notifyPlotChange( handles, 'morphogen', whichMgen );
    handles = guidata( hObject );
    setGUIMgenInfo( handles );
    set( handles.drawmulticolor, 'Value', 0 );
    set( handles.inputSelectButton, 'Value', 1 );
    set( handles.outputSelectButton, 'Value', 0 );

function generatetype_Callback(hObject, eventdata, handles)
  % fprintf( 1, 'generatetype_Callback\n' );
    meshTypes = get( hObject, 'String' );
    meshType = meshTypes{get(hObject, 'Value')};
    c = get( handles.newMeshPanel, 'Children' );
    allwidgets = struct();
    for i=1:length(c)
        if strcmp( get(c(i),'Style'), 'edit' )
            t = get( c(i), 'Tag' );
            if regexp( t, '^geomparam' )
                allwidgets.(t) = false;
            end
        end
    end
    setMeshParams( handles, meshType );
    
function setVisWidgets( handles, vis, names, values, allwidgets )
    for i=1:length(vis)
        set( handles.(vis{i}), 'Visible', 'on', 'String', values{i} );
        set( handles.([ vis{i}, 'Text' ]), 'Visible', 'on', 'String', names{i} );
        allwidgets.(vis{i}) = true;
    end
    allnames = fieldnames(allwidgets);
    for i=1:length(allnames)
        if ~allwidgets.(allnames{i})
            set( handles.(allnames{i}), 'Visible', 'off', 'String', '0' );
            set( handles.([ allnames{i}, 'Text' ]), 'Visible', 'off' );
        end
    end


function displayedGrowthMenu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function setDisplayedGrowthMenu( h, g )
%setDisplayedGrowthMenu( h, g )
%   H is the figure handles structure.
%   G is a morphogen name or index, or something else.
%   This routine sets the contents of the displayedGrowthMenu to hold the
%   names of all the morphogens of M, with the one indicated by G selected.
%   If G is not a morphogen name or index, the first morphogen is
%   selected.
    setDisplayedGrowthMenuStrings( h );
    if nargin < 2
        g = 1;
    end
    selectMgenInMenu( h, g );
    setGUIMgenInfo( h );


% --- Executes on button press in allowDilution.
function allowDilution_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh ), return; end
    attemptCommand( handles, false, false, ...
        'mgen_dilution', ...
        getDisplayedMgenIndex( handles ), ...
        get(hObject,'Value') ~= 0 );

    
function radialx_Callback(hObject, eventdata, handles)
% No action.


function radialx_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function radialy_Callback(hObject, eventdata, handles)
% No action.


function radialy_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function radialz_Callback(hObject, eventdata, handles)
% No action.


function radialz_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on mouse press over figure background.
function GFTwindow_ButtonDownFcn(hObject, eventdata, handles)


function maxFEtext_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [maxFEcells,ok] = getIntFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'maxFEcells', maxFEcells );
        end
    end


function maxFEtext_CreateFcn(hObject, eventdata, handles) %#ok<*INUSD>
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function maxBendtext_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [bendsplit,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'bendsplit', bendsplit );
        end
    end


function maxBendtext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function edgesplitscaletext_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [lstp,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'longSplitThresholdPower', lstp );
        end
    end


function edgesplitscaletext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function splitmargintext_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [splitmargin,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'splitmargin', splitmargin );
        end
    end

function splitmargintext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function minpolgradText_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [mpg,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            needReplot = handles.mesh.plotdefaults.drawgradients;
            attemptCommand( handles, false, needReplot, ...
                'setproperty', 'mingradient', mpg );
        end
    end


function minpolgradText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function relativepolgrad_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'relativepolgrad', hObject );


function solvertolerance_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [val,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'solvertolerance', val );
        end
    end

function solvertolerance_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function diffusionToleranceText_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [val,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'diffusiontolerance', val );
        end
    end

function diffusionToleranceText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function maxsolvetime_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [val,ok] = getDoubleFromDialog( hObject, 0 );
        if ok
            meshSetProperty( handles, 'maxsolvetime', val );
        end
    end

function maxsolvetime_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function maxBioAtext_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [maxBioAcells,ok] = getIntFromDialog( handles.maxBioAtext, 0 );
        if ok
            meshSetProperty( handles, 'maxBioAcells', maxBioAcells );
        end
    end


function maxBioAtext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function freezeSlider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function freezetext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function thicknessRB_Callback(hObject, eventdata)
    handles = guidata(hObject);
    if isempty( handles.mesh ), return; end
    selectedButton = get( hObject, 'SelectedObject' );
    buttonName = get( selectedButton, 'String' );
    attemptCommand( handles, false, false, 'setproperty', ...
        'thicknessMode', lower( buttonName ) );

function thicknessButton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh ), return; end
    [x,ok] = getDoubleFromDialog( handles.thicknessText );
    if ok && (x > 0)
        attemptCommand( handles, false, true, 'setthickness', x );
    end


function thicknessText_Callback(hObject, eventdata, handles)
    % Nothing to do

function thicknessText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on slider movement.
function mutantslider_Callback(hObject, eventdata, handles)

function mutantslider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function mutanttext_Callback(hObject, eventdata, handles)

function mutanttext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function revertMutantButton_Callback(hObject, eventdata, handles)
    % Set current mgen mutant level to 1.
    setSliderAndText( handles.mutantslider, 1 );
    if ~isempty( handles.mesh )
        needReplot = plottingMorphogen( handles.mesh );
        attemptCommand( handles, false, needReplot, ...
            'mgen_modulate', ...
            'morphogen', mgenNameFromMgenMenu( handles ), ...
            'mutant', 1 );
    end


function mutantValueCallback( hObject, name, val )
    handles = guidata( hObject );
    if ~isempty( handles.mesh )
        needReplot = plottingMorphogen( handles.mesh );
        attemptCommand( handles, false, needReplot, ...
            'mgen_modulate', ...
            'morphogen', mgenNameFromMgenMenu( handles ), ...
            'mutant', val );
    end

    
% --- Executes on button press in allWildcheckbox.
function allWildcheckbox_Callback(hObject, eventdata, h)
    if ~isempty( h.mesh )
        allWild = get( hObject, 'Value' );
        needReplot = plottingMorphogen( h.mesh );
        attemptCommand( h, false, needReplot, 'allowmutant', ~allWild );
        manageMutantControls( h );
    end


% --- Executes on button press in destrain.
function destrain_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        plotLabel = getMenuSelectedLabel( handles.newplotQuantityMenu );
        needReplot = strcmp( plotLabel, 'Strain' ) || strcmp( plotLabel, 'Stress' );
        attemptCommand( handles, false, needReplot, 'destrain' );
    end


% --- Executes on button press in flatstrain.
function flatstrain_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        plotLabel = getMenuSelectedLabel( handles.newplotQuantityMenu );
        needReplot = strcmp( plotLabel, 'Strain' ) || strcmp( plotLabel, 'Stress' );
        attemptCommand( handles, false, needReplot, 'flatstrain' );
    end
    

function boing()
    volume = 0.5;
    whereami = which('GFtbox');
    homedir = fileparts(whereami);
    x = wavread(fullfile( homedir, 'Boings', 'BONGS_2.wav' ));
    sound(x*volume,11000);
   
function handles = setViewControlMode( handles, mode, on )
  % axes( handles.picture );
    ud = get( handles.picture, 'UserData' );
    if on
        ud.dragmode = mode;
    else
        ud.dragmode = 'off';
    end
    set( handles.picture, 'UserData', ud );
    switch mode
        case 'pan'
            set( handles.zoomToggle, 'Value', 0 );
            set( handles.rotateToggle, 'Value', 0 );
            set( handles.rotuprightToggle, 'Value', 0 );
            set( handles.panToggle, 'Value', on );
            handles.zooming = 0;
            handles.rotating = 0;
            handles.rotuprighting = 0;
            handles.panning = on;
        case 'zoom'
            set( handles.panToggle, 'Value', 0 );
            set( handles.rotateToggle, 'Value', 0 );
            set( handles.zoomToggle, 'Value', on );
            set( handles.rotuprightToggle, 'Value', 0 );
            handles.panning = 0;
            handles.rotating = 0;
            handles.rotuprighting = 0;
            handles.zooming = on;
        case 'rot'
            set( handles.panToggle, 'Value', 0 );
            set( handles.zoomToggle, 'Value', 0 );
            set( handles.rotuprightToggle, 'Value', 0 );
            set( handles.rotateToggle, 'Value', on );
            handles.panning = 0;
            handles.zooming = 0;
            handles.rotuprighting = 0;
            handles.rotating = on;
        case 'rotupright'
            set( handles.panToggle, 'Value', 0 );
            set( handles.zoomToggle, 'Value', 0 );
            set( handles.rotateToggle, 'Value', 0 );
            set( handles.rotuprightToggle, 'Value', on );
            handles.panning = 0;
            handles.zooming = 0;
            handles.rotating = 0;
            handles.rotuprighting = on;
        otherwise
    end
    pan( handles.picture, 'off' );
    rotate3d( handles.picture, 'off' );
    zoom( handles.picture, 'off' );
    if ~on
        handles = updateAziElScrollFromView( handles );
    end

% --- Executes on button press in rotateToggle.
function rotateToggle_Callback(hObject, eventdata, handles)
    handles = setViewControlMode( handles, 'rot', ...
        get( handles.rotateToggle, 'Value' ) );
    guidata(hObject, handles);

    
function rotuprightToggle_Callback(hObject, eventdata, handles)
    handles = setViewControlMode( handles, 'rotupright', ...
        get( handles.rotuprightToggle, 'Value' ) );
    guidata(hObject, handles);


% --- Executes on button press in zoomToggle.
function zoomToggle_Callback(hObject, eventdata, handles)
    handles = setViewControlMode( handles, 'zoom', ...
        get( handles.zoomToggle, 'Value' ) );
    guidata(hObject, handles);


% --- Executes on button press in panToggle.
function panToggle_Callback(hObject, eventdata, handles)
    handles = setViewControlMode( handles, 'pan', ...
        get( handles.panToggle, 'Value' ) );
    guidata(hObject, handles);

    
function splitMgenButtonGroup_SelectionChangeFcn(hObject, eventdata)
    handles = guidata(hObject);
    if isempty( handles.mesh ), return; end
    
    selectedButton = get( hObject, 'SelectedObject' );
    buttonName = get( selectedButton, 'String' );
    % Find current morphogen.
    % Set its interpolation behaviour.
    attemptCommand( handles, false, false, 'mgeninterpolation', ...
        'morphogen', mgenNameFromMgenMenu( handles ), ...
        'interpolation', buttonName );

    

% --- Executes on button press in splitBioAbutton.
function splitBioAbutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, 'splitsecondlayer' );
    end


function bioAsplitTypeSelect_SelectionChangeFcn( hObject, eventdata )
UNWARN(eventdata);
    handles = guidata(hObject);
    if isempty( handles.mesh ), return; end
    
    selectedButton = get( hObject, 'SelectedObject' );
    buttonName = get( selectedButton, 'Tag' );
    splitcells = strcmp( buttonName, 'bioAsplitCellsButton' );
    meshSetProperty( handles, 'bioAsplitcells', splitcells );


function allowbiooverlapCheckbox_Callback(hObject, eventdata, handles)
    % Nothing

% --- Executes on button press in bioAfillbutton.
function bioAfillbutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh )
        [numcells,ok1] = getIntFromDialog( handles.actualBioACellstext, 0 );
        if ~ok1
            return;
        end
        if numcells < 5
            numcells = 5;
            set( handles.actualBioACellstext, 'String', '5' );
        end
        getBioAColorParams( handles );
        handles = guidata( hObject );
        wasBusy = setGFtboxBusy( handles, true );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'makesecondlayer', ...
            'mode', 'voronoi', ...
            'numcells', numcells );
        setGFtboxBusy( handles, wasBusy );
    end


function [c1,c2,cv] = bioAColorParams( handles )
    c1 = get( handles.cellColorIndicator1, 'BackgroundColor' );
    c2 = get( handles.cellColorIndicator2, 'BackgroundColor' );
    cv = getDoubleFromDialog( handles.colorVariationText, 0 );


function getBioAColorParams( handles )
    [c1,c2,cv] = bioAColorParams( handles );
    attemptCommand( handles, false, false, ...
        'setsecondlayerparams', ...
        'colors', [c1;c2], 'colorvariation', cv );


function actualBioACellstext_Callback(hObject, eventdata, handles)


function actualBioACellstext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in bioAscatterbutton.
function bioAscatterbutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    end
    [sides,ok1] = getIntFromDialog( handles.cellSidesText );
    [relsize,ok2] = getDoubleFromDialog( handles.bioArelsizetext );
    [axisratio,ok3] = getDoubleFromDialog( handles.bioAaxisratiotext );
    [numcells,ok4] = getDoubleFromDialog( handles.actualBioACellstext );
    if ok1 && ok2 && ok3 && ok4
        [c1,c2,cv] = bioAColorParams( handles );
        handles = guidata( hObject );
        wasBusy = setGFtboxBusy( handles, true );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'makesecondlayer', ...
            'mode', 'each', ...
            'relarea', relsize, ...
            'axisratio', axisratio, ...
            'numcells', numcells, ...
            'sides', sides, ...
            'allowoverlap', get( handles.allowbiooverlapCheckbox, 'Value' ), ...
            'colors', [c1;c2], ...
            'colorvariation', cv );
    	setGFtboxBusy( handles, wasBusy );
    end


function bioAGridButton_Callback(hObject, eventdata, handles)


function bioAgridbutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    end
    [relsize,ok1] = getDoubleFromDialog( handles.bioArelsizetext );
    if ok1
        [c1,c2,cv] = bioAColorParams( handles );
        handles = guidata( hObject );
        wasBusy = setGFtboxBusy( handles, true );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'makesecondlayer', ...
            'mode', 'grid', ...
            'relarea', relsize, ...
            'colors', [c1;c2], 'colorvariation', cv );
    	setGFtboxBusy( handles, wasBusy );
    end


function cellSidesText_Callback(hObject, eventdata, handles)


function cellSidesText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function bioArelsizetext_Callback(hObject, eventdata, handles)


function bioArelsizetext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function bioAaxisratiotext_Callback(hObject, eventdata, handles)


function bioAaxisratiotext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in bioAsinglebutton.
function bioAsinglebutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh )
        [sides,ok1] = getIntFromDialog( handles.cellSidesText );
        [relsize,ok2] = getDoubleFromDialog( handles.bioArelsizetext );
        [axisratio,ok3] = getDoubleFromDialog( handles.bioAaxisratiotext );
        if ok1 && ok2 && ok3
            [c1,c2,cv] = bioAColorParams( handles );
            handles = guidata( hObject );
            needReplot = handles.mesh.plotdefaults.drawsecondlayer;
            attemptCommand( handles, false, needReplot, ...
                'makesecondlayer', ...
                'mode', 'single', ...
                'relarea', relsize, ...
                'axisratio', axisratio, ...
                'sides', sides, ...
                'allowoverlap', get( handles.allowbiooverlapCheckbox, 'Value' ), ...
                'colors', [c1;c2], 'colorvariation', cv );
        end
    end


% --- Executes on button press in bioAdeletebutton.
function bioAdeletebutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, 'deletesecondlayer' );
    end


% --- Executes on button press in bioAshockbutton.
function bioAshockbutton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
    	shockProportion = get( handles.shockAslider, 'Value' );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'shockA', shockProportion );
    end

% --- Executes on button press in unshockBioA.
function unshockBioA_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'unshockA' );
    end

% --- Executes on button press in bioAcolorsButton.
function bioAcolorsButton_Callback(hObject, eventdata, handles)
    UNWARN(eventdata);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        [c1,c2,cv] = bioAColorParams( handles );
        handles = guidata( hObject );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'colourA', ...
            'colors', [c1;c2], 'colorvariation', cv );
    end

% --- Executes on button press in bioAuniformButton.
function bioAuniformButton_Callback(hObject, eventdata, handles)
    UNWARN(eventdata);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        getBioAColorParams( handles );
        handles = guidata( hObject );
        needReplot = handles.mesh.plotdefaults.drawsecondlayer;
        attemptCommand( handles, false, needReplot, ...
            'colourA', 'uniform', 1/3, 'random', 0.0 );
    end


function bioAlinesizeText_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    saveGFtboxConfig( handles );
    [x,ok] = getDoubleFromDialog( hObject );
    if ok && (x >= 0)
        notifyPlotChange( handles, 'bioAlinesize', x );
    end


function bioAlinesizeText_CreateFcn(hObject, eventdata, handles)
UNWARN(eventdata, handles);
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function bioApointsizeText_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    saveGFtboxConfig( handles );
    [x,ok] = getDoubleFromDialog( hObject );
    if ok && (x >= 0)
        notifyPlotChange( handles, 'bioApointsize', x );
    end


function bioApointsizeText_CreateFcn(hObject, eventdata, handles)
UNWARN(eventdata, handles);
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in nudgeL2button.
function nudgeL2button_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh ) && hasSecondLayer( handles.mesh )
        handles.mesh = perturbSecondLayer( handles.mesh, ...
            0.1 * handles.mesh.secondlayer.splitThreshold );
        handles = GUIPlotMesh( handles );
        guidata(hObject, handles);
    end


function GUIformat_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    OldFontDetails = getFontDetails( handles.restartButton );
    FontDetails = uisetfont( OldFontDetails, 'Update GFtbox Font' );
    if isstruct( FontDetails )
        GFtboxUpdateFonts( FontDetails, handles.GFTwindow, ...
            OldFontDetails );
        handles.fontdetails = FontDetails;
        saveGFtboxConfig( handles );
    end

% --------------------------------------------------------------------
function projectMenu_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    % Nothing.
    
    
function showProjectFolderItem_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    if isempty(handles.mesh)
        return;
    end
	if isempty(handles.mesh.globalProps.projectdir)
        complain( 'The mesh has not been saved as a project.\n' );
        return;
	end
    mdir = getModelDir( handles.mesh );
    [s,d] = opendir( mdir );
    if (s ~= 0) && ~isempty(d)
        complain( 'Error %d: %s\n', s, d );
    end


% --- Executes on button press in loadGrowthButton.
function loadGrowthButton_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata);
    if isempty( handles.mesh )
        errordlg( 'There is no current mesh.', 'Load Growth' )
        return;
    end
    if get( handles.runFlag, 'Value' )
        simRunningDialog( 'Cannot load new growth tensors while simulation is in progress.' );
        return;
    end
    handles.mesh = leaf_loadgrowth( handles.mesh );
    if handles.mesh.globalProps.targetRelArea > 0
        setDoubleInTextItem( handles.areaTargetText, handles.mesh.globalProps.targetRelArea );
    end
    handles = GUIPlotMesh( handles );
    guidata(handles.output, handles);
    

% --- Executes on button press in alwaysFlat.
function alwaysFlat_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);
    if ~isempty( handles.mesh )
        val = get( handles.alwaysFlat, 'Value' );
        attemptCommand( handles, false, val, ...
            'alwaysflat', val );
    end


function handles = frob5reset( handles )
    handles = safermfield( handles, 'initnodes' );
    handles = safermfield( handles, 'xrectwidth' );
    handles = safermfield( handles, 'yrectwidth' );

    
% --- Executes on button press in frob5button.
function frob5button_Callback(hObject, eventdata, handles)
    UNWARN(eventdata);
% Warp the Mona Lisa.
    if isempty( handles.mesh ), return; end
    if 1 || (~isfield(handles, 'mona')) || isempty(handles.mona)
        fprintf( 1, 'Loading Mona Lisa.\n' );
        handles.mona = imread( 'MonaLisa.bmp' );
      % handles.mona( 1:50,1:100,2:3) = 0;  % To show which axis is which
                                            % and what direction it runs.
    end
    if (~isfield(handles,'initnodes')) || isempty(handles.initnodes)
        fprintf( 1, 'Determining size of mesh.\n' );
        handles.initnodes = handles.mesh.nodes;
        for i=1:size(handles.initnodes,1)-1
            if handles.initnodes(i+1,1) < handles.initnodes(i,1)
                handles.xrectwidth = i;
                break;
            end
        end
        handles.yrectwidth = size(handles.initnodes,1)/handles.xrectwidth;
        fprintf( 1, 'xrectwidth %d yrectwidth %d\n', handles.xrectwidth, handles.yrectwidth );
    else
        fprintf( 1, 'xrectwidth %d yrectwidth %d already exist\n', handles.xrectwidth, handles.yrectwidth );
    end
    newnodes = handles.mesh.nodes( 1:size(handles.initnodes,1),: );
    hold on;
    warp( reshape( newnodes(:,1), handles.xrectwidth, handles.yrectwidth )', ...
          reshape( newnodes(:,2), handles.xrectwidth, handles.yrectwidth )', ...
          reshape( newnodes(:,3)+1, handles.xrectwidth, handles.yrectwidth )', ...
          handles.mona );
    hold off;
    guidata(hObject, handles);

    
% --- Executes on button press in plotFlag.
function plotFlag_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);

% --- Executes on button press in stopFlag.
function stopFlag_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);

% --- Executes on button press in commandFlag.
function commandFlag_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);

% --- Executes on button press in runFlag.
function runFlag_Callback(hObject, eventdata, handles)
    UNWARN(hObject, eventdata, handles);

function meshSetProperty( handles, varargin )
    if ~isempty(handles.mesh)
        attemptCommand( handles, false, false, ...
            'setproperty', varargin{:} );
    end

function meshSetBoolean( handles, name, h )
    if ~isempty(handles.mesh)
        attemptCommand( handles, false, false, ...
            'setproperty', name, get(h,'Value') ~= 0 );
    end
    
function enableInteractionFunction( handles, enable )
    setVisibility( handles.enableIFtext, ~enable );
    if ~isempty(handles.mesh)
        attemptCommand( handles, false, false, ...
            'setproperty', 'allowInteraction', logical(enable) );
    end

function UNWARN(varargin)
%UNWARN(varargin) does nothing.  Call this to suppress warnings about
%unused variables.
    DO_NOT_CALL_THIS_ITS_ONLY_TO_SUPPRESS_WARNINGS();

function DO_NOT_CALL_THIS_ITS_ONLY_TO_SUPPRESS_WARNINGS()
% This function prevents Matlab from warning that the functions that it
% calls are never called.  This function itself should not be called.
    if true, return; end
    projectMenu_Callback();
    loadGrowthButton_Callback();
    alwaysFlat_Callback();
    frob5button_Callback();
    mouseModeMenu_Callback();
    mouseModeMenu_CreateFcn();
    newplotQuantityMenu_Callback();
    newplotQuantityMenu_CreateFcn();
	showEdges_Callback();
    showSecondLayer_Callback();
    plotFlag_Callback();
    stopFlag_Callback();
    commandFlag_Callback();
    runFlag_Callback();
    movieButton_Callback();
    resetButton_Callback();
    clearButton_Callback();
    attachPictureButton_Callback();
    help_Callback();
    version_Callback();
    newMgenButton_Callback();
    deleteMgenButton_Callback();
    renameMgenButton_Callback();
    areaTargetText_Callback();
    areaTargetText_CreateFcn();
    editMgenInteractionButton_Callback();





% --- Executes on button press in attachPictureButton.
function attachPictureButton_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, true, 'attachpicture' );



% --------------------------------------------------------------------
function help_Callback(hObject, eventdata, handles)
    % Nothing.

% --------------------------------------------------------------------
function version_Callback(hObject, eventdata, handles)
    % Nothing.

% --- Executes on button press in newMgenButton.
function newMgenButton_Callback(hObject, eventdata, handles)
    NEWMETHOD = true;
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
    elseif get( handles.runFlag, 'Value' )
        % WARNING: Should ask user.
        complain( 'Cannot create new morphogens while simulation is running.' );
    elseif NEWMETHOD
        xx = performRSSSdialogFromFile('newmorphogenslayout.txt');
        if isempty(xx)
            return;
        end
        mgennames = xx.lb.strings;
        if isempty(mgennames)
            return;
        end
        handles.mesh = leaf_add_mgen( handles.mesh, mgennames{:} );
        setDisplayedGrowthMenu( handles, ...
            handles.mesh.plotdefaults.morphogen );
        handles = GUIPlotMesh( handles );
        guidata(hObject, handles);
    else
        [s,ok] = askForText( 'Create morphogens', ...
            ['Type the names and properties of the new morphogens.  Example:', char(10), ...
             '        ABC(diff=3) DEF(diff=0.5,decay=1) GHI', char(10), ...
             'Allowed properties are DIFF (diffusion constant) and DECAY (decay constant).', char(10), ...
             'Upper and lower case are equivalent. Omitted attributes default to zero.' ] );
        if ~isempty(s)
            [mgens,ok] = parseMgenList( s );
            if ok && ~isempty(mgens)
                mgennames = { mgens.mgen };
                diff = { mgens.diff };
                decay = { mgens.decay };
                handles.mesh = leaf_add_mgen( handles.mesh, mgennames{:} );
                diffargs = reshape( { mgennames{:}; diff{:} }, 1, [] );
                handles.mesh = leaf_mgen_conductivity( handles.mesh, diffargs{:} );
                decayargs = reshape( { mgennames{:}; decay{:} }, 1, [] );
                handles.mesh = leaf_mgen_absorption( handles.mesh, decayargs{:} );
                setDisplayedGrowthMenu( handles, ...
                    handles.mesh.plotdefaults.morphogen );
                handles = GUIPlotMesh( handles );
                guidata(hObject, handles);
            end
            if false
                if isValidMgenName( s )
                    handles.mesh = recordcommand( handles.mesh, false, 'add_mgen', s );
                    oldNumMgens = length( handles.mesh.mgenIndexToName );
                    handles.mesh = leaf_add_mgen( handles.mesh, s );
                    newNumMgens = length( handles.mesh.mgenIndexToName );
                    if newNumMgens > oldNumMgens
                        setDisplayedGrowthMenu( handles, ...
                            handles.mesh.plotdefaults.morphogen );
                        handles = GUIPlotMesh( handles );
                    end
                    guidata(hObject, handles);
                else
                    complain( ...
                        '"%s" is not a valid morphogen name. Use letters, digits, and\nunderscore only, beginning with a letter.', ...
                        s );
                end
            end
        end
    end


% --- Executes on button press in deleteMgenButton.
function deleteMgenButton_Callback(hObject, eventdata, handles)
    global gOLD_NUMRESERVEDMGENS;
    global gNEW_NUMRESERVEDMGENS;
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if handles.mesh.versioninfo.mgenversion==0
        NUMRESERVEDMGENS = gOLD_NUMRESERVEDMGENS;
    else
        NUMRESERVEDMGENS = gNEW_NUMRESERVEDMGENS;
    end
    if get( handles.runFlag, 'Value' )
        % WARNING: Should ask user.
        complain( 'Cannot delete a morphogen while simulation is running.' );
    else
        mgenIndex = getDisplayedMgenIndex( handles );
        mgenName = handles.mesh.mgenIndexToName{mgenIndex};
        if mgenIndex <= NUMRESERVEDMGENS
            errordlg( ...
                { [ 'Morphogen ', mgenName, ' is reserved and cannot be deleted.' ] }, ...
                'Delete morphogen' );
        else
            answer = questdlg( ...
                ['Delete morphogen ', char(39), mgenName, char(39), '?'], ...
                'Delete morphogen', ...
                'Yes','No','No' );
            if strcmp( answer, 'Yes' )
                oldNumMgens = length( handles.mesh.mgenIndexToName );
                attemptCommand( handles, true, false, 'delete_mgen', mgenName );
                handles = guidata( hObject );
                newNumMgens = length( handles.mesh.mgenIndexToName );
                if newNumMgens < oldNumMgens
                    setDisplayedGrowthMenu( handles, ...
                        handles.mesh.globalProps.displayedGrowth );
                    handles = GUIPlotMesh( handles );
                end
                guidata(hObject, handles);
            end
        end
    end

% --- Executes on button press in renameMgenButton.
function renameMgenButton_Callback(hObject, eventdata, handles)
    global gOLD_NUMRESERVEDMGENS;
    global gNEW_NUMRESERVEDMGENS;
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if handles.mesh.versioninfo.mgenversion==0
        NUMRESERVEDMGENS = gOLD_NUMRESERVEDMGENS;
    else
        NUMRESERVEDMGENS = gNEW_NUMRESERVEDMGENS;
    end

    if get( handles.runFlag, 'Value' )
        % WARNING: Should ask user.
        complain( 'Cannot rename a morphogen while simulation is running.' );
    end
    
    % Find the current morphogen name, and ask for a new one.
    mgenIndex = getDisplayedMgenIndex( handles );
    oldMgenName = handles.mesh.mgenIndexToName{mgenIndex};
    if mgenIndex <= NUMRESERVEDMGENS
        errordlg( ...
            { [ 'Morphogen ', oldMgenName, ' is reserved and cannot be renamed.' ] }, ...
            'Rename morphogen' );
        return;
    end
    queryString = sprintf( 'Rename "%s" to:', oldMgenName );
    newMgenName = askForString( 'Rename morphogen', queryString, oldMgenName );

    % Check the response for validity and rename the current morphogen to
    % the new name.
    if ~isempty(newMgenName) && ~strcmp( newMgenName, oldMgenName )
        if isValidMgenName( newMgenName )
            handles.mesh = recordcommand( handles.mesh, false, 'rename_mgen', ...
                    oldMgenName, newMgenName );
            handles.mesh = leaf_rename_mgen( handles.mesh, oldMgenName, newMgenName );
            setDisplayedGrowthMenuStrings( handles );
            guidata(hObject, handles);
        else
            complain( ...
                '"%s" is not a valid morphogen name. Use letters, digits, and\nunderscore only, beginning with a letter.', ...
                newMgenName );
        end
    end

function simtimeText_Callback(hObject, eventdata, handles)


function simtimeText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function areaTargetText_Callback(hObject, eventdata, handles)

function areaTargetText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function announceInteraction( handles )
    if isempty( handles.mesh.globalProps.mgen_interaction )
        set( handles.mgenInteractionName, ...
            'String', '(none)', ...
            'TooltipString', '' );
    else
        set( handles.mgenInteractionName, ...
            'String', handles.mesh.globalProps.mgen_interactionName, ...
            'TooltipString', ...
            fullfile( getModelDir( handles.mesh ), ...
                [ handles.mesh.globalProps.mgen_interactionName, '.m' ] ) );
    end


% --- Executes on button press in editMgenInitfnButton.
function editMgenInitfnButton_Callback(hObject, eventdata, handles)
    fprintf( 1, 'Initialisation function not implemented.\n' );


function rewriteIFButton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot rewrite interaction function while the simulation is running.' );
        return;
    end
    wasBusy = setGFtboxBusy( handles, true );
    attemptCommand( handles, false, false, 'rewriteIF', 'force', true );
	setGFtboxBusy( handles, wasBusy );


% --- Executes on button press in editMgenInteractionButton.
function editMgenInteractionButton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot edit interaction function while the simulation is running.' );
        return;
    end
    % Note that we do not record this command in the script.
    hadIF = ~isempty( handles.mesh.globalProps.mgen_interaction );
    if ~hadIF && isempty( getModelDir( handles.mesh ) )
        saveProjectAsItem_Callback(hObject, eventdata, handles);
        handles = guidata(hObject);
    end
    handles.mesh = leaf_edit_interaction( handles.mesh );
    if ~handles.mesh.globalProps.interactionValid
        meshSetProperty( handles, 'do_interaction', 1 )
        handles = guidata(hObject);
    end
    handles = indicateInteractionValidity( handles, true );
    haveIF = ~isempty( handles.mesh.globalProps.mgen_interaction );
    if hadIF ~= haveIF
        announceInteraction( handles );
    end
    guidata(hObject, handles);

function handles = initialiseIF( handles )
    if ~isempty(handles.mesh) ...
           && handles.mesh.globalProps.allowInteraction ...
           && isa(handles.mesh.globalProps.mgen_interaction,'function_handle')
        wasBusy = setGFtboxBusy( handles, true );
        attemptCommand( handles, false, true, 'dointeraction', 1 );
        handles = guidata( handles.output );
        updateGUIFromMesh( handles );
        handles = indicateInteractionValidity( handles );
        setGFtboxBusy( handles, wasBusy );
    end

% --- Executes on button press in initialiseIFButton.
function initialiseIFButton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot initialise interaction function while the simulation is running.' );
        return;
    end
    handles = initialiseIF( handles );
    guidata( hObject, handles );


function notesButton_Callback(hObject, eventdata, handles)
% Edit the notes file for the project.
    if isempty( handles.mesh )
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    notesname = makeNotesName( handles.mesh );
    if isempty( notesname )
        complain( 'Must save project before creating a notes file.\n' );
        return;
    end
    ok = true;
    if ~exist( notesname, 'file' )
        try
            fid = fopen( notesname, 'w' );
            fclose( fid );
        catch
            complain( 'Cannot create notes file %s.', ...
                notesname );
            ok = false;
        end
    end
    if ok
        try
            edit( notesname );
        catch
            complain( 'Cannot open notes file %s in editor.', ...
                notesname );
        end
    end


% --- Executes on slider movement.
function shockAslider_Callback(hObject, eventdata, handles)


function shockAslider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end



function shockAtext_Callback(hObject, eventdata, handles)


function shockAtext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function mouseeditmodeMenu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in fixXbox.
function fixXbox_Callback(hObject, eventdata, handles)
    setFixedMode( handles );


% --- Executes on button press in fixYbox.
function fixYbox_Callback(hObject, eventdata, handles)
    setFixedMode( handles );


% --- Executes on button press in fixZbox.
function fixZbox_Callback(hObject, eventdata, handles)
    setFixedMode( handles );


function handles = setFixedMode( handles )
    handles.interfacestate.fixedmode.x = get( handles.fixXbox, 'Value' );
    handles.interfacestate.fixedmode.y = get( handles.fixYbox, 'Value' );
    handles.interfacestate.fixedmode.z = get( handles.fixZbox, 'Value' );
    handles.mesh = establishInteractionMode( handles.mesh, ...
        'Fix nodes', ...
        handles.interfacestate.fixedmode );
    handles = GUIPlotMesh( handles );
    guidata( handles.output, handles );


% --- Executes on button press in unfixallButton.
function unfixallButton_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        handles.mesh = ...
            updateSelection( handles.mesh, [], [], [], 2 );
        if strcmp(handles.mesh.interactionMode.clickMode,'Fix nodes')
            attemptCommand( handles, false, true, ...
                'fix_vertex', ...
                'vertex', [], 'dfs', '' );
        else
            attemptCommand( handles, false, true, ...
                'locate_vertex', ...
                'vertex', [], 'dfs', '' );
        end
    end


function xaxislo_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function xaxislo_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function xaxishi_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function xaxishi_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function yaxislo_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function yaxislo_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function yaxishi_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function yaxishi_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function zaxislo_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function zaxislo_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function zaxishi_Callback(hObject, eventdata, handles)
    setaxisrangeFromDialog( handles );


function zaxishi_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



% --------------------------------------------------------------------
function meshMenu_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function loadmatItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'load', '.mat', 'interactive', true );
    finishLoadMesh( handles );

% --------------------------------------------------------------------
function loadscriptItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'load', '.m', 'interactive', true );
    finishLoadMesh( handles );


% --------------------------------------------------------------------
function loadobjItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'load', '.obj', 'interactive', true );
    finishLoadMesh( handles );

% --------------------------------------------------------------------
function loadMSRItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'load', '.msr', 'interactive', true );
    finishLoadMesh( handles );

function finishLoadMesh( handles )
    handles = guidata( handles.output );
    if ~isempty( handles.mesh )
        handles.mesh.pictures = handles.output;
        updateGUIFromMesh( handles );
        handles = GUIPlotMesh( handles );
        announceSimStatus( handles );
        guidata( handles.output, handles );
    end



% --------------------------------------------------------------------
function savematItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.mat' );


% --------------------------------------------------------------------
function savescriptItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.m' );


% --------------------------------------------------------------------
function saveobjItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.obj' );


% --------------------------------------------------------------------
function saveMSRItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.msr' );


function savevrmlItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.wrl' );


% --------------------------------------------------------------------
function savefigItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, 'save', '.fig' );


% --------------------------------------------------------------------
function wizardMenu_Callback(hObject, eventdata, handles)

% --------------------------------------------------------------------
function exportMeshItem_Callback(hObject, eventdata, handles)
    global EXTERNMESH;
    EXTERNMESH = handles.mesh;
    fprintf( 1, 'global EXTERNMESH exported.\n' );


% --------------------------------------------------------------------
function importMeshItem_Callback(hObject, eventdata, handles)
    global EXTERNMESH;
    if isempty( EXTERNMESH ), return; end
    if ~validmesh( EXTERNMESH ),
        complain( 'EXTERNMESH is invalid and cannot be imported.' );
        return;
    end
    handles.mesh = EXTERNMESH;
    fprintf( 1, 'EXTERNMESH imported.\n' );
    updateGUIFromMesh( handles );
    handles = GUIPlotMesh( handles );
    announceSimStatus( handles );
    guidata( handles.output, handles );


% --------------------------------------------------------------------
function paramsMenu_Callback(hObject, eventdata, handles)
    % Enable or disable menu items here.

% --------------------------------------------------------------------
function timeunitItem_Callback(hObject, eventdata, handles)
    global gGlobalProps
    if isempty(handles.mesh)
        setGlobals();
        starttime = gGlobalProps.starttime;
        timeunitname = gGlobalProps.timeunitname;
    else
        starttime = handles.mesh.globalProps.starttime;
        timeunitname = handles.mesh.globalProps.timeunitname;
    end
    x = timeunitDlg( ...
        'starttime', starttime, ...
        'timeunitname', timeunitname );
    if isstruct(x)
        [newstarttime,n] = sscanf( x.starttimeText, '%f' );
        if n==0
            complain( 'Invalid time ''%s''. Number expected.', ...
                x.starttimeText );
            return;
        elseif isempty(handles.mesh)
            gGlobalProps.starttime = newstarttime;
            gGlobalProps.timeunitname = x.timeunitText;
        else
            timechange = newstarttime - handles.mesh.globalProps.starttime;
            if (timechange ~= 0) || ~strcmp( x.timeunitText, handles.mesh.globalProps.timeunitname )
                attemptCommand( handles, true, false, ...
                    'setproperty', ...
                    'starttime', newstarttime, ...
                    'currenttime', timechange + handles.mesh.globalDynamicProps.currenttime, ...
                    'timeunitname', x.timeunitText );
            end
        end
    end


% --------------------------------------------------------------------
function distunititem_Callback(hObject, eventdata, handles)
    global gGlobalProps
    if isempty(handles.mesh)
        setGlobals();
        distunitname = gGlobalProps.distunitname;
        scaleunit = gGlobalProps.scalebarvalue;
    else
        distunitname = handles.mesh.globalProps.distunitname;
        scaleunit = handles.mesh.globalProps.scalebarvalue;
    end
    x = distunitDlg( 'distunitname', distunitname, 'scaleunit', scaleunit );
    if isstruct(x)
        [newscaleunit,newscaleunitok] = string2num( x.scaleunitText );
        if isempty(handles.mesh)
            gGlobalProps.distunitname = x.distunitText;
        elseif newscaleunitok
            attemptCommand( handles, true, false, ...
                'setproperty', ...
                'distunitname', x.distunitText, ...
                'scalebarvalue', max(0,newscaleunit) );
            handles = guidata( hObject );
            setscalebarsize( handles );
        else
            attemptCommand( handles, true, false, ...
                'setproperty', ...
                'distunitname', x.distunitText );
            handles = guidata( hObject );
            setscalebarsize( handles );
        end
    end


% --------------------------------------------------------------------
function rescaleItem_Callback(hObject, eventdata, handles)
    global gGlobalProps
    if isempty(handles.mesh)
        setGlobals();
        spaceunitname = gGlobalProps.distunitname;
        timeunitname = gGlobalProps.timeunitname;
    else
        spaceunitname = handles.mesh.globalProps.distunitname;
        timeunitname = handles.mesh.globalProps.timeunitname;
    end
    x = unitsfig( 'spaceunitname', spaceunitname, ...
                  'timeunitname', timeunitname );
    if isstruct(x)
        [timescale,ok1] = getDoubleFromString( 'Time scale', x.timescaleText, 0 );
        [spacescale,ok2] = getDoubleFromString( 'Space scale', x.spacescaleText, 0 );
        if ok1 && ok2
            if isempty(handles.mesh)
                gGlobalProps.distunitname = x.newspaceunitText;
                gGlobalProps.timeunitname = x.newtimeunitText;
            else
                attemptCommand( handles, true, true, ...
                    'rescale', ...
                    'spaceunitname', x.newspaceunitText, ...
                    'spaceunitvalue', spacescale, ...
                    'timeunitname', x.newtimeunitText, ...
                    'timeunitvalue', timescale );
            end
        end
    end


function defaultViewFromCurrentItem_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        cp = getCameraParams( handles.picture );
            attemptCommand( handles, true, false, 'setproperty', ...
                'defaultViewParams', cp );
    end

function setDefaultViewItem_Callback(hObject, eventdata, handles)
    if isempty(handles.mesh)
        fprintf( 1, 'No mesh.\n' );
        return;
    end
    az = handles.mesh.globalProps.defaultazimuth;
    el = handles.mesh.globalProps.defaultelevation;
    x = azelDlg( 'azimuth', az, 'elevation', el );
    if isstruct(x)
        [newaz,n1] = sscanf( x.azimuthText, '%f' );
        [newel,n2] = sscanf( x.elevationText, '%f' );
        if n1==0
            complain( 'Invalid azimuth ''%s''. Number expected.', ...
                x.azimuthText );
            return;
        elseif n2==0
            complain( 'Invalid elevation ''%s''. Number expected.', ...
                x.elevationText );
            return;
        elseif (newaz ~= az) || (newel ~= el)
            attemptCommand( handles, true, false, ...
                'setproperty', ...
                'defaultazimuth', newaz, ...
                'defaultelevation', newel );
        end
    end


% --------------------------------------------------------------------
function autozoomcentreItem_Callback(hObject, eventdata, handles) %#ok<*INUSL>
    newstate = toggleCheckedMenuItem( hObject );
    checkMenuItem( handles.autozoomItem, newstate );
    checkMenuItem( handles.autocentreItem, newstate );
    attemptCommand( handles, false, false, ...
                    'plotoptions', ...
                    'autozoom', newstate, ...
                    'autocentre', newstate );


% --------------------------------------------------------------------
function autozoomItem_Callback(hObject, eventdata, handles)
    newstate = toggleCheckedMenuItem( hObject );
    otherstate = ischeckedMenuItem( handles.autocentreItem );
    checkMenuItem( handles.autozoomcentreItem, newstate && otherstate );
    attemptCommand( handles, false, false, ...
                    'plotoptions', ...
                    'autozoom', newstate );

% --------------------------------------------------------------------
function autocentreItem_Callback(hObject, eventdata, handles)
    newstate = toggleCheckedMenuItem( hObject );
    otherstate = ischeckedMenuItem( handles.autozoomItem );
    checkMenuItem( handles.autozoomcentreItem, newstate && otherstate );
    attemptCommand( handles, false, false, ...
                    'plotoptions', ...
                    'autocentre', newstate );

function unimplementedItem(hObject, eventdata, handles)
    queryDialog( 1, 'Unimplemented', ...
        '"%s" is not yet implemented.', get( hObject, 'Label' ) );

function viewpointdirname = findViewpointDir( handles )
    viewpointdirname = fullfile( handles.userProjectsDir, 'viewpoints' );

function [olddir,viewpointdirname] = goToViewpointDir( handles )
    olddir = [];
    viewpointdirname = findViewpointDir( handles );
    ok = true;
    if ~exist( viewpointdirname, 'file' )
        [ok,msg,msgid] = mkdir( viewpointdirname );
    end
    if ok
        olddir = trycd( viewpointdirname );
    end
    if ~ok
        fprintf( 1, 'Cannot create viewpoints directory %s.\n', viewpointdirname );
        warning( msgid, msg );
    elseif isempty( olddir )
        fprintf( 1, 'Cannot find or create folder %s.\n', ...
            viewpointdirname );
        return;
    end

% --------------------------------------------------------------------
function saveviewItem_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        return;
    end
    olddir = goToViewpointDir( handles );
    if isempty( olddir )
        return;
    end
    [viewfilename, viewpathname, filterindex] = uiputfile('*.mat','Viewpoint file');
    trycd( olddir );
    if filterindex==0
        % User cancelled.
        return;
    end
    ourViewParams = handles.mesh.plotdefaults.ourViewParams; %#ok<NASGU>
    matlabViewParams = handles.mesh.plotdefaults.matlabViewParams; %#ok<NASGU>
    save( fullfile( viewpathname, viewfilename ), 'ourViewParams', 'matlabViewParams' );
    trycd( olddir );
    fprintf( 1, 'Saved viewpoint as %s in %s.\n', viewfilename, viewpathname );

function loadView( handles, viewfilename )
    x = load( viewfilename );
    haveOurViewParams = isfield( x, 'ourViewParams' );
    haveMatlabViewParams = isfield( x, 'matlabViewParams' );
    if (~haveOurViewParams) && (~haveMatlabViewParams)
        queryDialog( 1, 'File %s does not contain any viewpoints.', viewfilename );
        return;
    end
    plotparams = {};
    if haveOurViewParams
        plotparams = [ plotparams, 'ourViewParams', x.ourViewParams ];
        if ~haveMatlabViewParams
            plotparams = [ plotparams, 'matlabViewParams', cameraParamsFromOurViewParams( x.ourViewParams ) ];
        end
    end
    if haveMatlabViewParams
        plotparams = [ plotparams, 'matlabViewParams', x.matlabViewParams ];
        if ~haveOurViewParams
            plotparams = [ plotparams, 'ourViewParams', ourViewParamsFromCameraParams( x.matlabViewParams ) ];
        end
    end
    attemptCommand( handles, false, false, 'plotview', plotparams{:} );


% --------------------------------------------------------------------
function loadviewItem_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh )
        return;
    end
    olddir = goToViewpointDir( handles );
    if isempty( olddir )
        return;
    end
    [viewfilename, viewpathname, filterindex] = uigetfile('*.mat','Choose a viewpoint file');
    trycd( olddir );
    if filterindex==0
        % User cancelled.
        return;
    end
    loadView( handles, fullfile( viewpathname, viewfilename ) )


% --------------------------------------------------------------------
function useviewItem_Callback(hObject, eventdata, handles)
    

% --------------------------------------------------------------------
function stagesMenu_Callback(hObject, eventdata, handles)

    
% --------------------------------------------------------------------
function movieMenu_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function addFrameItem_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh ) && handles.mesh.globalProps.makemovie
        handles.mesh = recordframe( handles.mesh );
        guidata( hObject, handles );
    end


% --------------------------------------------------------------------
function addMovieFrames(hObject, handles, msg, varargin)
    if ~isempty( handles.mesh )
        ud = get( hObject, 'UserData' );
        frames = 60;
        if ~isempty( ud ) && isfield( ud, 'frames' )
            frames = ud.frames;
        end
        [frames, ok] = askForInt( msg, 'How many frames?', frames );
        if ok
            ud.frames = frames;
            set( hObject, 'UserData', ud );
            attemptCommand( handles, true, false, 'gyrate', ...
                'frames', frames, varargin{:} );
        end
    end

% --------------------------------------------------------------------
function spinItem_Callback(hObject, eventdata, handles)
    addMovieFrames( hObject, handles, ...
        'Spin movie', 'spin', 1, 'tilt', 0 );

% --------------------------------------------------------------------
function tiltItem_Callback(hObject, eventdata, handles)
    addMovieFrames( hObject, handles, ...
        'Tilt movie', 'spin', 0, 'tilt', 1, 'tiltangle', 89.99 );

% --------------------------------------------------------------------
function gyrateItem_Callback(hObject, eventdata, handles)
    addMovieFrames( hObject, handles, ...
        'Gyrate movie', 'spin', 1, 'tilt', 1, 'tiltangle', 45 );

% --------------------------------------------------------------------
function compressionQualityItem_Callback(hObject, eventdata, handles)
    [quality, ok] = askForInt( 'Set compression quality (0-100)', '', handles.quality );
    if ok
        handles.quality = quality;
        guidata( hObject, handles );
    end


% --------------------------------------------------------------------
function frameRateItem_Callback(hObject, eventdata, handles)
    [fps, ok] = askForInt( 'Set frames per second', '', handles.fps );
    if ok
        handles.fps = fps;
        set( handles.fpsText, 'String', sprintf( '%d', handles.fps ) );
        guidata( hObject, handles );
    end


function fpsText_Callback(hObject, eventdata, handles)
    [fps, ok] = getIntFromDialog( handles.fpsText );
    if ok
        handles.fps = fps;
        guidata( hObject, handles );
    else
        beep;
        set( handles.fpsText, 'String', sprintf( '%d', handles.fps ) );
    end
    

function fpsText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in autonamemovie.
function autonamemovie_Callback(hObject, eventdata, handles)
    meshSetBoolean( handles, 'autonamemovie', hObject );


% --------------------------------------------------------------------
function thumbnailItem_Callback(hObject, eventdata, handles)
	if ~isempty(handles.mesh) && ~isempty(handles.mesh.globalProps.projectdir)
        attemptCommand( handles, false, false, 'snapshot', '', 'thumbnail', 1 );
        drawThumbnail( handles );
    else
        complain( 'No project.' );
	end


% --------------------------------------------------------------------
function miscMenu_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function aboutMenu_Callback(hObject, eventdata, handles)

function dateItem_Callback(hObject, eventdata, handles)

% --- Executes on button press in archiveButton.
function archiveButton_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        attemptCommand( handles, true, false, 'archive' );
    end



% --- Executes on button press in asideRButton.
function asideRButton_Callback(hObject, eventdata, handles)
    aval = get( handles.asideRButton, 'Value' );
    if aval==0
        set( handles.asideRButton, 'Value', 1 );
    else
        set( handles.bsideRButton, 'Value', 0 );
        notifyPlotChange( handles, 'decorateAside', true );
    end

% --- Executes on button press in bsideRButton.
function bsideRButton_Callback(hObject, eventdata, handles)
    bval = get( handles.bsideRButton, 'Value' );
    if bval==0
        set( handles.bsideRButton, 'Value', 1 );
    else
        set( handles.asideRButton, 'Value', 0 );
        notifyPlotChange( handles, 'decorateAside', false );
    end

function sparsityText_Callback(hObject, eventdata, handles)
    [ v, ok1 ] = getDoubleFromDialog( hObject );
    if ok1
        notifyPlotChange( handles, 'sparsedistance', max(v,0) );
    end

function sparsityText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function multiBrightenText_Callback(hObject, eventdata, handles)
    [ v, ok1 ] = getDoubleFromDialog( hObject );
    if ok1
        notifyPlotChange( handles, 'multibrighten', min(max(v,0),1) );
    end

function multiBrightenText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function totalCellsText_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        [totalcells,ok] = getIntFromDialog( handles.totalCellsText, 0 );
        if ok
            attemptCommand( handles, false, false, ...
                'setproperty', 'inittotalcells', totalcells );
        end
    end

function totalCellsText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in simplifySecondLayerButton.
function simplifySecondLayerButton_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh ), return; end
    if ~hasSecondLayer( handles.mesh ), return; end
    if get( handles.runFlag, 'Value' )
        complain( 'Cannot simplify bio layer while simulation in progress.' );
    end
    fprintf( 1, 'simplifySecondLayerButton_Callback\n' );
    handles.mesh.secondlayer = deleteBoringCells( handles.mesh.secondlayer );
    handles = GUIPlotMesh( handles );
    announceSimStatus( handles );
    guidata( hObject, handles );


% --- Executes on button press in flipOrientationButton.
function flipOrientationButton_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        attemptCommand( handles, false, true, ...
            'fliporientation' );
    end


function setLegendItem_Callback(hObject, eventdata, handles)
    % Bring up a dialog to get a string from the user.
    % Check the string for validity and use it as the format for
    % the legend.
    % Allow a string of the form @asdasd to mean "invoke function asdasd(m)
    % to generate the legend string on each iteration.
    % Or maybe not.  Just have special @ formats to access various
    % properties of the mesh: iterations, morphogen.
    % Can the interaction function invoke GUI commands?  E.g. to display
    % different mesh properties at different times?
    % Maybe this function should just set a static string to be inserted
    % into the legend?
    if ~isempty(handles.mesh)
        x = askForLegendDlg( 'initialvalue', handles.mesh.globalProps.legendTemplate );
        if (~isempty(x)) && isfield( x, 'editableText' );
            attemptCommand( handles, false, false, ...
                'setproperty', 'legendTemplate', x.editableText );
        end
    end

function nodeNumbersItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'nodenumbering', hObject );

function edgeNumbersItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'edgenumbering', hObject );

function FENumbersItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'FEnumbering', hObject );

function staticDecorItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeCheckedMenuItem( handles, 'staticdecor', hObject );
    
% --------------------------------------------------------------------
function canvasColorsItem_Callback(hObject, eventdata, handles)
    % Bring up dialog to select canvas colors.
    if isempty( handles.mesh )
        return;
    end
    facecolor = handles.mesh.plotdefaults.canvascolor;
    edgecolor = handles.mesh.plotdefaults.FElinecolor;
    x = canvasColorsDlg( 'facecolor', facecolor, 'edgecolor', edgecolor );
    if ~isstruct(x)
        return;
    end
    if any( x.facecolor ~= handles.mesh.plotdefaults.canvascolor ) ...
            || any( x.edgecolor ~= handles.mesh.plotdefaults.FElinecolor )
        notifyPlotChange( handles, 'canvascolor', x.facecolor, 'FElinecolor', x.edgecolor );
    end

function replotItem_Callback(hObject, eventdata, handles)
    if ~isempty( handles.mesh )
        handles.mesh = leaf_plot( handles.mesh );
        guidata( hObject, handles );
    end

function setSelectedCompressor( codecMenu, codecItem )
    menuItems = get(codecMenu,'Children');
    for i=1:length(menuItems)
        if menuItems(i)==codecItem
            set( menuItems(i), 'Checked', 'on' );
        else
            set( menuItems(i), 'Checked', 'off' );
        end
    end
    
function setNamedCompressor( codecMenu, codecName )
    codecItems = get( codecMenu, 'Children' );
    foundit = false;
    for i=1:length(codecItems)
        if strcmp( codecName, get( codecItems(i), 'Label' ) )
            foundit = true;
            setSelectedCompressor( codecMenu, codecItems(i) );
            break;
        end
    end
    if ~foundit
        fprintf( 1, 'Unknown compressor %s requested.\n', ...
            codecName );
    end
    
function compressorItem_Callback(hObject, eventdata, handles) %#ok<*DEFNU>
    setSelectedCompressor( handles.codecMenu, hObject )
    if isempty( handles.mesh )
        saveGFtboxConfig( handles );
    end

function codecMenu_Callback(hObject, eventdata, handles)
    % Nothing.


function colorVariationText_Callback(hObject, eventdata, handles)
    [colorvariation,ok] = getDoubleFromDialog( hObject );
    if ok
        meshSetProperty( handles, 'colorvariation', colorvariation );
    end

function colorVariationText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function multiplotItem_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        menuindexes = get( handles.displayedGrowthMenu, 'UserData' );
        nummgens = length(handles.mesh.mgenIndexToName);
        menutomesh = zeros( 1, nummgens );
        mgenNames = cell( 1, nummgens );
        for i=1:nummgens
            mname = handles.mesh.mgenIndexToName{i};
            menupos = menuindexes.(mname);
            mgenNames{menupos} = mname;
            menutomesh( menupos ) = i;
        end
        currentmgens = FindMorphogenName( handles.mesh, ...
                            handles.mesh.plotdefaults.defaultmultiplot );
%         ud = get( handles.drawmulticolor, 'Userdata' );
%         if ~isempty(ud) && isfield( ud, 'morphogens' )
%             currentmgens = FindMorphogenName( handles.mesh, ud.morphogens );
%         else
%             currentmgens = {};
%         end
        
        x = multiplotDlg( 'morphogens', mgenNames, ...
                          'selected', currentmgens );
        if isstruct(x)
            selectedmgens = FindMorphogenName( handles.mesh, menutomesh( x.mgenListbox.values ) );
            attemptCommand( handles, false, false, ...
                'plotoptions', ...
                'morphogen', selectedmgens, ...
                'defaultmultiplot', selectedmgens );
%             set( handles.drawmulticolor, ...
%                 'Value', true, ...
%                 'Userdata', struct( 'morphogens', selectedmgens ) );
            handles = guidata( hObject );
            set( handles.inputSelectButton, 'Value', true );
            set( handles.drawmulticolor, 'Value', true );
            plotMgensFromGUI( handles );
        end
    end

function stripsaveItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );

function validateMeshItem_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        [result,handles.mesh] = validmesh( handles.mesh, true );
        guidata( hObject, handles );
        if result
            fprintf( 1, 'Mesh is ok.\n' );
            queryDialog( 1, 'Note', 'Mesh is ok.' );
        else
            fprintf( 1, 'Mesh has problems.\n' );
            beep;
            queryDialog( 1, 'Warning', 'Mesh is in an invalid state. See command window for details.' );
        end
    end

function useprevdispItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );
    usePrevDispAsEstimate = ischeckedMenuItem(hObject);
    meshSetProperty( handles, ...
        'usePrevDispAsEstimate', usePrevDispAsEstimate );

function tensorPropertyMenu_Callback(hObject, eventdata, handles)
    notifyPlotChange( handles, ...
        'outputaxes', lower(unspace(getMenuSelectedLabel( handles.tensorPropertyMenu ))) );
    handles = guidata( hObject );
    setMyLegend( handles.mesh );

function tensorPropertyMenu_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


function mgenVersionMenu_Callback(hObject, eventdata, handles)
    % Nothing.

function mgenVersionItem_Callback(hObject, eventdata)
    handles = guidata( hObject );
    [v,c,e] = sscanf( get( hObject, 'Tag' ), '%d', 1 );
    if isempty(e)
        handles.defaultMgenVersion = v;
        fprintf( 1, 'Version %d selected.\n', v );
        ch = get( get( hObject, 'Parent' ), 'Children' );
        for i=1:length(ch)
            if ch(i)==hObject
                set( ch(i), 'Checked', 'on' );
            else
                set( ch(i), 'Checked', 'of' );
            end
        end
        guidata( hObject, handles );
    end


function mgenUpgradeMenu_Callback(hObject, eventdata, handles)

function mgenUpgradeItem_Callback(hObject, eventdata)
    handles = guidata( hObject );
    if ~isempty( handles.mesh )
        [v,c,e] = sscanf( get( hObject, 'Tag' ), '%d', 1 );
        if isempty(e)
            handles.mesh = convertMorphogens( handles.mesh, v );
            updateGUIFromMesh( handles );
            handles = GUIPlotMesh( handles );
            guidata( hObject, handles );
        end
    end


function rotateMeshButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        angle = get( handles.rotateslider, 'Value' );
        if hObject==handles.rotateNegMeshButton
            angle = -angle;
        end
        if get( handles.rotateXButton, 'Value' )
            ax = 'X';
        elseif get( handles.rotateYButton, 'Value' )
            ax = 'Y';
        elseif get( handles.rotateZButton, 'Value' )
            ax = 'Z';
        else
            return;
        end
        attemptCommand( handles, true, true, ...
            'rotate', ax, angle );
    end

function rotateXButton_Callback(hObject, eventdata, handles)
    set( handles.rotateYButton, 'Value', 0 );
    set( handles.rotateZButton, 'Value', 0 );


function rotateYButton_Callback(hObject, eventdata, handles)
    set( handles.rotateXButton, 'Value', 0 );
    set( handles.rotateZButton, 'Value', 0 );


function rotateZButton_Callback(hObject, eventdata, handles)
    set( handles.rotateXButton, 'Value', 0 );
    set( handles.rotateYButton, 'Value', 0 );


function XrotateSlider_Callback(hObject, eventdata, handles)
    fprintf( 1, 'XrotateSlider_Callback h %f\n', hObject );
    get( hObject )


function rotateslider_Callback(hObject, eventdata, handles)


function rotateslider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


function rotatetext_Callback(hObject, eventdata, handles)


function rotatetext_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function emptyCallback(hObject, eventdata, handles)

function dissectButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        attemptCommand( handles, true, true, ...
            'dissect' );
    end


function explodeButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        attemptCommand( handles, true, true, ...
            'explode', 1 );
    end


function flattenButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        attemptCommand( handles, true, true, ...
            'flatten', 'interactive', true );
    end


% --------------------------------------------------------------------
function RecordMeshes_Callback(hObject, eventdata, handles)
% hObject    handle to RecordMeshes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    if strcmp(get(hObject,'Checked'),'off')
        set(hObject,'Checked','on');
        if ~isempty(handles.mesh)
            handles.mesh.globalProps.RecordMeshes.flag=true;
            handles.mesh.globalProps.RecordMeshes.saveframe=false;
        end
    else
        set(hObject,'Checked','off');
        if ~isempty(handles.mesh)
            handles.mesh.globalProps.RecordMeshes.flag=false;
            handles.mesh.globalProps.RecordMeshes.saveframe=false;
        end
    end
    guidata( handles.output, handles );


function allowNegativeGrowth_Callback(hObject, eventdata, handles)
    allowNeg = get(hObject,'Value')==1;
    meshSetProperty( handles, ...
        'allowNegativeGrowth', allowNeg );
    if ~isempty(handles.mesh)
        handles = guidata( hObject );
        handles.mesh = disallowNegativeGrowth( handles.mesh );
        handles = GUIPlotMesh( handles );
        guidata( hObject, handles );
    end

function usefrozengradient_Callback(hObject, eventdata, handles)
    meshSetProperty( handles, ...
        'usefrozengradient', get(hObject,'Value')==1 );
    handles = guidata( hObject );
    if handles.mesh.plotdefaults.drawgradients
        notifyPlotChange( handles );  % Need to revisit this.  This doesn't change any plot options, but
                                      % still needs a replot.
    end


function addProjectsFolderItem_Callback(hObject, eventdata, handles)
    projectsdir = uigetdir( handles.userProjectsDir, 'Select or create a projects folder:' );
    if projectsdir ~= 0
        wasBusy = setGFtboxBusy( handles, true );
        % Search for the new directory in the list of projects directories.
        dirindex = 0;
        for i=1:length( handles.userProjectsDirs )
            if strcmp( handles.userProjectsDirs{i}, projectsdir )
                dirindex = i;
                break;
            end
        end
        % Add the new directory to the list of projects directories if it
        % is not already there.
        if dirindex==0
            handles.userProjectsDirs{ end+1 } = projectsdir;
            handles = addProjectsMenu( handles, projectsdir, false, @projectMenuItemCallback );
        end
        % Add the new menu.
        handles = addProjectsMenu( handles, projectsdir, false, @projectMenuItemCallback );
        handles = selectDefaultProjectsMenu( handles, projectsdir );
        % Clean up.
        saveGFtboxConfig( handles );
        setGFtboxBusy( handles, wasBusy );
        guidata( hObject, handles );
    end


% --------------------------------------------------------------------
function Font_Size_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function plotMenu_Callback(hObject, eventdata, handles)
    % Find viewpoint files in the viewpoints directory.
    % Enter them in the submenu of useviewItem.
    % Give each a callback.

    % Get dir listing of viewpoints directory.
    viewpointdirname = findViewpointDir( handles );
    x = dirnames( fullfile( viewpointdirname, '*.mat' ) );
    uh = handles.useviewItem;
    delete( get( uh, 'Children' ) );
    if isempty(x)
            uimenu( 'Parent', uh, ...
                    'Label', 'None', ...
                    'Callback', [], ...
                    'Enable', 'off' );
    else
        for i=1:length(x)
            label = regexprep( x{i}, '\.mat$', '' );
            uimenu( 'Parent', uh, ...
                    'Label', label, ...
                    'Callback', @loadoneviewItem_Callback );
        end
    end

function loadoneviewItem_Callback(hObject, eventdata)
    handles = guidata( hObject );
    fprintf( 1, 'loadoneviewItem_Callback: %s\n', get( hObject, 'Label' ) );
    viewpointdirname = findViewpointDir( handles );
    loadView( handles, fullfile( viewpointdirname, [ get( hObject, 'Label' ), '.mat' ] ) );

% --------------------------------------------------------------------
function makefigItem_Callback(hObject, eventdata, handles)
    if isempty( handles.mesh ) || isempty( handles.mesh.globalProps.projectdir )
        % Nothing.
    else
        modeldir = fullfile( handles.mesh.globalProps.projectdir, ...
                             handles.mesh.globalProps.modelname );
        try
            makefig( modeldir );
        catch
            x = lasterror;
            s = sprintf( 'makefig failed:\n%s', x.message );
            msgbox( s );
        end
    end


% --------------------------------------------------------------------
function blackMenuItem_Callback(hObject, eventdata, handles)
    if ~ischeckedMenuItem( hObject )
        setPlotBackground( handles, [0 0 0] );
        checkMenuItem( handles.blackMenuItem, true );
        checkMenuItem( handles.whiteMenuItem, false );
    end

% --------------------------------------------------------------------
function whiteMenuItem_Callback(hObject, eventdata, handles)
    if ~ischeckedMenuItem( hObject )
        setPlotBackground( handles, [1 1 1] );
        checkMenuItem( handles.blackMenuItem, false );
        checkMenuItem( handles.whiteMenuItem, true );
    end

% --------------------------------------------------------------------
function legendMenuItem_Callback(hObject, eventdata, handles)
    visible = toggleShowHideMenuItem( hObject );
    set( handles.legend, 'Visible', boolchar( visible, 'on', 'off' ) );
    attemptCommand( handles, false, false, ...
        'plotoptions', 'drawlegend', visible );

function scalebarMenuItem_Callback(hObject, eventdata, handles)
    visible = toggleShowHideMenuItem( hObject );
    set( handles.scalebar, 'Visible', boolchar( visible, 'on', 'off' ) );

function scalebarscaleItem_Callback(hObject, eventdata, handles)
    % Get a scaling factor from the user.
    % Set the corresponding property in plotdefaults.scalebarscaling.


% --------------------------------------------------------------------
function thicknessMenuItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'thick', hObject );

function seamsMenuItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'drawseams', hObject );

function cellsonbothsidesItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeCheckedMenuItem( handles, 'cellsonbothsides', hObject );

function rectifyVerticalsItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );
    meshSetProperty( handles, 'rectifyverticals', ischeckedMenuItem( hObject ) );

function staticReadOnlyItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );
    meshSetProperty( handles, 'staticreadonly', ischeckedMenuItem( hObject ) );

function allowSparseItem_Callback(hObject, eventdata, handles)
    toggleCheckedMenuItem( hObject );
    meshSetProperty( handles, 'allowsparse', ischeckedMenuItem( hObject ) );


function inputSelectButton_Callback(hObject, eventdata, handles)
    selectButton( handles.inputSelectButton, handles.outputSelectButton );
    plotMgensFromGUI( handles );


% --- Executes on button press in outputSelectButton.
function outputSelectButton_Callback(hObject, eventdata, handles)
    selectButton( handles.outputSelectButton, handles.inputSelectButton );
    if get( hObject, 'Value' )
        oq = plottedOutputQuantity( handles );
        notifyPlotChange( handles, 'outputquantity', oq );
    else
        notifyPlotChange( handles, 'blank', true );
    end
    handles = guidata( hObject );
    setMyLegend( handles.mesh );

% --------------------------------------------------------------------
function axesMenuItem_Callback(hObject, eventdata, handles)
    axeson = toggleShowHideMenuItem( hObject );
    if isempty( handles.mesh )
        if axeson
            axis( handles.picture, 'on' );
        else
            axis( handles.picture, 'off' );
        end
    else
        attemptCommand( handles, false, false, ...
            'showaxes', axeson );
    end

% --------------------------------------------------------------------
function displacementsMenuItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'drawdisplacements', hObject );

% --------------------------------------------------------------------
function normalsMenuItem_Callback(hObject, eventdata, handles)
    notifyPlotChangeShowHide( handles, 'drawnormals', hObject );

function lightMenuItem_Callback(hObject, eventdata, handles)
    label = get( hObject, 'Label' );
    turnLightOn = strcmp( label, 'Turn Light On' );
    if turnLightOn
        set( hObject, 'Label', 'Turn Light Off' );
    else
        set( hObject, 'Label', 'Turn Light On' );
    end
    attemptCommand( handles, false, false, ...
        'light', ...
        turnLightOn );

function opacityItem_Callback(hObject, eventdata, handles)
    [x,ok] = getplotparamfloat( hObject, handles, ...
                                'Set opacity of mesh', ...
                                'Allowed range 0 (transparent) to 1 (opaque)', ...
                                [0 1], ...
                                'FaceAlpha', ...
                                'alpha' );
    
function ambientItem_Callback(hObject, eventdata, handles)
    [x,ok] = getplotparamfloat( hObject, handles, ...
                                'Set ambient light level', ...
                                'Allowed range 0 to 1', ...
                                [0 1], ...
                                'AmbientStrength', ...
                                'ambientstrength' );

function [x,ok] = getplotparamfloat( hObject, handles, ...
                                     dlgtitle, dlgprompt, bounds,  ...
                                     plotattribute, ...
                                     meshparamname )
    ud = get( hObject, 'UserData' );
    [x,ok] = askForFloat( dlgtitle, dlgprompt, ud.currentvalue, bounds );
    if ~ok
        return;
    end
    if ud.currentvalue == x
        return;
    end
    ud.currentvalue = x;
    set( hObject, 'UserData', ud );
    if ~ok
        return;
    end
    if get( handles.runFlag, 'Value' )==0
        setPatchProperty( handles.picture, plotattribute, x );
        if ~isempty( handles.mesh )
            handles.mesh.plotdefaults.(meshparamname) = x;
            guidata( hObject, handles );
            saveStaticPart( handles.mesh );
        end
    else
        notifyPlotChange( handles, propname, ud.currentvalue );
    end

function setPatchProperty( ax, propname, propval )
    c = get( ax, 'Children' );
    pc = strcmp( get(c,'Type'), 'patch');
    try
        set( c(pc), propname, propval );
    catch
        x = 1;
    end

function stereoItem_Callback(hObject, eventdata, handles)
    queryDialog( 1, 'Note', 'Stereo is not yet supported.' );
    return;
    if ~isfield( handles, 'stereoparams' )
      % handles.stereoparams = struct( 'offset');
    end
  % x = stereoParamsDlg( handles.stereoparams )
    x = stereoParamsDlg()
    if (~isstruct(x)) && (x == -1)
        % User cancelled.
        return;
    end
    x = safermfield( x, 'cancelButton', 'okButton' )
    %{
x has these fields:
    enableStereoCheckbox: 1
        screensizeToggle: 0
        imageSpacingText: '1024'
            vergenceText: '2.5'
               direction: '+h'
    %}
    if x.enableStereoCheckbox
        if ~isfield( handles, 'secondfigure' )
        else
        end
        % if second figure not present
        %   Create it.
        %   Copy main plot to second.
        % 	Adjust view directions.
        % else
        %   if vergence was changed then adjust view directions.
    else
        % if second figure present
        %   Destroy it.
        if isfield( handles, 'secondfigure' )
            if ishandle( handles.secondfigure )
                delete( handles.secondfigure );
            end
            handles = rmfield( handles, 'secondfigure' );
        end
    end
    handles.stereoparams = x;
    guidata( hObject, handles );


function azclipText_Callback(hObject, eventdata, handles)
    [ v, ok1 ] = getDoubleFromDialog( hObject );
    if ok1
        notifyPlotChange( handles, 'clippingAzimuth', v );
    end

function azclipText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function elclipText_Callback(hObject, eventdata, handles)
    [ v, ok1 ] = getDoubleFromDialog( hObject );
    if ok1
        notifyPlotChange( handles, 'clippingElevation', v );
    end

function elclipText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function dclipText_Callback(hObject, eventdata, handles)
    [ v, ok1 ] = getDoubleFromDialog( hObject );
    if ok1
        notifyPlotChange( handles, 'clippingDistance', v );
    end

function dclipText_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function clipCheckbox_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'doclip', hObject )

function clipbymgenCheckbox_Callback(hObject, eventdata, handles)
    notifyPlotChangeFromGUIBool( handles, 'clipbymgen', hObject )

function clipmgenButton_Callback(hObject, eventdata, handles)
    if ~isempty(handles.mesh)
        clipmgens = zeros(1,length(handles.mesh.plotdefaults.clipmgens));
        for i=1:length(handles.mesh.plotdefaults.clipmgens)
            clipmgens(i) = handles.mesh.mgenNameToIndex.(handles.mesh.plotdefaults.clipmgens{i});
        end
        clipmgens = unique(clipmgens);
        initparams = struct( ...
            'clipmgens', clipmgens, ...
            'above', handles.mesh.plotdefaults.clipmgenabove, ...
            'all', handles.mesh.plotdefaults.clipmgenall, ...
            'threshold', handles.mesh.plotdefaults.clipmgenthreshold );
        initparams.allmgens = handles.mesh.mgenIndexToName;
        result = clipmgenDlg( 'INITPARAMS', initparams );
        if isstruct(result)
            replot = handles.mesh.plotdefaults.clipbymgen;
            attemptCommand( handles, false, replot, ...
                'plotoptions', ...
                'clipmgens', handles.mesh.mgenIndexToName(result.mgens), ...
                'clipmgenthreshold', result.threshold, ...
                'clipmgenabove', result.above, ...
                'clipmgenall', result.all );
        end
    end
    
function projectsMenu_Callback(hObject, eventdata, handles)
    % Nothing.

function refreshProjectsMenu_Callback(hObject, eventdata, handles)
    handles = refreshProjectsMenu( guidata(hObject) );
    guidata( hObject, handles );

function flatlightMenuItem_Callback(hObject, eventdata, handles)
    setLightMode(hObject, handles);

function gouraudMenuItem_Callback(hObject, eventdata, handles)
    setLightMode(hObject, handles);

function phongMenuItem_Callback(hObject, eventdata, handles)
    setLightMode(hObject, handles);

function setLightMode(hObject, handles)
    % Light mode does not work.
    %{
    c = get( handles.lightmodeMenu, 'Children' );
    for i=1:length(c)
        if c(i)==hObject
            set( c(i), 'Checked', 'on' );
        else
            set( c(i), 'Checked', 'off' );
        end
    end
    notifyPlotChange( handles, 'lightmode', lower( get( c(i), 'Label' ) ) );
    %}

% --------------------------------------------------------------------
function precisionItem_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function singlePrecisionItem_Callback(hObject, eventdata, handles)
    selectMenuChoice( hObject );
    meshSetProperty( handles, 'solverprecision', 'single' );


% --------------------------------------------------------------------
function doublePrecisionItem_Callback(hObject, eventdata, handles)
    selectMenuChoice( hObject );
    meshSetProperty( handles, 'solverprecision', 'double' );


% --------------------------------------------------------------------
function solverItem_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function cgsSolverItem_Callback(hObject, eventdata, handles)
    selectMenuChoice( hObject );
    meshSetProperty( handles, 'solver', 'cgs' );


% --------------------------------------------------------------------
function lsqrSolverItem_Callback(hObject, eventdata, handles)
    selectMenuChoice( hObject );
    meshSetProperty( handles, 'solver', 'lsqr' );


% --------------------------------------------------------------------
function culaSgesvSolverItem_Callback(hObject, eventdata, handles)
    selectMenuChoice( hObject );
    meshSetProperty( handles, 'solver', 'culaSgesv' );



% --------------------------------------------------------------------
function errorTypeMenu_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function normErrorItem_Callback(hObject, eventdata, handles)
    checkErrorItems( handles, true );
    meshSetProperty( handles, 'solvertolerancemethod', 'norm' );

% --------------------------------------------------------------------
function maxabsErrorItem_Callback(hObject, eventdata, handles)
    checkErrorItems( handles, false );
    meshSetProperty( handles, 'solvertolerancemethod', 'max' );


function makecaptionItem_Callback(hObject, eventdata, handles)
    queryDialog( 1, 'Caption', 'Caption generation has not been implemented yet.' );
    if ~isempty( handles.mesh )
        
    end

function purgeProjectItem_Callback(hObject, eventdata, handles)
    attemptCommand( handles, true, false, ...
                    'purgeproject', ...
                    'recycle', true, ...
                    'confirm', true );

            

function projectMenuItemCallback(hObject, eventdata)
fprintf( 1, 'projectMenuItemCallback %s\n', get( hObject, 'Label' ) );
    ud = get( hObject, 'UserData' );
    handles = guidata( hObject );
    makeDefaultThumbnail( handles.mesh );
    if ud.readonly && isempty( handles.userProjectsDir )
        complain( 'You cannot open a system project until you have defined a user projects directory.\n' );
        return;
    end
    m = [];
    if ud.readonly
        motifsDir = fullfile( handles.userProjectsDir, 'Motifs' );
        ok = mkdir( motifsDir );
        if ~ok
            complain( '%s: Unable to create Motifs folder in your projects folder %s.\n', ...
                mfilename(), handles.userProjectsDir );
        else
            [projectdir,modelname] = fileparts( ud.modeldir );
            [m,ok] = leaf_loadmodel( handles.mesh, modelname, projectdir, ...
                'copydir', motifsDir );
        end
    else
        [projectdir,modelname] = fileparts( ud.modeldir );
        [m,ok] = leaf_loadmodel( handles.mesh, modelname, projectdir );
        if ~ok
            beep;
        end
    end
    if ok && ~isempty(m)
%         unselectProjectMenu( handles );
%         selectProjectMenu( hObject, true );
        handles = installNewMesh( handles, m );
        updateProjectMenuHighlights( handles, ud.modeldir );
        guidata(hObject, handles);
    end

    
function handles = selectDefaultProjectsMenu( handles, defaultDir )
% Search for a given directory on the Projects menu and colour the menu items
% accordingly.

    if nargin < 2
        defaultDir = '';
    else
        handles.userProjectsDir = defaultDir;
    end
    updateProjectMenuHighlights( handles, defaultDir );


% function mh = findSelectedProjectMenuItem( menus, dir, mh )
%     if isempty(dir)
%         return;
%     end
%     if nargin < 3
%         mh = [];
%     end
%     for i=1:length(menus)
%         ud = get( menus(i), 'UserData' );
%         if isempty( ud ), continue; end
%         if ~isfield( ud, 'modeldir' ), continue; end
%         if ~isAncestorDirOf( ud.modeldir, dir ), continue; end
%         % Checkmarks don't work for submenus, only menu items.
%         mh = menus(i);
%         mh1 = findSelectedProjectMenuItem( get( menus(i), 'Children' ), dir );
%         if ~isempty( mh1 )
%             mh = mh1;
%             return;
%         end
%     end


    
% --------------------------------------------------------------------
function convertToFlashItem_Callback(hObject, eventdata, handles)
    newstate = toggleCheckedMenuItem( hObject );
    meshSetProperty( handles, 'flashmovie', newstate );



% --------------------------------------------------------------------
function editMovieScriptsItem_Callback(hObject, eventdata, handles)
    queryDialog( 1, 'Not Implemented', 'Movie scripts are not yet implemented.' );


% --------------------------------------------------------------------
function newWaypointItem_Callback(hObject, eventdata, handles)
    queryDialog( 1, 'Not Implemented', 'Movie scripts are not yet implemented.' );




% --------------------------------------------------------------------
function rendererMenu_Callback(hObject, eventdata, handles)


% --------------------------------------------------------------------
function checkRendererItemFromName( handles )
    renderers = get( handles.rendererMenu, 'Children' );
    for i=1:length(renderers)
        checkMenuItem( renderers(i), ...
            strcmp( get( renderers(i), 'Label' ), handles.Renderer ) );
    end


% --------------------------------------------------------------------
function setRendererItem(hObject, handles)
    global GFtboxFigure
    handles.Renderer = get( hObject, 'Label' );
    set( GFtboxFigure, 'Renderer', handles.Renderer );
    fprintf( 1, 'Rendering method is "%s".\n', get( GFtboxFigure, 'Renderer' ) );
    renderers = get( get( hObject, 'Parent' ), 'Children' );
    for i=1:length(renderers)
        checkMenuItem( renderers(i), renderers(i)==hObject );
    end
    guidata( hObject, handles );


% --------------------------------------------------------------------
function openGLRendererItem_Callback(hObject, eventdata, handles)
% hObject    handle to openGLRendererItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    setRendererItem(hObject, handles);


% --------------------------------------------------------------------
function zbuggerRendererItem_Callback(hObject, eventdata, handles)
% hObject    handle to zbuggerRendererItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    setRendererItem(hObject, handles);


% --------------------------------------------------------------------
function paintersRendererItem_Callback(hObject, eventdata, handles)
% hObject    handle to paintersRendererItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    setRendererItem(hObject, handles);


% --------------------------------------------------------------------
function noneRendererItem_Callback(hObject, eventdata, handles)
% hObject    handle to noneRendererItem (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
    setRendererItem(hObject, handles);
