Home > ensemble_gui > ens_main.m

ens_main

PURPOSE ^

ens_main() is the entry point for the Ensemble analysis management GUI.

SYNOPSIS ^

function [mainFig] = ens_main( varargin )

DESCRIPTION ^

 ens_main() is the entry point for the Ensemble analysis management GUI.
 No arguments required. 
 The startup routine looks for a config file called 






 Note: code will look ugly unless viewed in a wide editor window (100-132 chars).

 ### we're using "z" as the main guidata variable name for convenience in development.
 this can be changed to something more meaningful but i would actually vote for leaving it as z - JG

 Change history:
 05/07/07: initial handoff of completed v1.0 - JG

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 %
0002 % ens_main() is the entry point for the Ensemble analysis management GUI.
0003 % No arguments required.
0004 % The startup routine looks for a config file called
0005 %
0006 %
0007 %
0008 %
0009 %
0010 function [mainFig] = ens_main( varargin )
0011 %
0012 % Note: code will look ugly unless viewed in a wide editor window (100-132 chars).
0013 %
0014 % ### we're using "z" as the main guidata variable name for convenience in development.
0015 % this can be changed to something more meaningful but i would actually vote for leaving it as z - JG
0016 %
0017 % Change history:
0018 % 05/07/07: initial handoff of completed v1.0 - JG
0019 %
0020 
0021 
0022 configFileName = 'ens_config.ini'; % default config file name
0023 arg_conn_id = -1;    %  -1 means no conn_id arg was passed from command line.
0024 for x = 1 : 2 : length(varargin)
0025     switch( varargin{x} )
0026         case 'config'
0027             configFileName = varargin{x+1};
0028         case 'conn_id'
0029             arg_conn_id = varargin{x+1};
0030     end
0031 end
0032 
0033 
0034 
0035 
0036 
0037 % loads all values from config file and does some other low level stuff.
0038 % creation of GUI objects is done below.
0039 % if arg_conn_id is valid, it trumps the default conn_id and/or the conn_id specified in config file
0040 z = init( configFileName, arg_conn_id ); 
0041 
0042 
0043 if( ~length(z) )
0044     msg( 'error: init() returned nothing', 'msg' );
0045     mainFig = [];
0046     return;
0047 end
0048 
0049 
0050 % create the main GUI figure
0051 z.handle.window.main = figure(    'tag', 'z.handle.window.main', ...
0052                                             'name', 'Ensemble Analysis Job Manager', ...
0053                                             'menubar', 'none', ...
0054                                             'resize', 'on', ...
0055                                             'position', [16 z.config.dim.sh-z.config.dim.h-16 z.config.dim.w z.config.dim.h] );
0056 set( z.handle.window.main,                    'resizefcn', {@resizeMainFigureCB, z.handle.window.main} );
0057 mainFig = z.handle.window.main; % store figure handle in function return variable
0058 
0059 
0060 
0061 % create panels
0062 z.handle.panel.left = uipanel(        'units', 'pixels' );
0063 z.handle.panel.midTop = uipanel(        'units', 'pixels' );
0064 z.handle.panel.midCenter = uipanel(    'units', 'pixels' );
0065 z.handle.panel.midBottom = uipanel(    'units', 'pixels' );
0066 z.handle.panel.right = uipanel(        'units', 'pixels' );
0067 z.handle.panel.bottom = uipanel(        'units', 'pixels' );
0068 z.handle.panel.top = uipanel(            'units', 'pixels' );
0069 
0070 
0071 % create input menus and widgets
0072 
0073 
0074 % experiment selection drop-down list for top panel
0075 z.handle.widget.top.selectExperiment = uicontrol( ...
0076                                             'style', 'popupmenu', ...
0077                                             'string', z.data.meta.expListStr, ...
0078                                             'parent', z.handle.panel.top, ...
0079                                             'callback', {@selectExperimentListCB, z.handle.window.main} );
0080 
0081 
0082 
0083 % button to add new analysis unit to the chain
0084 z.handle.widget.top.newan = uicontrol( ...
0085                                             'style', 'pushbutton', ...
0086                                             'parent', z.handle.panel.top, ...
0087                                             'string', 'new', ...
0088                                             'enable', 'on', ...
0089                                             'callback', {@newAnalysisCB, z.handle.window.main}, ... 
0090                                             'tag', 'z.handle.widget.top.newan' );
0091 
0092 
0093 % button to delete selected job unit from the list
0094 z.handle.widget.top.deletean = uicontrol( ...
0095                                             'style', 'pushbutton', ...
0096                                             'parent', z.handle.panel.top, ...
0097                                             'string', 'delete', ...
0098                                             'enable', 'off', ...
0099                                             'callback', {@deleteAnalysisCB, z.handle.window.main}, ... 
0100                                             'tag', 'z.handle.widget.top.deletean' );
0101 
0102 
0103 % button to quit
0104 z.handle.widget.top.quit = uicontrol( ...
0105                                             'style', 'pushbutton', ...
0106                                             'parent', z.handle.panel.top, ...
0107                                             'string', 'quit', ...
0108                                             'enable', 'on', ...
0109                                             'callback', {@quitCB, z.handle.window.main}, ... 
0110                                             'tag', 'z.handle.widget.top.quit' );
0111 
0112 
0113 % buttons to reposition analysis units in the list
0114 z.handle.widget.bottom.movedown = uicontrol( ...    
0115                                             'style', 'pushbutton', ...
0116                                             'parent', z.handle.panel.bottom, ...
0117                                             'string', '+', ...
0118                                             'enable', 'off', ...
0119                                             'callback', {@moveCB, z.handle.window.main}, ... 
0120                                             'tag', 'movedown' );
0121 z.handle.widget.bottom.moveup = uicontrol( ...    
0122                                             'style', 'pushbutton', ...
0123                                             'parent', z.handle.panel.bottom, ...
0124                                             'string', '-', ...
0125                                             'enable', 'off', ...
0126                                             'callback', {@moveCB, z.handle.window.main}, ... 
0127                                             'tag', 'moveup' );
0128 
0129 
0130 % button to add left panel selections to filt
0131 z.handle.widget.bottom.addinclude = uicontrol( ...    
0132                                             'style', 'pushbutton', ...
0133                                             'parent', z.handle.panel.bottom, ...
0134                                             'string', 'include', ...
0135                                             'enable', 'off', ...
0136                                             'callback', {@addToFilterCB, z.handle.window.main}, ... 
0137                                             'tag', 'addinclude' );
0138 
0139 % button to subtract left panel selections to filt
0140 z.handle.widget.bottom.addexclude = uicontrol( ...    
0141                                             'style', 'pushbutton', ...
0142                                             'parent', z.handle.panel.bottom, ...
0143                                             'string', 'exclude', ...
0144                                             'enable', 'off', ...
0145                                             'callback', {@addToFilterCB, z.handle.window.main}, ... 
0146                                             'tag', 'addexclude' );
0147 
0148 
0149 % save job spec
0150 z.handle.widget.bottom.save = uicontrol( ...    
0151                                             'style', 'pushbutton', ...
0152                                             'parent', z.handle.panel.top, ...
0153                                             'string', 'save', ...
0154                                             'enable', 'off', ...
0155                                             'callback', {@saveCB, z.handle.window.main}, ... 
0156                                             'tag', 'z.handle.widget.top.save' );
0157 % save with results attached?
0158 z.handle.widget.bottom.includeResults = uicontrol( ...    
0159                                             'style', 'togglebutton', ...
0160                                             'parent', z.handle.panel.top, ...
0161                                             'enable', 'off', ...
0162                                             'tag', 'z.handle.widget.top.includeResults' );
0163 
0164 % load job spec
0165 z.handle.widget.bottom.load = uicontrol( ...    
0166                                             'style', 'pushbutton', ...
0167                                             'parent', z.handle.panel.top, ...
0168                                             'string', 'load', ...
0169                                             'enable', 'on', ...
0170                                             'callback', {@loadCB, z.handle.window.main}, ... 
0171                                             'tag', 'z.handle.widget.top.load' );
0172 
0173 
0174 % execute the job spec
0175 z.handle.widget.bottom.go = uicontrol( ...    
0176                                             'style', 'pushbutton', ...
0177                                             'parent', z.handle.panel.bottom, ...
0178                                             'string', '!', ...
0179                                             'enable', 'off', ...
0180                                             'callback', {@goCB, z.handle.window.main}, ... 
0181                                             'tag', 'z.handle.widget.bottom.go' );
0182 
0183 
0184 % this button will dump debug info to the std matlab output
0185 z.handle.widget.bottom.dump = uicontrol( ...    
0186                                             'style', 'pushbutton', ...
0187                                             'parent', z.handle.panel.bottom, ...
0188                                             'string', '?', ...
0189                                             'enable', 'on', ...
0190                                             'visible', 'off', ...
0191                                             'callback', {@dumpCB, z.handle.window.main}, ... 
0192                                             'tag', 'z.handle.widget.bottom.dump' );
0193 if( 1 ) % DEBUG
0194     set( z.handle.widget.bottom.dump, 'visible', 'on' );
0195 end
0196 
0197 
0198 % this button will display info about the selected analysis unit
0199 z.handle.widget.bottom.anInfo = uicontrol( ...    
0200                                             'style', 'pushbutton', ...
0201                                             'parent', z.handle.panel.bottom, ...
0202                                             'string', 'info', ...
0203                                             'enable', 'off', ...
0204                                             'callback', {@anInfoCB, z.handle.window.main}, ... 
0205                                             'tag', 'z.handle.widget.bottom.anInfo' );
0206 
0207 % this button will set the analysis of the currently selected unit to the
0208 % currently selected analysis
0209 z.handle.widget.bottom.anAdd = uicontrol( ...    
0210                                             'style', 'pushbutton', ...
0211                                             'parent', z.handle.panel.bottom, ...
0212                                             'string', 'apply', ...
0213                                             'enable', 'off', ...
0214                                             'callback', {@anApplyCB, z.handle.window.main}, ... 
0215                                             'tag', 'z.handle.widget.bottom.anAdd' );
0216 
0217 
0218 % lists all analysis units selected for the current spec
0219 z.handle.widget.right.analysisListbox = uicontrol( ...
0220                                             'style', 'listbox', ...
0221                                             'parent', z.handle.panel.right, ...
0222                                             'string', '', ...
0223                                             'value', 0, ...
0224                                             'callback', {@analysisSelectCB, z.handle.window.main}, ...
0225                                             'tag', 'z.handle.widget.right.analysisListbox' );
0226 
0227 % populate analysis list for right-window
0228 % the analysis info has already been extracted from the database in the init() routine.
0229 % this list is static for the life of this GUI so we only have to set it up once here.
0230 if( length( z.data.meta.anList{2} ) )
0231     s = cell2str(z.data.meta.anList{2}(1));
0232     for x = 2 : length( z.data.meta.anList{2} )
0233         s = [s '|' cell2str(z.data.meta.anList{2}(x))];
0234     end
0235     set( z.handle.widget.right.analysisListbox, 'value', 1 );
0236     set( z.handle.widget.right.analysisListbox, 'string', s );
0237     set( z.handle.widget.bottom.anInfo, 'enable', 'on' );
0238 end
0239 
0240 
0241 % lists all analysis units. initially empty
0242 z.handle.widget.mid.jobSpecListbox = uicontrol( ...
0243                                             'style', 'listbox', ...
0244                                             'parent', z.handle.panel.midTop, ...
0245                                             'string', '', ...
0246                                             'value', 0, ...
0247                                             'callback', {@regenerateTreeForSelectedJob, z.handle.window.main}, ...
0248                                             'tag', 'z.handle.widget.mid.jobSpecListbox' );
0249 
0250 
0251 % create all the input widgets for the analysis unit fields appearing in the center-bottom section.
0252 % these will all be invisible initially and made visible as necessary for editing- only
0253 % 1 will ever be enabled and visible at any given time, since they all will have the same position.
0254 z.handle.widget.mid.fieldEditString = uicontrol( ...
0255                                             'style', 'edit', ...
0256                                             'string', '', ...
0257                                             'visible', 'off', ...
0258                                             'enable', 'off', ...
0259                                             'callback', {@fieldEditTextCB, z.handle.window.main}, ...
0260                                             'parent', z.handle.panel.midBottom );
0261 z.handle.widget.mid.fieldEditPopupmenu = uicontrol( ...
0262                                             'style', 'popupmenu', ...
0263                                             'string', '<none>', ...
0264                                             'visible', 'off', ...
0265                                             'enable', 'off', ...
0266                                             'callback', {@fieldEditPopupCB, z.handle.window.main}, ...
0267                                             'parent', z.handle.panel.midBottom );
0268 z.handle.widget.mid.filtDeleteButton = uicontrol( ...    
0269                                             'style', 'pushbutton', ...
0270                                             'parent', z.handle.panel.midBottom, ...
0271                                             'string', 'delete', ...
0272                                             'enable', 'off', ...
0273                                             'visible', 'off', ...
0274                                             'callback', {@filtDeleteCB, z.handle.window.main}, ... 
0275                                             'tag', 'z.handle.widget.mid.filtDeleteButton' );
0276 
0277 
0278 
0279 % these are all plain read-only text fields
0280 z.handle.text.reorder = uicontrol ( ...
0281                                             'style', 'text', ...
0282                                             'string', 'reorder', ...
0283                                             'parent', z.handle.panel.bottom );
0284 
0285 z.handle.text.includeResults = uicontrol ( ...
0286                                             'style', 'text', ...
0287                                             'string', '<-include results', ...
0288                                             'parent', z.handle.panel.top );
0289 
0290 % status message at bottom of GUI
0291 z.handle.text.status = uicontrol ( ...
0292                                             'style', 'text', ...
0293                                             'string', 'Welcome to Ensemble.', ...
0294                                             'parent', z.handle.panel.bottom );
0295 
0296 % variable name in center bottom panel (for editing analysis unit fields)
0297 z.handle.text.fieldName = uicontrol ( ...
0298                                             'style', 'text', ...
0299                                             'string', '<no edit>', ...
0300                                             'parent', z.handle.panel.midBottom );
0301 
0302 
0303 
0304 
0305 % create certain nested structs/fields here that wouldn't otherwise
0306 % exist in the beginning, because:
0307 % there are certain sections of the code that refer to these fields.
0308 % if they refer to the fields before they exist, matlab
0309 % will break. those sections could check for the existence of these
0310 % fields every time it refers to them with isfield() or whatever, but it's
0311 % so much easier and cleaner to just guarantee
0312 % that these fields will always exist, even though they may be == [].
0313 z.data.left.temp = 1;
0314 z.data.left = rmfield( z.data.left, 'temp' ); 
0315 z.data.an{1} = 1;
0316 z.data.an(1) = [];
0317 z.data.left.selected = [];
0318 
0319 
0320 % store the master struct as guidata for main figure
0321 guidata( z.handle.window.main, z );
0322 
0323 
0324 
0325 % this will always be called last when changes are made
0326 % which affect the layout or size of things - including window resizing.
0327 positionGUIElements(z);
0328 
0329 
0330 
0331 
0332 
0333 
0334 % =========================================
0335 function [] = resizeMainFigureCB( h, v, mainFig )
0336     z = guidata(mainFig);
0337     newp = get(z.handle.window.main,'position');
0338     z.config.dim.w = newp(3);
0339     z.config.dim.h = newp(4);
0340     positionGUIElements(z);
0341     guidata(z.handle.window.main,z); 
0342 
0343 
0344 
0345 
0346 
0347 
0348 % ==================================
0349 % anything that is affected by resizing of the main figure
0350 % needs to be dealt with here. this also initializes the positions at startup.
0351 function [] = positionGUIElements(z)
0352 
0353     ph = z.config.dim.h-z.config.dim.bh-z.config.dim.th-2; % panel height
0354     otl = floor( z.config.dim.w/3 ); % one thirds left
0355     ttl = 2 * floor( z.config.dim.w/3 ); % two thirds left or talk to you later
0356 
0357     h = z.handle;
0358 
0359     % find out height of the top middle panel which contains the analysis listbox
0360     % if there are no units at all, pretend listSize is 1 so the box is visible
0361     listSize = max([1 length( z.data.an )]);
0362     
0363     % set minimum height for the analysis item display in center mid panel.
0364     % this is specified in the init() fucntion, but don't let it cover
0365     % the entire panel if it's been resized to a smaller height than
0366     % the config dimension says.
0367     minAnHeight = z.config.dim.minAnTreeHeight;
0368     if( minAnHeight > ph-(z.config.dim.cbh+40) )
0369         minAnHeight = ph-(z.config.dim.cbh+40);
0370     end
0371 
0372     listHeight = listSize * 16;
0373     listHeight = min([listHeight ph-(minAnHeight+z.config.dim.cbh)]);
0374     listHeight = max([listHeight 20]);
0375     % ------------------------------------------------------------
0376 
0377 
0378     if( isfield(h,'panel') )
0379         set( z.handle.panel.left,    'position', [1 z.config.dim.bh+1 otl-1 ph] );
0380         set( z.handle.panel.midTop,'position', [otl+1 z.config.dim.bh+(ph-(listHeight+7)) otl-1 listHeight+8] );
0381         set( z.handle.panel.midCenter,'position',[otl+1 z.config.dim.bh+z.config.dim.cbh+1 otl-1 (ph-listHeight)-z.config.dim.cbh-8] );
0382         set( z.handle.panel.midBottom,'position',[otl+1 z.config.dim.bh+1 otl-1 z.config.dim.cbh] );
0383         set( z.handle.panel.right,    'position', [ttl+1 z.config.dim.bh+1 otl-1 ph] );
0384         set( z.handle.panel.bottom,'position', [1 1 z.config.dim.w-1 z.config.dim.bh] );
0385         set( z.handle.panel.top,    'position', [1 z.config.dim.bh+ph+2 z.config.dim.w-1 z.config.dim.th] );
0386     end
0387 
0388 
0389     if( isfield( h, 'text' ) )
0390         set( z.handle.text.status,    'position', [1 1 z.config.dim.w-4 17] );
0391         set( z.handle.text.fieldName,    'position', [1 3 floor(otl/3)-2 17] );
0392         set( z.handle.text.includeResults, 'position', [(2*otl)+23 1 116 17] );
0393         set( z.handle.text.reorder, 'position', [otl+42 z.config.dim.bh-z.config.dim.b1h-6 50 17] );
0394     end
0395 
0396 
0397     if( isfield( h, 'widget' ) )
0398         set( z.handle.widget.bottom.movedown, 'position', [otl z.config.dim.bh-z.config.dim.b1h-5 20 20] );
0399         set( z.handle.widget.bottom.moveup, 'position', [otl+21 z.config.dim.bh-z.config.dim.b1h-5 20 20] );
0400         set( z.handle.widget.bottom.addinclude, 'position', [floor(otl/2)-60 ...
0401                                                 z.config.dim.bh-z.config.dim.b1h-5 60 z.config.dim.b1h] );
0402         set( z.handle.widget.bottom.addexclude, 'position', [floor(otl/2)+1 ...
0403                                                 z.config.dim.bh-z.config.dim.b1h-5 60 z.config.dim.b1h] );
0404         set( z.handle.widget.bottom.save, 'position', [(2*otl)-40 1 40 20] );
0405         set( z.handle.widget.bottom.includeResults, 'position', [(2*otl)+2 1 20 20] );
0406         set( z.handle.widget.bottom.load, 'position', [otl+2 1 40 20] );
0407         set( z.handle.widget.bottom.go, 'position', [(3*otl/2)-15 z.config.dim.bh-z.config.dim.b1h-5 30 20] );
0408         set( z.handle.widget.bottom.dump, 'position', [z.config.dim.w-24 z.config.dim.bh-22 20 18] );
0409         set( z.handle.widget.bottom.anInfo, 'position', [z.config.dim.w-(otl/2)-40+46 z.config.dim.bh-22 34 18] );
0410         set( z.handle.widget.bottom.anAdd, 'position', [z.config.dim.w-(otl/2)-41 z.config.dim.bh-22 46 18] );
0411 
0412         set( z.handle.widget.top.selectExperiment, 'position', [1 1 otl-4 20] );
0413         set( z.handle.widget.top.newan, 'position', [(1.5*otl)+5 1 35 20] );
0414         set( z.handle.widget.top.deletean, 'position', [(1.5*otl)-45 1 50 20] );
0415         set( z.handle.widget.top.quit, 'position', [z.config.dim.w-37 1 35 20] );
0416 
0417         set( z.handle.widget.mid.jobSpecListbox, 'position', [1 2 otl-5 listHeight] );
0418 
0419         set( z.handle.widget.right.analysisListbox, 'position', [1 1 otl-4 ph-4] );
0420 
0421         set( z.handle.widget.mid.fieldEditString, 'position', [floor(otl/3) 3 floor((2*otl)/3)-4 17] ); 
0422         set( z.handle.widget.mid.fieldEditPopupmenu, 'position', [floor(otl/3) 3 floor((2*otl)/3)-4 20] ); 
0423         set( z.handle.widget.mid.filtDeleteButton, 'position', [floor(otl/2)+10 1 50 20] ); 
0424 
0425     end
0426 
0427 
0428 
0429 
0430 % =====================================================================
0431 % ### do we want to add functionality to save window position and size?
0432 %
0433 function [] = loadCB( a1, a2, fig )
0434 
0435     z = guidata( fig );
0436 
0437     pos = get( fig, 'position' );
0438     x = pos(1) + floor(z.config.dim.w/3);
0439     y = z.config.dim.sh - pos(2) - (z.config.dim.h - z.config.dim.th + z.config.dim.bh);
0440 
0441     [fname, pname] = uigetfile( '*.job', 'location', [x y] );
0442     fullname = fullfile( pname, fname );
0443 
0444     % matlab won't let you select a file that is a directory or doesn't exist.
0445     % so all we have to worry about is that it has .job as the extension
0446     if( length( fname ) < 5 )
0447         msg( 'no load file specified', 'status' );
0448         return;
0449     end        
0450     if( length( strfind( fname, '.job' )) ~= 1  )
0451         msg( 'wrong file type  error: GH54', 'errbox' ); 
0452         msg( sprintf( 'the file you specified is not a .job file: %s', fullname ), 'status' ); 
0453         return;
0454     end
0455 
0456     % -mat forces matlab to treat the .job file type as if it were a .mat file
0457     % old will be a struct with one element: z
0458     % the data that was present when the file was saved will only
0459     % be present if the checked the box to include results in the save.
0460     % old.z.an{x}.results  could potentially be very large in that case.
0461     old = load( '-mat', fullname );
0462 
0463     if( isfield( old, 'z' ) )
0464 
0465         data = old.z.data;
0466         if( isfield( data, 'an' ) )
0467             z.data.an = old.z.data.an;
0468             % need to do this here, before it's rendered, in case the list is currently empty.
0469             % if it is, then jobSpecListBox's value is 0 and matlab will complain
0470             set( z.handle.widget.mid.jobSpecListbox, 'value', 1 );
0471 
0472             % if a previous middle tree exists, delete it and its container --------
0473             % NOTE: this code is copied from jobSpecSelectCB- to make it a function,
0474             % we'd have to save and load guidata() here again to pass the info back
0475             % and forth which is costly so as long as it's only duplicated
0476             % once it's ok for now.
0477             h = z.handle;
0478             if( isfield( h, 'container' ) )
0479                 c = h.container;
0480                 if( isfield( c, 'midTree' ) )
0481                     delete( z.handle.container.midTree );
0482                     z.handle.container = rmfield( z.handle.container, 'midTree' );
0483                 end
0484             end
0485             % ----------------------------------------------------------------------
0486 
0487 
0488             % ### might be nice to automatically generate the uitree for the 1st unit in the loaded list
0489 
0490         else
0491             msg( 'your file loaded, but no analysis units were found within', 'msgbox' );    
0492         end
0493 
0494     else
0495             msg( 'your file loaded, but no z data was found', 'msgbox' );    
0496     end
0497 
0498 
0499     % if we have nothing in the job list for whatever the reason, disable some buttons
0500     % but since we can't save an empty analysis list, we should
0501     % never be able to load one, so length(a.data.an) should always be > 0 here
0502     if( length( z.data.an ) )
0503         set( z.handle.widget.bottom.save, 'enable', 'on' );
0504         set( z.handle.widget.bottom.includeResults, 'enable', 'on' );
0505         set( z.handle.widget.top.deletean, 'enable', 'on' );
0506         set( z.handle.widget.bottom.anAdd, 'enable', 'on' );
0507         
0508         % if all analysis units have callback functions specified, enable execute button
0509         set( z.handle.widget.bottom.go, 'enable', 'on' );
0510         for x = 1 : length( z.data.an )
0511             if( ~length( z.data.an{x}.fun ) )
0512                 set( z.handle.widget.bottom.go, 'enable', 'off' );
0513             end
0514         end
0515         
0516     else
0517         set( z.handle.widget.bottom.save, 'enable', 'off' );
0518         set( z.handle.widget.bottom.includeResults, 'enable', 'off' );
0519         set( z.handle.widget.top.deletean, 'enable', 'off' );
0520         set( z.handle.widget.bottom.go, 'enable', 'off' );
0521         set( z.handle.widget.bottom.anAdd, 'enable', 'off' );
0522     end
0523 
0524     % allow reordering position within unit list if there's at least 2
0525     if( length( z.data.an ) > 1 )
0526         set( z.handle.widget.bottom.moveup, 'enable', 'on' );
0527         set( z.handle.widget.bottom.movedown, 'enable', 'on' );
0528     else
0529         set( z.handle.widget.bottom.moveup, 'enable', 'off' );
0530         set( z.handle.widget.bottom.movedown, 'enable', 'off' );
0531     end
0532         
0533 
0534     msg( sprintf( 'loaded save file:  %s\n', fullfile( pname, fname) ), 'status' );
0535     guidata( fig, z );
0536 
0537     updateUnitListDisplay( fig );
0538 
0539     positionGUIElements( z );
0540 
0541 
0542 
0543 
0544 
0545 function [] = jg( i )
0546 
0547     fprintf( 'jg here %d\n', i );
0548 
0549 
0550 
0551 
0552 % ==========================================
0553 function [] = saveCB( a1, a2, fig )
0554 
0555     z = guidata( fig );
0556 
0557     pos = get( fig, 'position' );
0558     x = pos(1) + floor(z.config.dim.w/3);
0559     y = z.config.dim.sh - pos(2) - (z.config.dim.h - z.config.dim.th + z.config.dim.bh); % + z.config.dim.bh + 10
0560 
0561 
0562     [fname, pname] = uiputfile( '.job', 'save current state', 'location', [x y] );
0563     fullname = fullfile( pname, fname );
0564 
0565     % this happens when user presses cancel button
0566     if( length( fname ) < 2 | length( pname ) < 2 )
0567         msg( 'save cancelled', 'status' );
0568         return;
0569     end
0570 
0571     % otherwise, check for invalid input
0572     if( length( fname ) < 5 | length( strfind( fname, '.job' )) ~= 1 )
0573         msg( sprintf( 'couldn''t save file: %s', fullname ), 'status' ); 
0574         return;
0575     else
0576 
0577         % ### why not just save z.data.an directly since that's all we're keeping?
0578         % originally there was other stuff in z we needed, and maybe in the future there
0579         % will be some other settings we want to save like window size/position.
0580         anInfo = z.data.an;
0581         
0582         % check the includeResults checkbox - if not checked, make sure results are all []
0583         if( get( z.handle.widget.bottom.includeResults, 'value' ) == 0 )
0584             for x = 1 : length( anInfo )
0585                 anInfo{x}.results = {}; % ### empty cell array or empty struct?
0586             end
0587             % fprintf( 'deleted all existing results from each analysis unit before saving\n' );
0588         else
0589             % fprintf( 'including results with save structures\n' );
0590         end
0591     
0592 
0593         clear z;
0594         z.data.an = anInfo;
0595         save( fullfile( pname, fname ), 'z' );
0596         msg( sprintf( 'saved: %s', fullfile( pname, fname ) ), 'status' ); 
0597     end
0598 
0599 
0600 
0601 % ====================================
0602 % this init() is mostly to read params from the config file and load experiment
0603 % meta data from the ensemble mysql database specified in the config file.
0604 % creation of ui objects, panels, and widgets etc. is not done here.
0605 function [z] = init( configFileName, arg_conn_id )
0606 
0607     if( exist( configFileName ) ~= 2 )    
0608         msg( sprintf( '\n\n** Config file "%s" doesn''t exist. using defaults **\n\n', configFileName ) );
0609     end
0610 
0611     z.config = ens_initConfig( configFileName );
0612 
0613     % if any of these are true, then initConfig had trouble
0614     if( ~length(z.config) | ~isfield( z.config, 'db_server' ) | ~isfield( z.config, 'db_name' ) | ~isfield( z.config, 'conn_id' ) )
0615         msg( 'initConfig had problems. err: WD23', 'errbox' );
0616         z = [];
0617         return;
0618     end
0619         
0620     
0621     % if conn_id was passed in at the command line, it trumps the conn_id resulting from initConfig()
0622     if( arg_conn_id > -1 )
0623         z.config.conn_id = arg_conn_id;
0624     end
0625 
0626     mysql_make_conn( z.config.db_server, z.config.db_name, z.config.conn_id ); 
0627 
0628 
0629     % load master list of experiments in this database
0630     z.data.meta.expList = mysql_extract_data(    'table', 'experiment', ...
0631                                                             'conn_id',  z.config.conn_id, ...
0632                                                             'extract_flds', {'experiment_id','experiment_title'} );
0633     if( length( z.data.meta.expList{1} ) )
0634         z.data.meta.expListStr = cell2str(z.data.meta.expList{2}(1));
0635         for x = 2 : length( z.data.meta.expList{2} )
0636             z.data.meta.expListStr = [z.data.meta.expListStr '|' cell2str(z.data.meta.expList{2}(x))];
0637         end
0638     else
0639         z.data.meta.expListStr = 'no experiments available';
0640     end
0641 
0642 
0643     z.data.meta.anList = mysql_extract_data(    'table', 'analysis', ...
0644                                                             'conn_id',  z.config.conn_id, ...
0645                                                             'extract_flds', {'analysis_id','function_name','comment'} );
0646     
0647     if( isfield( z.config, 'analysisFunction' ) == 1 )
0648         l = length( z.data.meta.anList{2} );
0649         for i = 1 : length( z.config.analysisFunction )
0650             
0651             z.data.meta.anList{1}(l+i) = l+i; % i don't think this field really matters
0652             z.data.meta.anList{2}(l+i) = z.config.analysisFunction(i);
0653         
0654             % comments should always be filled in with "n/a" or whatever even if
0655             % the user doesn't specify a comment in the ini file. ens_initConfig
0656             % should make sure of that. but check here anyway
0657             if( isfield(  z.config, 'analysisComment' ) == 1 && length( z.config.analysisComment ) >= i )
0658                 z.data.meta.anList{3}(l+i) = z.config.analysisComment(i);
0659             else
0660                 z.data.meta.anList{3}(l+i) = 'no comment';
0661             end
0662         end
0663     end
0664 
0665 
0666 
0667 
0668 % =======================================
0669 function [] = anApplyCB( inarg1, inarg2, fig )
0670     z = guidata(fig);
0671 
0672     % first make sure we have an anlysis selected on the right,
0673     % and a created unit selected in the middle.
0674     
0675     rightValue = get( z.handle.widget.right.analysisListbox, 'value' );
0676     midValue = get( z.handle.widget.mid.jobSpecListbox, 'value' );
0677 
0678     if( midValue < 1 | midValue > size(z.data.an) )
0679         msg( 'invalid analysis unit selection (middle pane)', 'status' );
0680         return;
0681     end
0682 
0683     if( rightValue < 1 | rightValue > length( z.data.meta.anList{2} ) )
0684         msg( 'invalid analysis unit selection (right pane)', 'status' );
0685         return;
0686     end
0687 
0688     try
0689         z.data.an{midValue}.fun = str2func( cell2str( z.data.meta.anList{2}(rightValue) ) );
0690     catch
0691         msg( sprintf( 'error QA32: trying to assign function handle: %s',z.data.meta.anList{2}(rightValue)), 'errbox' );
0692         return;
0693     end
0694 
0695     % in case the returned params struct doesn't come with a default filter,
0696     % save the current filter to reattach to the new params struct.
0697     saveFilt = z.data.an{midValue}.params.filt;
0698 
0699     % analysis functions are required to support  fun( 'getDefaultParams' )
0700     evalStr = sprintf( 'z.data.an{midValue}.params = %s( ''getDefaultParams'' );', func2str( z.data.an{midValue}.fun ) );
0701     try
0702         eval( evalStr );
0703     catch
0704         msg( sprintf( 'error when calling %s( ''getDefaultParams'' )\nsetting params to []', ...
0705                                                                                                 func2str( z.data.an{midValue}.fun ) ), 'msgbox' );
0706         % JG should maybe separate this into its own getDefaultParams function,
0707         % for cases where the analysis function doesn't supply its own getDefaultParams?
0708 
0709     end
0710 
0711     if( isfield( z.data.an{midValue}.params, 'filt' ) )
0712         % do nothing- keep the default filter returned by the function
0713     else
0714         % populate the filt field with whatever was in there before- might be [] or whatever.
0715         z.data.an{midValue}.params.filt = saveFilt;
0716     end
0717 
0718     % if all analysis units in the list have callback functions specified, enable the execute button
0719     % assume they all do
0720     set( z.handle.widget.bottom.go, 'enable', 'on' );
0721     for x = 1 : length( z.data.an )
0722         if( ~length( z.data.an{x}.fun ) )
0723             % if at least one unit doesn't have a CB function, disable execute button
0724             set( z.handle.widget.bottom.go, 'enable', 'off' );
0725         end
0726     end        
0727 
0728 
0729     guidata( fig, z );
0730     
0731     % this will force the middle pane to reload the uitree for the selected anUnit
0732     regenerateTreeForSelectedJob( '', '', fig )
0733 
0734     msg( sprintf( 'function for selected analysis unit set to right panel list item %d', rightValue ), 'status' );
0735 
0736 
0737 
0738 
0739 % =======================================
0740 function [] = anInfoCB( inarg1, inarg2, fig )
0741     z = guidata(fig);
0742 
0743     v = get( z.handle.widget.right.analysisListbox, 'value' );
0744     msg( cell2str( z.data.meta.anList{3}(v) ), 'msgbox' );
0745  
0746 
0747 
0748 
0749 
0750 function [] = quitCB( inarg1, inarg2, fig )
0751 
0752 delete( fig );
0753 
0754 
0755 
0756 
0757 
0758 
0759 % ===========================================
0760 function [] = moveCB( inarg1, inarg2, fig )
0761     z = guidata( fig );
0762 
0763 
0764     if( length( z.data.an ) < 2 )
0765         msg( 'nothing to move', 'status' );
0766         return;
0767     end
0768 
0769     tag = get( gcbo, 'tag' );
0770     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
0771     lim = length( z.data.an );
0772 
0773     if( strcmp( tag, 'movedown' ) )
0774         if( whichAnUnit < lim & whichAnUnit > 0 )
0775             temp = z.data.an{whichAnUnit+1};
0776             z.data.an{whichAnUnit+1} = z.data.an{whichAnUnit};
0777             z.data.an{whichAnUnit} = temp;
0778             set( z.handle.widget.mid.jobSpecListbox, 'value', whichAnUnit+1 );
0779         end
0780     end
0781 
0782     if( strcmp( tag, 'moveup' ) )
0783         if( whichAnUnit <= lim & whichAnUnit > 1 )
0784             temp = z.data.an{whichAnUnit-1};
0785             z.data.an{whichAnUnit-1} = z.data.an{whichAnUnit};
0786             z.data.an{whichAnUnit} = temp;
0787             set( z.handle.widget.mid.jobSpecListbox, 'value', whichAnUnit-1 );
0788         end
0789     end
0790 
0791     guidata( fig, z ) 
0792 
0793     % this is not really necessary- we could just call updateUnitListDisplay to see the new list in the
0794     % new order. but if there's a ui tree open for the current unit, and the requires field
0795     % is selected, there will be a popup list already created which would have the old
0796     % list ordering. if you were to then try to modify the requires field by choosing
0797     % a different unit from the stale popup list, the results are unpredictable.
0798     regenerateTreeForSelectedJob( '', '', fig );
0799 
0800 
0801 
0802 
0803 % ===========================================
0804 function [] = goCB( inarg1, inarg2, fig )
0805     z = guidata( fig );
0806 
0807     % make sure each unit has a function that is visible to matlab.
0808     for x = 1 : length( z.data.an )
0809         if( exist( func2str( z.data.an{x}.fun ) ) ~= 2 )
0810             msg( sprintf( 'Function %s for analysis unit %d is not visible to matlab', ...
0811                                                                 func2str( z.data.an{x}.fun ), x ), 'msgbox' );    
0812             return;    
0813         end
0814     end
0815 
0816     params.ensemble.conn_id = z.config.conn_id;
0817     params.ensemble.db_name = z.config.db_name;
0818     params.ensemble.db_server = z.config.db_server;
0819     params.run_analyses = [1:length(z.data.an)];
0820 
0821     msg( sprintf( 'executing analyses%s', sprintf( ' %d', params.run_analyses ) ), 'status' );
0822     z.data.an = ensemble_jobman( z.data.an, params );
0823 
0824     % save results
0825     guidata( fig, z );
0826 
0827     regenerateTreeForSelectedJob( '', '', fig );
0828 
0829 
0830 
0831 
0832 % ===========================================
0833 function [] = dumpCB( inarg1, inarg2, fig )
0834     z = guidata( fig );
0835 
0836 
0837     msg( 'status dumped to matlab output', 'status' );
0838     fprintf( '\n\n---- status ------------------------------------\n' );
0839     fprintf( 'z\n' );
0840 
0841     % it probably isn't necessary to check for these basic stucts
0842     % every time, but there are cases where buttons and callbacks
0843     % are executed before some of them are filled in, causing an error.
0844     % we could keep track of which ones are always explicitly set
0845     % in the startup routine, but it's hard to keep it all straight.
0846     d = z.data;
0847     fprintf( '\tdata\n' );
0848 
0849     if( length( z.data.an ) < 1 )
0850         fprintf( '\t\tan = []\n' );
0851     else
0852         for x = 1 : length( z.data.an )
0853             fprintf( '\t\tan{%d}\n', x );
0854             fprintf( '\t\t\tname = %s\n', z.data.an{x}.name );
0855             %fprintf( '\t\t\trequires = %s\n', z.data.an{x}.requires{1}.name );
0856         end
0857     end
0858 
0859     fprintf( '\t\tleft\n' ); 
0860     l = z.data.left;
0861     if( isfield( l, 'selected' ) )
0862         fprintf( '\t\t\tselected: %d items\n', size( z.data.left.selected,1) );
0863     else
0864         fprintf( '\t\t\tselected = null\n' );
0865     end
0866 
0867 
0868     % these are all guaranteed to exist because we create these widgets at startup
0869     fprintf( '\thandle\n' );
0870     fprintf( '\t\twidget\n' );
0871     fprintf( '\t\t\tmid\n' );
0872     fprintf( '\t\t\t\tjobSpecListbox\n' );
0873     fprintf( '\t\t\t\t\tvalue = %d\n', get( z.handle.widget.mid.jobSpecListbox, 'value' ) );
0874     fprintf( '\t\t\tright\n' );
0875     fprintf( '\t\t\t\tanalysisListbox\n' );
0876     fprintf( '\t\t\t\t\tvalue = %d\n', get( z.handle.widget.right.analysisListbox, 'value' ) );
0877     
0878 
0879     fprintf( '\n\n' );
0880 
0881 
0882 
0883 
0884 
0885 
0886 
0887 % =========================================
0888 function [] = analysisSelectCB( arg1, arg2, fig )
0889 
0890     % nothing to do here
0891 
0892 
0893     
0894 
0895 
0896 % ==============================
0897 function [] = regenerateTreeForSelectedJob( arg1, arg2, fig )
0898 
0899     z = guidata( fig );
0900 
0901     if( length( z.data.an ) < 1 )
0902         return;
0903     end
0904 
0905     whichSelected = get( z.handle.widget.mid.jobSpecListbox, 'value' );
0906 
0907     if( whichSelected < 1 | whichSelected > length( z.data.an ) )
0908         msg( sprintf( 'selected analysis unit %d is out of bounds', whichSelected ), 'errbox' );
0909         return;
0910     end
0911 
0912     % if a previous middle tree exists, delete it and its container --------
0913     h = z.handle;
0914     if( isfield( h, 'container' ) )
0915         c = h.container;
0916         if( isfield( c, 'midTree' ) )
0917             delete( z.handle.container.midTree );
0918             z.handle.container = rmfield( z.handle.container, 'midTree' );
0919         end
0920     end
0921     % ----------------------------------------------------------------------
0922 
0923     z.handle.widget.mid.anTree = getMiddleTree( z.data.an{whichSelected}, z.handle.window.main );
0924     z.handle.container.midTree = get(z.handle.widget.mid.anTree, 'uicontainer');
0925     set(z.handle.container.midTree, 'parent', z.handle.panel.midCenter );
0926 
0927 
0928     % after diabling any previous controls, enable the textfield for editing name directly even though
0929     % the uitree hasn't been clicked on. this is sort of a luxury feature
0930     disableAllfieldEditWidgets( fig );
0931     z.data.center.vName = 'name';
0932     z.data.center.vValue = z.data.an{whichSelected}.name;
0933     set( z.handle.widget.mid.fieldEditString, 'visible', 'on' ); 
0934     set( z.handle.widget.mid.fieldEditString, 'enable', 'on' ); 
0935     set( z.handle.widget.mid.fieldEditString, 'string', z.data.center.vValue );
0936     set( z.handle.text.fieldName, 'string', z.data.center.vName );
0937     
0938 
0939     guidata( fig, z ) 
0940     updateUnitListDisplay( fig );
0941     positionGUIElements( z );
0942 
0943 
0944 
0945 
0946 
0947 % ======================================
0948 function [] = deleteAnalysisCB( arg1, arg2, fig )
0949 
0950     z = guidata( fig );
0951 
0952     anVal = get( z.handle.widget.mid.jobSpecListbox, 'value' );
0953     if( anVal < 1 | anVal > length( z.data.an ) )
0954         return;
0955     end
0956 
0957     % first delete the display tree for this analysis unit
0958     h = z.handle;
0959     if( isfield( h, 'container' ) )
0960         c = h.container;
0961         if( isfield( c, 'midTree' ) )
0962             delete( z.handle.container.midTree );
0963             z.handle.container = rmfield( z.handle.container, 'midTree' );
0964             
0965         end
0966     end
0967 
0968     % we need this below
0969     oldName = z.data.an{anVal}.name;
0970 
0971     % now remove the analysis struct from the cell array.
0972     z.data.an(anVal) = [];
0973 
0974     % disallow reordering of position within unit list if there's not at least 2 of them
0975     if( length( z.data.an ) < 2 )
0976         set( z.handle.widget.bottom.moveup, 'enable', 'off' );
0977         set( z.handle.widget.bottom.movedown, 'enable', 'off' );
0978     end
0979 
0980     % set the value to 1 by default or 0 if there are no units left.
0981     % disable the delete button if there's nothing left to delete.
0982     if( length( z.data.an ) < 1 )
0983         set( z.handle.widget.top.deletean, 'enable', 'off' );
0984         set( z.handle.widget.bottom.save, 'enable', 'off' );
0985         set( z.handle.widget.bottom.includeResults, 'enable', 'off' );
0986         set( z.handle.widget.mid.jobSpecListbox, 'value', 0 );
0987         set( z.handle.widget.bottom.go, 'enable', 'off' );
0988         set( z.handle.widget.bottom.addexclude, 'enable', 'off' );
0989         set( z.handle.widget.bottom.addinclude, 'enable', 'off' );
0990         set( z.handle.widget.bottom.anAdd, 'enable', 'off' );
0991     else
0992         % default selection to item 1 after each deletion
0993         set( z.handle.widget.mid.jobSpecListbox, 'value', 1 );
0994 
0995         % if one of the other units required the deleted unit,
0996         % throw up a message and erase the stale dependency
0997         for x = 1 : length( z.data.an )
0998             for y = 1 : length( z.data.an{x}.requires )
0999                 if( strcmp( z.data.an{x}.requires{y}.name, oldName ) )
1000                     z.data.an{x}.requires(y) = [];
1001                     msg( sprintf( 'analysis unit ''%s'' required the unit you just deleted.\nthis requirement has been removed', z.data.an{x}.name ), 'msgbox' );
1002                 end
1003             end
1004         end
1005     
1006         % first, assume all analysis units have callback functions...
1007         set( z.handle.widget.bottom.go, 'enable', 'on' );
1008         for x = 1 : length( z.data.an )
1009             if( ~length( z.data.an{x}.fun ) )
1010                 % but if at least one unit doesn't have a CB function, disable execute button
1011                 set( z.handle.widget.bottom.go, 'enable', 'off' );
1012             end    
1013         end
1014 
1015 
1016     end
1017 
1018     guidata( fig, z ) 
1019 
1020     updateUnitListDisplay( fig );
1021 
1022     % needed to resize analysis listbox height
1023     positionGUIElements( z );
1024 
1025 
1026 
1027 
1028 
1029 
1030 % ===================================
1031 function [] = newAnalysisCB( inarg1, inarg2, fig )
1032 
1033     z = guidata( fig );
1034 
1035     anNameCell = inputdlg( 'Enter name of new analysis unit:', '', 1 );
1036     if( length(anNameCell) ~= 1 )
1037         msg( 'new unit cancelled', 'status' );
1038         return; % user pressed cancel button
1039     end
1040     anName = cell2str( anNameCell );
1041     if( length( anName ) < 1 )
1042         msg( 'new unit cancelled', 'status' );
1043         return; % user pressed "ok" but didn't enter any text
1044     end
1045     msg( sprintf( 'analysis unit ''%s'' added', anName ), 'status' );
1046 
1047     % we have something to save now
1048     set( z.handle.widget.bottom.save, 'enable', 'on' );
1049     set( z.handle.widget.bottom.includeResults, 'enable', 'on' );
1050 
1051     % the new unit will be at the end of the list
1052     num = length( z.data.an ) + 1;
1053 
1054     if( analysisNameExists( anName, fig, [] ) == 1 ) 
1055         msg( 'the name you entered is already in use', 'msgbox' );
1056         return;
1057     else
1058         z.data.an{num} = newAnalysisUnit( anName );
1059     end
1060 
1061     % don't shift the focus of the currently selecetd unit,
1062     % unless the new unit we just created is now the only one.
1063     if( num == 1 ) 
1064         set( z.handle.widget.mid.jobSpecListbox, 'value', num );
1065     end
1066 
1067     % now we have something to delete
1068     set( z.handle.widget.top.deletean, 'enable', 'on' );
1069     
1070     % now we can apply the selected analysis funciton from right pane to the current an unit
1071     set( z.handle.widget.bottom.anAdd, 'enable', 'on' );
1072 
1073     % allow reordering of an unit list if there's at least 2
1074     if( length( z.data.an ) > 1 )
1075         set( z.handle.widget.bottom.moveup, 'enable', 'on' );
1076         set( z.handle.widget.bottom.movedown, 'enable', 'on' );
1077     end
1078 
1079     % since we have at least one analysis unit, we have one selected.
1080     % now, if there's also some items selected in the left window, enable
1081     % the filt include/exclude buttons
1082     if( length( z.data.left.selected ) )
1083         set( z.handle.widget.bottom.addexclude, 'enable', 'on' );
1084         set( z.handle.widget.bottom.addinclude, 'enable', 'on' );
1085     else
1086         set( z.handle.widget.bottom.addexclude, 'enable', 'off' );
1087         set( z.handle.widget.bottom.addinclude, 'enable', 'off' );
1088     end
1089 
1090     % disable executwe button because we know that at least one analysis unit in
1091     % the list (the one we just added) doesn't have a callback function specified yet.
1092     set( z.handle.widget.bottom.go, 'enable', 'off' );
1093 
1094 
1095     % upload changes to guidata
1096     guidata( fig, z ) 
1097 
1098     % needed to regenerate the analysis list options
1099     updateUnitListDisplay( fig );
1100 
1101     % need to resize analysis listbox height
1102     positionGUIElements( z );
1103 
1104     % bizarre matlab GUI bug- for some reason calling this again here fixes
1105     % a transient formatting problem which appears after adding the first unit
1106     resizeMainFigureCB( '', '', fig )
1107 
1108 
1109 
1110 
1111 % ============================================
1112 % generates human readable string describing the input filt.
1113 % returns a cell array of strings describing the input struct,
1114 % which is intended to be a filt for this purpose, but this function will work with
1115 % any struct. in fact ### we would want to pull this out into a static utility function
1116 % if anybody else need it.
1117 % each string is one line of the filt such as:
1118 % filt.exclude.any.subject_id = {'tmp_*','01zin79271','08tgs78071'}
1119 % filt.exclude.any.session_id = [1873 1984  1523:1576]
1120 %
1121 function [s] = getFilterList( f )
1122 
1123     s = {};
1124     if( length( f ) < 1 )
1125         return;
1126     end
1127 
1128     fn = fieldnames( f );
1129     howManyTotal = 0;
1130 
1131     for x = 1 : length( fn )
1132         fieldsIJustGot = {};
1133 
1134         nextField = getfield( f, fn{x} );
1135         if( ~isstruct(nextField) )
1136             prefChar = ' = ';
1137             if( iscell( nextField ) )
1138                 temp = '{';
1139                 for z = 1 : length( nextField )
1140                     temp = sprintf( '%s''%s''', temp, nextField{x} );
1141                     % add a comma following each element except the last in the cell array
1142                     if( z < length(nextField) )
1143                         temp = sprintf( '%s,', temp );
1144                     end
1145                 end
1146                 temp = sprintf( '%s}', temp );
1147                 fieldsIJustGot{1} = temp;
1148             else
1149                 if( isnumeric( nextField ) )
1150                     temp = '[';
1151                     for z = 1 : length( nextField )
1152                         temp = sprintf( '%s%d', temp, nextField(z) );
1153                         % add a space following each element except the last in the cell array
1154                         if( z < length(nextField) )
1155                             temp = sprintf( '%s ', temp );
1156                         end
1157                     end
1158                     temp = sprintf( '%s]', temp );
1159                     fieldsIJustGot{1} = temp;
1160                 else
1161                     fieldsIJustGot{1} = '?DF34 data type not accounted for?';
1162                 end
1163             end
1164     
1165         else
1166             prefChar = '.';
1167             % recursive function call
1168             fieldsIJustGot = getFilterList( nextField );
1169         end
1170 
1171         for y = 1 : length( fieldsIJustGot )
1172             howManyTotal = howManyTotal + 1; % initial value is 0
1173             s{howManyTotal} = sprintf( '%s%s%s', fn{x}, prefChar, fieldsIJustGot{y} );
1174         end
1175 
1176     end
1177 
1178 
1179 
1180 
1181 
1182 
1183 % ============================================
1184 % create a blank analysis unit
1185 function [an] = newAnalysisUnit( name )
1186 
1187     % defaults
1188     an.type = '';
1189     an.fun = [];
1190     an.requires = {};
1191     an.name = name;
1192     an.params.filt = [];
1193     an.results = {};
1194 
1195     
1196     if( 0 ) % DEBUG
1197         an.name = name;
1198         an.params.x1 = 123;
1199         an.params.y2 = 'blah blah';
1200         an.params.z3 = 12.2333;
1201         an.params.abc = [1 2.2 3];
1202         an.params.def = [1 2 3
1203                             4 5 6
1204                             7 8 9];
1205         an.params.col_vec = [3 5 23 7 4 2 1]';
1206         e.x1 = 342;
1207         e.x2 = '4534jlkj';
1208         e.x3 = [1 5 6];
1209         f.a1 = 23;
1210         f.a2 = e;
1211         an.params.sss = f;
1212         cell{1} = 'ghgfhf';
1213         cell{2} = 'gkfkh';
1214         cell{3} = '45353s';
1215         an.params.xyz = cell;
1216     end
1217 
1218     if( 0 ) %  DEBUG
1219         saveP = an.params;
1220         an = generateTestUnit;
1221         an.params = saveP;
1222         an.params.filt = [];
1223         an.results = {};
1224         an.type = '';
1225     end
1226 
1227 
1228 
1229 
1230 
1231 
1232 % ===================================
1233 function [] = addToFilterCB( inarg1, inarg2, fig )
1234     z = guidata( fig ); 
1235 
1236     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
1237 
1238     if( strcmp( get( gcbo, 'tag' ), 'addinclude' ) )
1239         isInclude = 1;
1240     else
1241         isInclude = 0;
1242     end
1243 
1244     sel = z.data.left.selected;
1245     filtAddition = [];
1246     for x = 1 : size( sel, 1 )
1247         if( sel(x,1) > 0 )
1248             if( isInclude )
1249                 filtAddition.include.all.experiment_id = [sel(x,1)];
1250             else
1251                 filtAddition.exclude.all.experiment_id = [sel(x,1)];
1252             end
1253         end
1254         if( sel(x,2) > 0 )
1255             if( isInclude )
1256                 filtAddition.include.all.form_id = [sel(x,2)];
1257             else
1258                 filtAddition.exclude.all.form_id = [sel(x,2)];
1259             end
1260         end
1261         if( sel(x,3) > 0 )
1262             if( isInclude )
1263                 filtAddition.include.all.question_id = [sel(x,3)];
1264             else
1265                 filtAddition.exclude.all.question_id = [sel(x,3)];
1266             end
1267         end
1268         z.data.an{whichAnUnit}.params.filt = add2filt( z.data.an{whichAnUnit}.params.filt, filtAddition );
1269     end
1270 
1271     guidata( fig, z );
1272 
1273     % create a new ui tree because the existing one is now displaying stale filt info
1274     regenerateTreeForSelectedJob( '', '', fig );
1275 
1276 
1277 
1278 
1279 
1280 % ====================================
1281 % this string gets assigned to the listbox for the analysis units.
1282 % each list display item is separated by a | character.
1283 function [listString] = getAnalysisUnitListString( fig )
1284     z = guidata( fig );
1285     listString = '';
1286 
1287     if( length( z.data.an ) < 1 )
1288         return; 
1289     end
1290 
1291     listString = ['1) ' z.data.an{1}.name]; 
1292     for x = 2 : length( z.data.an )
1293         listString = sprintf( '%s|%d) %s', listString, x, z.data.an{x}.name );
1294     end
1295 
1296 
1297 
1298 
1299 % ====================================
1300 % regenerates the list based on the available analysis units.
1301 function [] = updateUnitListDisplay(fig)
1302 
1303     z = guidata( fig );
1304 
1305     if( length( z.data.an ) < 1 )
1306         set( z.handle.widget.mid.jobSpecListbox, 'string', '' );
1307     
1308         % only change the current selection if the list is empty- must set to 0.
1309         set( z.handle.widget.mid.jobSpecListbox, 'value', 0 );
1310     else
1311         set( z.handle.widget.mid.jobSpecListbox, 'string', getAnalysisUnitListString( fig ) );
1312     end
1313 
1314 
1315     guidata(fig,z);
1316 
1317 
1318 
1319 
1320 
1321 
1322 
1323 % ======================================
1324 function [] = selectExperimentListCB( a1, a2, fig )
1325 
1326     z = guidata( fig ); 
1327     v = get(gcbo,'value'); % v is the list element number (not the experiment ID)
1328     expNum = z.data.meta.expList{1}(v); % here is the actual experiment ID
1329     
1330     % destroy any previous experiment selection uitree
1331     % isn't there an easier way to check for the existence of a
1332     % previous graphics object with this handle than using
1333     % isfield at every level?
1334     h = z.handle;
1335     if( isfield( h, 'menu' ) )
1336         m = h.menu;
1337         if( isfield( m, 'leftTree' ) )
1338             delete( z.handle.menu.leftTree );
1339             z.handle.menu = rmfield( z.handle.menu, 'leftTree' );
1340             msg( 'deleting previous uitree', 'status' );
1341         end
1342     end
1343 
1344     msg( sprintf( 'loading', expNum, cell2str(z.data.meta.expList{2}(v)) ), 'status' );
1345 
1346     % nothing is selected any more
1347     z.data.left.selected = [];
1348     set( z.handle.widget.bottom.addexclude, 'enable', 'off' );
1349     set( z.handle.widget.bottom.addinclude, 'enable', 'off' );
1350         
1351 
1352     % create ui tree for the selected experiment
1353     z.handle.menu.leftTree = ens_getLeftTree( expNum, z.handle.window.main );
1354 
1355     % you can't set the parent of the tree directly, you have to go through its container.
1356     z.handle.container.leftTree = get(z.handle.menu.leftTree, 'uicontainer');
1357     set(z.handle.container.leftTree, 'parent', z.handle.panel.left );
1358 
1359     msg( sprintf( 'loaded experiment %d: %s', expNum, cell2str(z.data.meta.expList{2}(v)) ), 'status' );
1360 
1361     guidata( fig, z );
1362 
1363 
1364 
1365 
1366 % ========================================================================
1367 % general messaging fucntion supports types:
1368 % status - writes to the status/info bar at bottom of GUI
1369 % msgbox - popup message
1370 % errbox - popup error message
1371 % default - fprintf to matlab console output
1372 function [] = msg( msg, outputType )
1373 
1374     if( ~exist( 'outputType', 'var' ) )
1375         outputType = 'default';
1376     end
1377         
1378 
1379     if( strcmp( outputType, 'errbox' ) )
1380         errordlg( msg, 'BAD' );
1381         return;
1382     end 
1383 
1384     if( strcmp( outputType, 'msgbox' ) )
1385         msgbox( msg, 'I would like you to know this:' );
1386         return;
1387     end
1388     
1389     if( strcmp( outputType, 'status' ) )
1390         fig = getOurGUIHandle;
1391 
1392         % if it didn't work, something is really wrong.
1393         if( ~length(fig) )
1394             return;
1395         end
1396 
1397         % check to see if this is too long for the status bar?
1398         % right now it just stops at the end. maybe coudl at least replace with '...'
1399         z = guidata( fig );
1400         set( z.handle.text.status, 'string', msg );
1401         return;
1402     end
1403 
1404     if( strcmp( outputType, 'homing_pigeon' ) )
1405         % ### need to register homing pigeon service with matlab
1406     end
1407 
1408     % default
1409     fprintf( '%s\n', msg );
1410     
1411     
1412 
1413 
1414 % ======================================
1415 % this is for functions that don't have direct access to our figure.
1416 % this is much safer than using gcf()
1417 function [h] = getOurGUIHandle()
1418 
1419     h = [];
1420     c = get(0,'children');
1421     for x = 1 : length(c)
1422         if( strcmp( get(c(x),'tag'), 'z.handle.window.main' ) )
1423             h = c(x);
1424             return;
1425         end
1426     end
1427 
1428     fprintf( 'ERROR: couldn''t find main GUI figure handle\n\n' );
1429 
1430 
1431 
1432 
1433 
1434 % ==============================================
1435 % return the first node in the uitree for the selected analysis unit
1436 function [tree] = getMiddleTree( an, fig )
1437     
1438     z = guidata( fig );
1439 
1440     % uitreenode( value, description, icon, leaf )
1441     % nodes values are assigned as: <variable name>.<variable value>
1442     % we have to keep track of these name.value pairs separately
1443     % from what is displayed to the user, which might not necessarily
1444     % always want to be the same.
1445     value = sprintf( 'name.%s', an.name );
1446     rootNode = uitreenode( value, an.name, [], false ); 
1447 
1448     % attach the rootNode to the uitree and set the expansion callback.
1449     tree = uitree(    'Root', rootNode, ...
1450                                 'ExpandFcn', {@expandAnalysisUnitTreeCB, fig} );
1451     set( tree, 'NodeSelectedCallback', {@analysisFieldSelectionCB, fig} );
1452     set( tree, 'MultipleSelectionEnabled', 0 );
1453     % position and size are relative to the parent uicontainer or figure.
1454     set(tree, 'Units', 'normalized', 'position', [0 0 1 1] );
1455 
1456 
1457 
1458 
1459 % =======================================================
1460 function analysisFieldSelectionCB( tree, value, fig )
1461     z = guidata( fig );
1462 
1463     nodes = tree.SelectedNodes;
1464 
1465     % ### multiple selection is disabled.
1466     % things would get tricky and confusing fast if multiple analysis unit selection were allowed
1467     if( length( nodes ) ~= 1 )
1468         msg( sprintf( '%d nodes selected in analysis unit uitree (should be 1)', length(nodes)), 'errbox' );
1469         msg( 'aborted', 'status' );
1470         return;
1471     end
1472 
1473     % node value which is created by us comes in the form <name>.<value>
1474     % there are some nodes that have multiple values: <name>.<value1>.<value2>.<value3>...
1475     % but we are only splitting at the first "." right now. anything to the right
1476     % of the first . is considered the entire "value". later when each node is dealt
1477     % with individually based on its name, those special sections will parse out
1478     % the the value to each of its components if necessary. see: params for the main example
1479     tag = get( nodes(1), 'value' );
1480     p = strfind( tag, '.' );
1481     if( p <= 0 )
1482         msg( sprintf( 'bad node value: %s\n', tag ), 'errbox' );
1483         return;
1484     end
1485 
1486     % we could pass these directly as params to updateFieldEditRegion but it's a good idea to store
1487    z.data.center.vName = tag(1:p-1);
1488    z.data.center.vValue = tag( p+1:end ); % might contain multiple sub-values, each separated by a "."
1489 
1490     if( 0 ) % DEBUG
1491         msg( sprintf( 'selected field name -->%s<--  value-->%s<--', z.data.center.vName, z.data.center.vValue ), 'status' );
1492     end
1493 
1494     % save changes
1495     guidata( fig, z );
1496 
1497 
1498     % this will create all the necessary input fields in the center
1499     % bottom panel for editing analysis field values
1500     updateFieldEditRegion( fig );
1501 
1502 
1503 
1504 
1505 % =========================================================
1506 function [] = disableAllfieldEditWidgets( fig )
1507     z = guidata( fig );
1508 
1509     set( z.handle.text.fieldName, 'string', '' );
1510 
1511     if( strcmp( get(  z.handle.widget.mid.fieldEditPopupmenu, 'visible' ), 'on' ) )
1512          set( z.handle.widget.mid.fieldEditPopupmenu, 'visible', 'off' );
1513          set( z.handle.widget.mid.fieldEditPopupmenu, 'enable', 'off' );
1514     end
1515     if( strcmp( get(  z.handle.widget.mid.fieldEditString, 'visible' ), 'on' ) )
1516          set( z.handle.widget.mid.fieldEditString, 'visible', 'off' );
1517          set( z.handle.widget.mid.fieldEditString, 'enable', 'off' );
1518     end
1519 
1520     if( strcmp( get(  z.handle.widget.mid.filtDeleteButton, 'visible' ), 'on' ) )
1521          set( z.handle.widget.mid.filtDeleteButton, 'visible', 'off' );
1522          set( z.handle.widget.mid.filtDeleteButton, 'enable', 'off' );
1523     end
1524 
1525 
1526 
1527 
1528 
1529 
1530 % =========================================================
1531 % looks at the global vNAme and vValue to see which node is currently selected.
1532 % based on each special case, this function determines which editing options
1533 % should be displayed to the user at the center-bottom panel, if any.
1534 % for example if vName is "name", we enable the generic text field
1535 % to allow the user to change the name of the selected analysis unit.
1536 function [] = updateFieldEditRegion( fig )
1537     z = guidata( fig );
1538 
1539     % these store the current node selection
1540     vName = z.data.center.vName;
1541     vValue = z.data.center.vValue;
1542 
1543     % first make any previously used field invisible/disabled
1544     disableAllfieldEditWidgets( fig );
1545 
1546     % if any of these is selected, we want to leave the edit region disabled-
1547     % there's nothing that the user can change about any of these fields
1548     if(     strcmp( vName, 'fun' ) | ...
1549             strcmp( vName, 'results' ) |    strcmp( vName, 'filtItem' ) )
1550         return;
1551     end
1552 
1553     if( strcmp( vName, 'filt' ) )
1554         set( z.handle.widget.mid.filtDeleteButton, 'visible', 'on' ); 
1555         set( z.handle.widget.mid.filtDeleteButton, 'enable', 'on' ); 
1556         set( z.handle.text.fieldName, 'string', 'filt' );
1557         return;
1558     end
1559     
1560 
1561     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
1562     if( whichAnUnit < 1 | whichAnUnit > length( z.data.an ) )
1563         msg( 'error VN57', 'errbox' );    
1564         return;
1565     end    
1566 
1567 
1568     % we need to use the popup list for this
1569     if( strcmp( vName, 'requires' ) )
1570 
1571         listString = getAnalysisUnitListString( fig );
1572         set( z.handle.widget.mid.fieldEditPopupmenu, 'string', listString );
1573         set( z.handle.widget.mid.fieldEditPopupmenu, 'visible', 'on' ); 
1574         set( z.handle.widget.mid.fieldEditPopupmenu, 'enable', 'on' ); 
1575     
1576         % go through this whole thing just to find out which
1577         % unit the selected unit currently requires, if any.
1578         % if so, we want to set the initial value of the popup window
1579         % to the required unit for clarity and smooth operation.
1580         % sometimes the popup window is glitchy as it is.
1581         whichRequired = 1;
1582         if( length( z.data.an{whichAnUnit}.requires ) ) 
1583             for t = 1 : length( z.data.an )
1584                 if( strcmp( z.data.an{whichAnUnit}.requires{1}.name, z.data.an{t}.name ) )
1585                     whichRequired = t;
1586                     break;
1587                 end
1588             end
1589         end
1590 
1591         set( z.handle.widget.mid.fieldEditPopupmenu, 'value', whichRequired );
1592         set( z.handle.text.fieldName, 'string', vName );
1593 
1594         return;
1595     end
1596 
1597     if( strcmp( vName, 'params' ) )
1598 
1599         if( length( vValue ) < 1 )
1600             % jg1
1601             % enable edit box to manually add fields to params struct.
1602             % commands are interpreted as if typed in the command line
1603             % following "an{this}.params." --- then the user might type for example
1604             % "t = 13;", restuing in "an{this}.params.t=13;"
1605             set( z.handle.widget.mid.fieldEditString, 'visible', 'on' ); 
1606             set( z.handle.widget.mid.fieldEditString, 'enable', 'on' ); 
1607             set( z.handle.widget.mid.fieldEditString, 'string', '' );
1608             set( z.handle.text.fieldName, 'string', 'cmd: params.' );
1609             return;    
1610         else
1611             % vValue might be a long list of nested stuct names like "s1.s2.s3.s4.selected_field_name'
1612             evalStr = sprintf( 'thisField = z.data.an{whichAnUnit}.params.%s;', vValue );
1613             eval( evalStr );
1614         end
1615 
1616         % nothing to modify here
1617         if( isstruct( thisField ) )
1618             return;
1619         end
1620 
1621 
1622         % we need to know the final field name for display purposes below, but we
1623         % don't have just the fnial name isolated by itself yet.
1624         % so we use this function as a convenience to parse the names for us, and
1625         % the final field name will be the last cell returned by the parse function
1626         names = parseNodeName( vValue );
1627         fieldName = names{ length( names ) }; 
1628 
1629 
1630         % most cases will use this
1631         set( z.handle.widget.mid.fieldEditString, 'visible', 'on' ); 
1632         set( z.handle.widget.mid.fieldEditString, 'enable', 'on' ); 
1633 
1634 
1635         [dataType, numDim] = getDataType( thisField );
1636         switch( dataType )
1637 
1638         % STRING
1639         case 's'
1640             set( z.handle.widget.mid.fieldEditString, 'string', thisField );
1641 
1642 
1643         % INT/FLOAT
1644         case {'d', 'f'}
1645             switch numDim    
1646             case 0 
1647                 set( z.handle.widget.mid.fieldEditString, 'string', num2str( thisField ) );
1648             case 1 
1649                 if( size(thisField,1) > 1 )
1650                     set( z.handle.widget.mid.fieldEditString, 'string', ['[' num2str(thisField') ']'] );
1651                     msg( 'NOTE: column vector displayed as row vector. if you save changes, it will become a row vector', 'status' );
1652                 else
1653                     set( z.handle.widget.mid.fieldEditString, 'string', ['[' num2str(thisField) ']'] );
1654                 end
1655             otherwise    
1656                 set( z.handle.widget.mid.fieldEditString, 'string', sprintf( '<%dd matrix>', numDim ) );
1657             end
1658 
1659 
1660         % CELL ARRAY
1661         case {'c'}
1662             disableAllfieldEditWidgets( fig );    % this is kind of inefficient since we just enabled fieldEditString above
1663                                                             % but there's no way around some repeated or redundant code with all these
1664             return;
1665 
1666 
1667         otherwise
1668             msg( 'error: ASL45', 'errbox' );
1669             return;
1670         end
1671 
1672         % if we made it here, then we're displaying something for the user to edit.
1673         % so change the text on the lower-left-center to the field name instead of "<no edit>"
1674         names = parseNodeName( vValue );
1675         set( z.handle.text.fieldName, 'string', names{ length(names) } );
1676         return;
1677     end
1678 
1679 
1680     % anything that makes it down here uses the simple text field
1681     set( z.handle.widget.mid.fieldEditString, 'visible', 'on' ); 
1682     set( z.handle.widget.mid.fieldEditString, 'enable', 'on' ); 
1683     set( z.handle.widget.mid.fieldEditString, 'string', vValue );
1684     set( z.handle.text.fieldName, 'string', vName );
1685 
1686 
1687 
1688 
1689 
1690 
1691 % ======================================================
1692 % deletes the entire filter at once.
1693 % ### line-item deletion is not suported in v1.0
1694 function [] = filtDeleteCB( var1, var2, fig )
1695     z = guidata( fig );
1696 
1697     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
1698     newValue = get( gcbo(), 'value' );
1699     if( whichAnUnit < 1 | whichAnUnit > length( z.data.an ) )
1700         msg( 'eror FM101.3', 'errbox' );    
1701         return;
1702     end    
1703 
1704     z.data.an{whichAnUnit}.params.filt = [];
1705     msg( sprintf( 'filt for analysis unit %d has been deleted. see for yourself', whichAnUnit ), 'status' );
1706 
1707     guidata( fig, z );
1708     regenerateTreeForSelectedJob( '', '', fig );
1709 
1710 
1711 
1712 
1713 % ======================================================
1714 function [] = fieldEditPopupCB( var1, var2, fig )
1715     z = guidata( fig );
1716 
1717     % right now this only applies to selecting a new "requires" value-
1718     % need to deal with multiple possibilities for vName here
1719     % if other fields have popup menus.
1720 
1721     % the current selection will always apply to one of the
1722     % analysis units in the list. it should never be possible to
1723     % get an out of bounds value here
1724     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
1725     newValue = get( gcbo(), 'value' );
1726     if( whichAnUnit < 1 | whichAnUnit > length( z.data.an ) | newValue < 1 | newValue > length( z.data.an ) )
1727         msg( 'eror PY45', 'errbox' );    
1728         return;
1729     end    
1730 
1731     msg( sprintf( 'unit %s now requires unit %s', z.data.an{whichAnUnit}.name, z.data.an{newValue}.name ), 'status' ); 
1732 
1733     if( whichAnUnit == newValue )
1734         msg( 'warning: you have specified this unit''s output as its own input', 'msgbox' );
1735     end
1736 
1737     requiresStruct.name = z.data.an{newValue}.name;
1738     z.data.an{whichAnUnit}.requires{1} = requiresStruct;
1739 
1740     guidata( fig, z );
1741     regenerateTreeForSelectedJob( '', '', fig );
1742 
1743 
1744 
1745 
1746 % ======================================================
1747 function [res] = analysisNameExists( findThis, fig, exceptThese ) 
1748     z = guidata( fig );
1749     res = 0;
1750 
1751     if( length( z.data.an ) < 1 )
1752         return;
1753     end
1754 
1755     for x = 1 : length( z.data.an )
1756         if( length( find( exceptThese == x ) ) > 0 )
1757             continue; 
1758         end
1759     
1760         if( strcmp( z.data.an{x}.name, findThis ) )
1761             res = 1;
1762             return;
1763         end
1764     end
1765 
1766 
1767 
1768 
1769 
1770 
1771 
1772 % ======================================================
1773 function [] = fieldEditTextCB( var1, var2, fig )
1774     z = guidata( fig );
1775 
1776     
1777     vName = z.data.center.vName;
1778     vValue = z.data.center.vValue;
1779 
1780     newValue = get( gcbo(), 'string' );
1781 
1782 
1783     % the field chagne will always apply to one of the
1784     % analysis units in the list, so make sure we're in bounds
1785     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
1786     if( whichAnUnit < 1 | whichAnUnit > length( z.data.an ) )
1787         msg( 'error TR32', 'errbox' );    
1788         return;
1789     end    
1790 
1791 
1792     if( strcmp( vName, 'name' ) )
1793         if( length( newValue ) < 1 )
1794             msg( 'can''t change name to blank', 'msgbox' );
1795             return;
1796         end
1797         % make sure name is not in use
1798         if( analysisNameExists( newValue, fig, whichAnUnit ) )
1799             msg( 'the name you entered is already in use', 'msgbox' );
1800             return;
1801         end
1802         z.data.an{whichAnUnit}.name = newValue;
1803 
1804         % if any existing unit required the unit whose name was just changed,
1805         % change their requires field to match the new name to match the new name
1806         for x = 1 : length( z.data.an )
1807             if( ~length( z.data.an{x}.requires ) )
1808                 continue;
1809             end
1810             if( strcmp( z.data.an{x}.requires{1}.name, vValue ) )
1811                 msg( sprintf( 'unit %d (%s) requires the unit whose name you just changed.\nupdating %s''s requires field with the new name: %s', x, z.data.an{x}.name, z.data.an{x}.name, newValue ), 'msgbox' );
1812                 z.data.an{x}.requires{1}.name = newValue;
1813             end
1814         end
1815 
1816     end
1817 
1818 
1819     if( strcmp( vName, 'type' ) )
1820         if( length( newValue ) < 1 )
1821             msg( 'can''t change type to blank', 'msgbox' );
1822             return;
1823         end
1824         z.data.an{whichAnUnit}.type = newValue;
1825     end
1826     
1827 
1828     if( strcmp( vName, 'params' ) )
1829 
1830         % jg2
1831         if( length( vValue ) <= 1 )
1832             % if here, then they are at the params level, and whatever they submitted
1833             % should be executed literally as an addition to params.
1834             %jg1 = 5
1835             if( length(newValue) <= 1 )
1836                 msg( 'nothing entered', 'status' )
1837             else
1838                 cmd = sprintf( 'z.data.an{%d}.params.%s;', whichAnUnit, newValue );
1839                 try
1840                     eval( cmd )
1841                     msg( sprintf( 'executed: %s', cmd ), 'status' );
1842                 catch 
1843                     msg( sprintf( 'error executing: "%s"', cmd ), 'status' );
1844                 end
1845             end
1846                     
1847         else
1848             names = parseNodeName( vValue );
1849 
1850             finalFieldName = names{ length(names) };
1851             baseStructPath = 'params';
1852             for x = 1 : length( names )-1
1853                 baseStructPath = sprintf( '%s.%s', baseStructPath, names{x} );
1854             end
1855             msg( sprintf( 'set param field %s.%s to: %s', baseStructPath, finalFieldName, newValue ), 'status' );
1856     
1857     
1858             % the newValue for this param comes to us as a string through the editable
1859             % text field, so we have to cast it to its true type.
1860             % the only way to find out its true type is to look at the previous
1861             % value of that param field
1862             evalStr = sprintf( 'oldFieldValue = getfield( z.data.an{whichAnUnit}.%s, ''%s'' );', baseStructPath, finalFieldName );
1863             eval( evalStr );
1864             [dataType, numDim] = getDataType( oldFieldValue );
1865             switch( dataType )
1866     
1867             case 's'    
1868                 % no conversion necessary for newValue - leave as string
1869     
1870             case {'d','f'}
1871                 % evalin is just like eval but lets you use the context of the workspace,
1872                 % otherwise workspace variables aren't visible from here using eval()
1873                 try
1874                     newValue = evalin( 'base', newValue );
1875                 catch
1876                     msg( 'the string you entered didn''t evaluate to a valid number/vector/matrix', 'msgbox' );
1877                     return;
1878                 end
1879     
1880             otherwise
1881                 msg( 'unknown data type err: MF321', 'errbox' );
1882             end
1883     
1884             % now assign newValue to the right place
1885             evalStr = sprintf( 'z.data.an{whichAnUnit}.%s.%s = newValue;', baseStructPath, finalFieldName );
1886             eval( evalStr );
1887                 
1888         end % else - length(vValue) is <= 1
1889     end
1890 
1891     % save changes
1892     guidata( fig, z );
1893 
1894     % gnerates a new guitree
1895     regenerateTreeForSelectedJob( '', '', fig );
1896 
1897 
1898 
1899 
1900 % separates the input string "<v1>.<v2>.<v3>.<v4>..."
1901 % into a cell array: { 'v1', 'v2', 'v3', 'v4', ... }
1902 function [v] = parseNodeName( s )
1903 
1904     p = strfind( s, '.' );
1905     if( length( p ) <= 0 )
1906         v{1} = s;
1907         return;
1908     end
1909 
1910     v{1} = s(1:p(1)-1);
1911     rem = s(p(1)+1:end);
1912 
1913     count = 2;
1914     while( 1 )
1915         p = strfind( rem, '.' );
1916 
1917         if( length(p) <= 0 )
1918             if( length( rem ) > 0 )
1919                 v{count} = rem;
1920             end
1921             return; 
1922         else
1923             v{count} = rem( 1:p(1)-1 );
1924             rem = rem( p(1)+1:end );
1925             count = count + 1;
1926         end
1927     end
1928         
1929 
1930 
1931 
1932 % ======================================================
1933 % README:
1934 % node names are kind of complicated because there are so many special
1935 % cases- there's no single system or solution i could find which didn't
1936 % have exceptions so i picked the system that made the most sense to me.
1937 % the node "value" is the part the the code uses to identify and modify
1938 % values. generally the value is of the form <node name>.<node value>
1939 % so for example the value of the node for "name" (analysis unit name)
1940 % might be  value = "name.my_unit_1"
1941 % however, things get more tricky when it comes to params, which
1942 % supports arbitrary nested structs. we use the node value here
1943 % to store the path through the nested structs to the current node.
1944 % they all begin with "params", and one possible example might be:
1945 % value = "params.abc.def.object34.object676"
1946 % note that in this case the final element in that concatenated list of fields
1947 % does not store the value of the field, as was the case with the
1948 % top-level node values (like name, function, requires, etc.)
1949 % the top level root node for the params struct has value = "params."
1950 %
1951 % each of the special node types needs to be dealt with separately
1952 % in 3 places: 1 is here, where the nodes are created when the user expands a
1953 % section, and 2 when the user selects a node, we update the global vName and vValue,
1954 % and 3 when the user saves changes to a field, we look at the stored vName and vValue
1955 % and deal with each different case accordingly.
1956 function [nodes] = expandAnalysisUnitTreeCB( tree, value, fig )
1957 
1958     p = strfind( value, '.' );
1959     if( length( p ) < 1 )
1960         msg( sprintf( 'expandAnalysisUnitTreeCB: ERROR: bad node value: %s\n', value ), 'errbox' );
1961         nodes = [];
1962         return;
1963     end
1964 
1965     vName = value(1:p(1)-1);
1966     vValue = value(p(1)+1:end);
1967 
1968     if( 0 ) % DEBUG
1969         msg( sprintf( 'expanding analysis unit   %s.%s', vName, vValue ), 'status' );
1970     end
1971 
1972     z = guidata( fig );
1973     an = z.data.an{get(z.handle.widget.mid.jobSpecListbox,'value')}; 
1974     
1975 
1976     % if true then we're at the top level expansion
1977     if( strcmp( vName, 'name' ) )
1978 
1979         % this should never be possible
1980         if( ~strcmp( vValue, an.name ) )
1981             msg( sprintf( 'error X11: analysis unit names don''t match:  %s | %s', vValue, an.name ), 'errbox' );
1982             nodes = [];
1983             return;
1984         end
1985 
1986         val = sprintf( 'type.%s', an.type ); 
1987         if( length( an.type ) )
1988             desc = sprintf( 'type: %s', an.type );
1989         else
1990             desc = 'type: <empty> ';
1991         end
1992         nodes(1) = uitreenode( val, desc, [], true );
1993     
1994         if( length( an.fun ) )
1995             val = sprintf( 'fun.%s', func2str( an.fun ) ); 
1996             desc = sprintf( 'function: @%s', func2str( an.fun ) );
1997         else
1998             val = 'fun.';
1999             desc = 'function: <none specified>';
2000         end
2001         nodes(2) = uitreenode( val, desc, [], true );
2002     
2003         val = 'requires.';
2004         if( length( an.requires ) )
2005             % TODO: multiple cells in the reuiqres field aren't really supported in the gui right now.
2006             % if an outside analysis unit is somehow ipmorted with multiple reuqires they will be displayed
2007             % here, but the user only has the ability to modify the first requires{1} cell
2008             desc = sprintf( 'requires: %s', an.requires{1}.name );
2009             for r = 2 : length( an.requires )
2010                 desc = sprintf( '%s, %s', desc, an.requires{r}.name );
2011             end
2012         else
2013             desc = 'requires: <no input selected>';
2014         end
2015         nodes(3) = uitreenode( val, desc, [], true );
2016     
2017         val = sprintf( 'params.' ); 
2018         desc = sprintf( 'params' );
2019         nodes(4) = uitreenode( val, desc, [], false );
2020 
2021         val = sprintf( 'results.' ); 
2022         desc = sprintf( 'results: %d cells', length( an.results ) );
2023         nodes(5) = uitreenode( val, desc, [], true );
2024 
2025         return;
2026     end
2027 
2028     if( strcmp( vName, 'params' ) )
2029 
2030         names = parseNodeName( value );
2031         structPath = 'params';
2032         for x = 2 : length( names )
2033             structPath = sprintf( '%s.%s', structPath, names{x} );
2034         end
2035         evalStr = sprintf( 's = an.%s;', structPath );
2036         eval( evalStr );
2037 
2038         fn = fieldnames( s );
2039         for x = 1 : length(fn)
2040             if( strcmp( fn{x}, 'filt' ) )
2041                 val = sprintf( 'filt.' ); 
2042                 desc = sprintf( 'filt' );
2043                 nodes(x) = uitreenode( val, desc, [], false );
2044             else 
2045                 val = sprintf( '%s.%s', structPath, fn{x} ); 
2046                 fieldValue = getfield( s, fn{x} );
2047                 [dataType, numDimensions] = getDataType( fieldValue );
2048                 isLeaf = true;
2049                 switch dataType
2050 
2051                 case 's'    
2052                     desc = sprintf( '%s: ''%s''', fn{x}, fieldValue ); 
2053 
2054                 case {'d','f'}
2055                     if( numDimensions < 2 )
2056                         if( numDimensions == 1 )
2057                             if( size( fieldValue, 1 ) > 1 )
2058                                 desc = sprintf( '%s: [%s] (column)', fn{x}, num2str(fieldValue') ); 
2059                             else
2060                                 desc = sprintf( '%s: [%s]', fn{x}, num2str(fieldValue) ); 
2061                             end
2062                         else
2063                             desc = sprintf( '%s: %s', fn{x}, num2str(fieldValue) ); 
2064                         end
2065                     else
2066                         desc = sprintf( '%s: %dd matrix', fn{x}, numDimensions ); 
2067                     end
2068 
2069 
2070                 case 'o'
2071                     desc = sprintf( '%s', fn{x} ); 
2072                     isLeaf = false;
2073 
2074                 case 'c'
2075                     desc = sprintf( '%s: %d cells', fn{x}, length(fieldValue) ); 
2076 
2077                 otherwise
2078                     desc = 'unknown field type';
2079                     msg( 'unknown field type  BB29', 'errbox' );
2080                 end
2081 
2082                 nodes(x) = uitreenode( val, desc, [], isLeaf );
2083             end
2084         end
2085 
2086         return;
2087     end
2088 
2089     if( strcmp( vName, 'filt' ) )
2090     
2091         filtList = getFilterList( an.params.filt );
2092         if( length( filtList ) )
2093             for x = 1 : length( filtList )
2094                 val = sprintf( 'filtItem.%d', x );
2095                 desc = sprintf( '%s\n', filtList{x} );
2096                 nodes(x) = uitreenode( val, desc, [], true );
2097             end
2098         else
2099             nodes = [];
2100         end
2101 
2102         return;
2103     end
2104 
2105     msg( sprintf( 'expand analysis unit: unknown node name: %s', vName ), 'errbox' );
2106     nodes = [];
2107 
2108 
2109 
2110 
2111 
2112 %=================================================
2113 % numDimensions:
2114 % 0 is a single value
2115 % 1 is a 1d vector
2116 % 2 is a 2d matrix
2117 % N is an Nd matrix
2118 %
2119 % t:
2120 % o = struct
2121 % c = cell array
2122 % d = int
2123 % f = float
2124 % s = string
2125 % ? = ?
2126 function [t, numDimensions] = getDataType( in )
2127     numDimensions = 0;
2128 
2129     if( isstruct( in ) )
2130         t = 'o';
2131         return;
2132     end
2133     
2134     if( iscell( in ) )
2135         t = 'c';
2136         return;
2137     end
2138 
2139     if( ischar( in ) )
2140         t = 's';
2141     else
2142         if( isnumeric( in ) )
2143             if( numel( in ) > 1 )
2144                 numDimensions = length(size(in)); % same as ndims(in)
2145             end
2146             if( numDimensions == 2 )
2147                 % at this point we know we have either a vector or a 2D matrix.
2148                 % but the caller needs to know which one it is, so here we
2149                 % decrement numDimensions from 2 to 1 for vectors.
2150                 if( size(in,1) ==1 | size(in,2) == 1 )
2151                     numDimensions = numDimensions - 1;    
2152                 end
2153             end
2154                 
2155             if( ceil( in ) == in )
2156                 t = 'd';
2157             else
2158                 t = 'f';
2159             end
2160         else
2161             t = '?';
2162         end
2163     end
2164 
2165 
2166 
2167 function [tree] = ens_getLeftTree( experimentNumbers, figHandle )
2168     
2169     % if you are implementing support for multiple experiments,
2170     % you must search all code for: SME
2171     % this means something about that code needs to be
2172     % changed in order to enable multiple experiment support.
2173     % these markers aren't necessarily the only changes that need to be made
2174     if( length( experimentNumbers ) ~= 1 )
2175         % ### SME
2176         fprintf( 'filterSelectionUI: ERROR: multiple experiments not supported\n' );
2177         tree = [];
2178         return;
2179     end
2180 
2181     z = guidata( figHandle );
2182 
2183 
2184     expMetaData = mysql_extract_metadata( ...
2185                                             'table', 'experiment', ...
2186                                             'conn_id', z.config.conn_id, ...
2187                                             'experiment_id', experimentNumbers ); 
2188 
2189     % value, description, icon, leaf
2190     % nodes values are assigned as: level.row
2191     % so this root node is at experiment level and it's the 1 and only.
2192     rootNode = uitreenode( 'experiment.1', expMetaData.experiment_title, [], false ); 
2193     set( rootNode, 'userdata', experimentNumbers); % ## SME
2194 
2195     % attach the rootNode to the uitree and set the expansion callback.
2196     tree = uitree(    'Root', rootNode, ...
2197                                 'ExpandFcn', {@expandGUITreeCB, expMetaData} );
2198     set( tree, 'NodeSelectedCallback', {@leftTreeSelectionCB, figHandle} );
2199     set( tree, 'MultipleSelectionEnabled', true );
2200     % position and size are relative to the parent uicontainer or figure.
2201     set(tree, 'Units', 'normalized', 'position', [0 0 1 1] );
2202 
2203 
2204 
2205 
2206 % =======================================================
2207 function leftTreeSelectionCB( tree, value, figHandle )
2208 
2209     nodes = tree.SelectedNodes;
2210 
2211     if( 0 ) % DEBUG
2212         fprintf( '%d items selected\n', length(nodes) );
2213     end
2214 
2215     % data for selections keeps track of experiment number,
2216     % form number, and question number.
2217     % a selection can happen at any level, so a selection
2218     % line item (one node) may be an experiment, a form,
2219     % or a particular question.
2220     % the selectedData matrix stores all mixed types of line
2221     % items, one per row, with cols 1,2,3 storing experiment,form,question
2222     % ids in that order. selections at the form level will leave
2223     % the 3rd column blank. example of a selectedData matrix:
2224     %    [12        45        123
2225     %    12            0        0
2226     %    12            45        124
2227     %    12            46        0]
2228     selectedData = zeros(length(nodes),3);
2229 
2230     for x = 1 : length( nodes )
2231 
2232         % parse the value field of this node to find out
2233         % if this is an experiment, form, or question
2234         v = get( nodes(x), 'value' );
2235         p = strfind( v, '.' );
2236         type = v(1:p-1);
2237 
2238         % we could add error checking for p < 0 but this should never happen
2239 
2240         if( strcmp( type, 'experiment' ) )
2241             selectedData(x,:) = [get(nodes(x),'userdata') 0 0];
2242         end
2243 
2244         if( strcmp( type, 'form' ) )
2245             % find out experiment id by looking at parent
2246             e = get(get(nodes(x),'parent'),'userdata');
2247             selectedData(x,:) = [e get(nodes(x),'userdata') 0];
2248         end
2249         
2250         if( strcmp( type, 'question' ) )
2251             % find out experiment id by looking at parent
2252             p1 = get(nodes(x),'parent');
2253             p2 = get(p1,'parent');
2254             f = get(p1,'userdata');    
2255             e = get(p2,'userdata');    
2256             q = get(nodes(x),'userdata');
2257             selectedData(x,:) = [e f q];
2258         end
2259 
2260         if( 0 ) % DEBUG
2261             fprintf( 'selected %d: [%d %d %d]\n', x, selectedData(x,1),selectedData(x,2),selectedData(x,3) );
2262             fprintf( '%d name: %s   value: %s  userdata: %d\n', ...
2263                             x, get(nodes(x),'name'), get(nodes(x), 'value'), get(nodes(x),'userdata') );
2264         end
2265     end
2266 
2267     % update global (figure) app data struct
2268     z = guidata( figHandle );
2269 
2270     % toggle enable submit button for the left panel
2271     % i don't think there's any way to unselect all
2272     % and even if there were, i don' tthink it would
2273     % call this selection CB, but handle the 0 case just in case
2274     
2275     % there has to be an existing analysis unit selected also
2276     whichAnUnit = get( z.handle.widget.mid.jobSpecListbox, 'value' );
2277 
2278     
2279     if( length(nodes) > 0 & whichAnUnit > 0 )
2280         set( z.handle.widget.bottom.addexclude, 'enable', 'on' );
2281         set( z.handle.widget.bottom.addinclude, 'enable', 'on' );
2282     else
2283         set( z.handle.widget.bottom.addexclude, 'enable', 'off' );
2284         set( z.handle.widget.bottom.addinclude, 'enable', 'off' );
2285     end
2286 
2287     z.data.left.selected = selectedData;
2288     guidata( figHandle, z );
2289 
2290 
2291 
2292 
2293 
2294 
2295 % ======================================================
2296 function [nodes] = expandGUITreeCB( tree, value, expMetaData )
2297 
2298 
2299     p = strfind( value, '.' );
2300     if( p <= 0 )
2301         fprintf( 'expandGUITreeCB: ERROR: bad node value: %s\n', value );
2302         nodes = [];
2303         return;
2304     end
2305 
2306     level = value(1:p-1);
2307     row = str2num( value( p+1:end) );
2308 
2309     if( 0 ) % DEBUG
2310         fprintf( 'expanding level %s, row %d\n', level, row );
2311     end
2312     
2313     if( strcmp( level, 'experiment' ) )
2314         numChild = length( expMetaData.form );
2315         nextLevel = 'form';
2316     end
2317     if( strcmp( level, 'form' ) )
2318         numChild = length( expMetaData.form(row).question );
2319         nextLevel = 'question';
2320     end
2321     
2322     % create a uitree node for each of the items in the next expansion
2323     for x = 1 : numChild    
2324 
2325 
2326         if( strcmp( level, 'experiment' ) )
2327             desc = sprintf( '%s (%d)', ...
2328                  expMetaData.form(x).form_name, ...
2329                  expMetaData.form(x).form_id );
2330 
2331             % if it has questions associated with it, then it's not a leaf
2332             if( length( expMetaData.form(x).question ) > 0 )
2333                 isLeaf = false;
2334             else
2335                 isLeaf = true;
2336             end
2337             record_id = expMetaData.form(x).form_id;
2338         end
2339 
2340 
2341         if( strcmp( level, 'form' ) )
2342             desc = sprintf( '%s (%s)', ...
2343                  expMetaData.form(row).question(x).question_text, ...
2344                  sprintf( '%.04f',expMetaData.form(row).question(x).compqid ) );
2345             isLeaf = true;
2346             record_id = expMetaData.form(row).question(x).question_id;
2347         end
2348     
2349         % matlab automatically attaches this list of nodes to the
2350         % parent in the uitree.
2351         nodes(x) = uitreenode( sprintf( '%s.%d',nextLevel,x ), ...
2352                         desc, [], isLeaf );
2353         set(nodes(x),'userdata',record_id);
2354 
2355     end
2356     
2357 
2358 
2359 
2360 
2361 
2362 
2363 
2364 
2365 
2366 
2367 
2368 
2369 
2370 
2371 
2372 
2373 
2374 
2375 
2376 
2377 
2378

Generated on Wed 20-Sep-2023 04:00:50 by m2html © 2003