multifunction function to get/set various stores of stuff (armoire is the French for cupboard). FORMAT varargout = mars_armoire(action, item, data, filename) This cupboard is to put items which I will want to fish out from time to time. The items may well be associated with a filename If they are associated with a filename when set, they are assumed to have been saved already. If not, they are flagged as awaiting a save If the data changes, you can indicate this with the update method, which changes the data, and flags for a save In terms of the program structure, the function acts an object container, but where the objects are only half implemented, in this case as fields in a global variable MARMOIRE. The permissable actions are: add - add an item to the armoire exist - ask if there an exists an item of given name add_if_absent - adds item if it does not yet exist set - sets data for item get - gets data from item set_ui - sets data, getting via UI save - save data for item, if required save_ui - saves, using GUI to ask for filename For save and save_ui, the 'data' argument can contain a structure with flags. Fields in flag structure can be 'force' - force save even if not flagged as needed 'warn_empty' - GUI warn if no data to save 'ync' - start save with save y/n/cancel dialog 'prompt' - prompt for save; 'prompt_suffix - suffix for prompt 'prompt_prefix - prefix for prompt 'ui' - use UI prompts for save - forced if save_ui 'no_no_save' - if 'no' is chosen in the save dialog, contents are flagged as not needing a save in the future (has_changed flag set to 0) save 'all' - saves data for all items, if required update - updates data, sets flag to show change clear - clears data for item isempty - returns 1 if no data for item need_save - returns 1 if this item needs a save And for use in debugging: dump - returns contents of the underling variable for any other action string, mars_armoire will look to see if the action matches any of the field names in the structures and get/set this fieldname value (set if data is not empty, get otherwise) Each item is stored in a field in the global variable The name of the field is the 'item' argument to this function Each item field requires the following fields data - the data (or a filename which loads as the data - see the char_is_filename field) has_changed - flag, if set, means data has changed since first set save_if_changed - flag, if set, will try to save changed data when a save is requested. Saves can also be forced. leave_as_file - flag, if set, will attempt to leave the data, defined by the filename, on the disk, not in memory, and only load the data for a 'get'. Otherwise, if a set occurs, and the data field is empty, will load data into the global variable when 'set'ing field and leave it there. If the data changes, and requires a save, this field has no function, until the next save. file_name - file name of .mat file containing data If data is empty, and file_name is not, an attempt to 'get' data will load contents of file_name default_file_name - default filename offered for save file_type - type of file to load ('mat' or 'ascii') char_is_filename - flag, if set, char data is assumed to be a filename filter_spec - filter spec for uigetfile (see help uigetfile) prompt - prompt for uigetfile verbose - flag, if set, displays more information during processing set_action - actions to perform when item is set in form of callback string. This is executed in the 'i_set' subfunction, and can use all variables functions defined therein. See programmers notes in the function for callback format set_action_if_update - flag, if set, applied set_action for 'update' as well as 'set' set_action_if_clear - flag, if set, applied set_action for 'clear' as well as 'set' $Id$
0001 function varargout = mars_armoire(action, item, data, filename) 0002 % multifunction function to get/set various stores of stuff 0003 % (armoire is the French for cupboard). 0004 % FORMAT varargout = mars_armoire(action, item, data, filename) 0005 % 0006 % This cupboard is to put items which I will want to fish out 0007 % from time to time. 0008 % 0009 % The items may well be associated with a filename 0010 % If they are associated with a filename when set, they 0011 % are assumed to have been saved already. 0012 % If not, they are flagged as awaiting a save 0013 % 0014 % If the data changes, you can indicate this with the 0015 % update method, which changes the data, and flags for a save 0016 % 0017 % In terms of the program structure, the function acts an object container, 0018 % but where the objects are only half implemented, in this case as fields in 0019 % a global variable MARMOIRE. 0020 % 0021 % The permissable actions are: 0022 % 0023 % add - add an item to the armoire 0024 % exist - ask if there an exists an item of given name 0025 % add_if_absent - adds item if it does not yet exist 0026 % set - sets data for item 0027 % get - gets data from item 0028 % set_ui - sets data, getting via UI 0029 % save - save data for item, if required 0030 % save_ui - saves, using GUI to ask for filename 0031 % For save and save_ui, the 'data' argument 0032 % can contain a structure with flags. 0033 % Fields in flag structure can be 0034 % 'force' - force save even if not flagged as needed 0035 % 'warn_empty' - GUI warn if no data to save 0036 % 'ync' - start save with save y/n/cancel dialog 0037 % 'prompt' - prompt for save; 0038 % 'prompt_suffix - suffix for prompt 0039 % 'prompt_prefix - prefix for prompt 0040 % 'ui' - use UI prompts for save - forced if save_ui 0041 % 'no_no_save' - if 'no' is chosen in the save dialog, 0042 % contents are flagged as not needing a save in 0043 % the future (has_changed flag set to 0) 0044 % save 'all' - saves data for all items, if required 0045 % update - updates data, sets flag to show change 0046 % clear - clears data for item 0047 % isempty - returns 1 if no data for item 0048 % need_save - returns 1 if this item needs a save 0049 % 0050 % And for use in debugging: 0051 % dump - returns contents of the underling variable 0052 % 0053 % for any other action string, mars_armoire will look to see if the action 0054 % matches any of the field names in the structures and get/set this 0055 % fieldname value (set if data is not empty, get otherwise) 0056 % 0057 % Each item is stored in a field in the global variable 0058 % 0059 % The name of the field is the 'item' argument to this function 0060 % Each item field requires the following fields 0061 % 0062 % data - the data 0063 % (or a filename which loads as the data - see the 0064 % char_is_filename field) 0065 % has_changed - flag, if set, means data has changed since first set 0066 % save_if_changed - flag, if set, will try to save changed data when a 0067 % save is requested. Saves can also be forced. 0068 % leave_as_file - flag, if set, will attempt to leave the data, defined 0069 % by the filename, on the disk, not in memory, and only 0070 % load the data for a 'get'. 0071 % Otherwise, if a set occurs, and the data field is 0072 % empty, will load data into the global variable when 0073 % 'set'ing field and leave it there. 0074 % If the data changes, and requires a save, this field 0075 % has no function, until the next save. 0076 % file_name - file name of .mat file containing data 0077 % If data is empty, and file_name is not, 0078 % an attempt to 'get' data will load contents of 0079 % file_name 0080 % default_file_name - default filename offered for save 0081 % file_type - type of file to load ('mat' or 'ascii') 0082 % char_is_filename - flag, if set, char data is assumed to be a filename 0083 % filter_spec - filter spec for uigetfile (see help uigetfile) 0084 % prompt - prompt for uigetfile 0085 % verbose - flag, if set, displays more information during 0086 % processing 0087 % set_action - actions to perform when item is set 0088 % in form of callback string. This is executed 0089 % in the 'i_set' subfunction, and can use all 0090 % variables functions defined therein. See programmers 0091 % notes in the function for callback format 0092 % set_action_if_update - flag, if set, applied set_action for 'update' as 0093 % well as 'set' 0094 % set_action_if_clear - flag, if set, applied set_action for 'clear' as 0095 % well as 'set' 0096 % 0097 % $Id$ 0098 0099 % Programmers' notes 0100 % ------------------ 0101 % set_action callbacks 0102 % callbacks should be one of the following two formats; 0103 % 0104 % [data errf msg] = my_function(args) or 0105 % [item_field errf msg] = my_function(args) 0106 % 0107 % The first form just returns the data desired to be set, 0108 % the second returns the whole item field, where the data 0109 % is contained in the field 'data'. 0110 % if 'errf' is set, the routine warns, and abort the set with 0111 % the 'msg'. 0112 % 0113 % The available args are: 0114 % I - proposed whole item field contents 0115 % data - proposed data to be inserted 0116 % passed_filename - filename passed to function 0117 % 0118 % and anything else... 0119 0120 % NaN for an argument signals it has not been passed 0121 % empty means that it was passed, but was empty 0122 if nargin < 1 % no action 0123 error('Need action!'); 0124 return 0125 end 0126 if ~ismember(action, {'dump'}) 0127 if nargin < 2 % no item 0128 error('Need item!'); 0129 return 0130 end 0131 end 0132 if nargin < 3 0133 data = NaN; 0134 end 0135 if nargin < 4 0136 filename = NaN; 0137 end 0138 0139 % certain actions do not require valid item names 0140 if ~ismember(action, ... 0141 {'add', 'add_if_absent', 'exist', 'dump', 'save_all'}) 0142 % the rest do 0143 flist = i_item_list; 0144 switch item 0145 case 'all' 0146 % If item is 'all', do this action for all items 0147 % Watch for save_ui, as we need to look out for cancel 0148 s_u_f = strcmp(lower(action), 'save_ui'); 0149 a = {}; 0150 for fn = flist' 0151 a{end+1} = mars_armoire(action, fn{1}, data, filename); 0152 if s_u_f & (a{end} == -1), varargout = {-1}; return, end 0153 end 0154 varargout = a; 0155 return 0156 otherwise 0157 % item must be a field name in structure 0158 % fetch and set name field 0159 if ~ismember(item, flist) 0160 error([item ' is an unaccountable item']); 0161 end 0162 i_contents = i_up_dump(item); 0163 i_contents.name = item; 0164 i_contents.last_action = action; 0165 end 0166 end 0167 0168 % run actions 0169 switch lower(action) 0170 case 'add' 0171 data.name = item; 0172 I = i_def; 0173 def_fns = fieldnames(I); 0174 new_fns = def_fns(~ismember(def_fns, fieldnames(data))); 0175 for fn = new_fns' 0176 data = setfield(data, fn{1}, getfield(I, fn{1})); 0177 end 0178 i_down_dump(data); 0179 case 'add_if_absent' 0180 if ~mars_armoire('exist', item) 0181 mars_armoire('add', item, data); 0182 end 0183 case 'exist' 0184 varargout = {ismember(item, i_item_list)}; 0185 case 'default_item' 0186 varargout = {i_def}; 0187 case 'set' 0188 if is_nan(data) & is_nan(filename) 0189 varargout = {i_set_ui(i_contents)}; 0190 else 0191 varargout = {i_set(i_contents, data, filename)}; 0192 end 0193 case 'get' 0194 if i_isempty(i_contents) 0195 varargout = {i_set_ui(i_contents)}; 0196 else 0197 varargout = {i_get(i_contents)}; 0198 end 0199 case 'set_ui' 0200 varargout = {i_set_ui(i_contents)}; 0201 case 'update' 0202 varargout = {i_set(i_contents, data, filename)}; 0203 i_contents = i_up_dump(item); 0204 i_contents.has_changed = 1; 0205 i_down_dump(i_contents); 0206 case 'clear' 0207 varargout = {i_set(i_contents, [], '')}; 0208 case 'save' 0209 if is_nix(filename) & ... 0210 isempty(i_contents.file_name) 0211 varargout = {i_save_ui(i_contents, data, filename)}; 0212 else 0213 varargout = {i_save(i_contents, data, filename)}; 0214 end 0215 case 'save_ui' 0216 % data is used as flags for save call 0217 if ~isstruct(data), data = []; end 0218 data.ui = 1; 0219 varargout = {i_save_ui(i_contents, data, filename)}; 0220 case 'need_save' 0221 varargout = {i_need_save(i_contents)}; 0222 case 'isempty' 0223 varargout = {i_isempty(i_contents)}; 0224 case 'dump' 0225 varargout = {i_dump}; 0226 otherwise 0227 % look in fieldnames 0228 if ismember(action, fieldnames(i_contents)) 0229 if ~is_nan(data) % it's a set 0230 i_contents = setfield(i_contents, action, data); 0231 i_down_dump(i_contents); 0232 end 0233 varargout = {getfield(i_contents, action)}; 0234 else % really, this must be a mistake 0235 error(['The suggested action, ' action ', is disturbing']); 0236 end 0237 end 0238 return % end of main function 0239 0240 function I = i_def 0241 % returns default item 0242 I = struct('data', [],... 0243 'file_name', '',... 0244 'default_file_name','',... 0245 'has_changed', 0,... 0246 'leave_as_file', 0,... 0247 'save_if_changed', 1,... 0248 'file_type', 'mat',... 0249 'char_is_filename',1,... 0250 'set_action_if_update', 0 ,... 0251 'set_action_if_clear', 0 ,... 0252 'verbose', 1,... 0253 'title', 'file',... 0254 'filter_spec', '',... 0255 'set_action', ''); 0256 return 0257 0258 function res = i_isempty(I) 0259 res = isempty(I.data) & isempty(I.file_name); 0260 return 0261 0262 function res = i_set_ui(I) 0263 [fn pn] = mars_uifile('get', I.filter_spec, ['Select ' I.title '...']); 0264 if isequal(fn,0) | isequal(pn,0), res = []; return, end 0265 res = i_set(I, [], fullfile(pn, fn)); 0266 return 0267 0268 0269 function res = i_set(I, data, filename) 0270 0271 % Keep copy of passed filename for set_action call 0272 passed_filename = filename; 0273 0274 % optionally, treat char data as filename 0275 % but passed filename overrides char data 0276 if I.char_is_filename & ischar(data) 0277 if ~is_nix(filename) 0278 warning(sprintf(... 0279 'Passed filename %s overrides data filename %s\n',... 0280 filename, data)); 0281 else 0282 filename = data; 0283 end 0284 data = []; 0285 end 0286 0287 if is_nix(filename) % may need to save if no associated filename 0288 I.has_changed = 1; 0289 else % don't need to save, but may need to load from file 0290 I.has_changed = 0; 0291 if isempty(data) 0292 data = load(filename, ['-' I.file_type]); 0293 end 0294 end 0295 I.data = data; 0296 0297 % If no filename passed: 0298 % if new set, filename is empty 0299 % if an update, filename stays 0300 is_update = strcmp(I.last_action, 'update'); 0301 if is_nan(filename) 0302 if ~is_update 0303 filename = ''; 0304 end 0305 end 0306 I.file_name = filename; 0307 0308 % If this was a clear, don't flag for save 0309 if i_isempty(I), I.has_changed = 0; end 0310 0311 % and here is where we do the rules stuff 0312 is_clear = strcmp(I.last_action, 'clear'); 0313 if ~isempty(I.set_action) & ... 0314 (ismember(I.last_action, {'get','set','set_ui'}) | ... 0315 (is_update & I.set_action_if_update) | ... 0316 (is_clear & I.set_action_if_clear)) 0317 [tmp errf msg] = eval(I.set_action); 0318 if errf 0319 res = []; 0320 warning(['Data not set: ' msg]); 0321 return 0322 end 0323 % work out if whole thing as been returned, or only data 0324 if isfield(tmp, 'set_action') % whole thing 0325 I = tmp; 0326 else % it's just the data 0327 I.data = tmp; 0328 end 0329 end 0330 0331 % return set data 0332 res = I.data; 0333 0334 % possibly remove data from structure 0335 if ~I.has_changed & I.leave_as_file 0336 I.data = []; 0337 end 0338 0339 % do the actual save into global structure 0340 i_down_dump(I); 0341 0342 return 0343 0344 function res = i_get(I) 0345 res = I.data; 0346 if isempty(res) & ~isempty(I.file_name) 0347 res = load(I.file_name, ['-' I.file_type]); 0348 end 0349 return 0350 0351 function res = i_save_ui(I, flags, filename) 0352 if ~isstruct(flags), flags = []; end 0353 if i_isempty(I) & isfield(flags, 'warn') 0354 msgbox('Nothing to save', [I.title ' is not set'], 'warn'); 0355 res = 0; 0356 return 0357 end 0358 flags.ui = 1; 0359 res = i_save(I, flags, filename); 0360 return 0361 0362 function res = i_save(I, flags, filename) 0363 % data field is treated as flags 0364 if is_nix(flags) flags == []; end 0365 if is_nix(filename), filename = I.file_name; end 0366 if is_nix(filename), filename = I.default_file_name; end 0367 if i_need_save(I) | isfield(flags, 'force') % force flag 0368 % prompt for filename if UI 0369 if isfield(flags, 'ui') 0370 % Work out prompt 0371 if isfield(flags, 'prompt') 0372 prompt = flags.prompt; 0373 else 0374 prompt = I.title; 0375 end 0376 if isfield(flags, 'prompt_prefix') 0377 prompt = [flags.prompt_prefix prompt]; 0378 end 0379 if isfield(flags, 'prompt_suffix') 0380 prompt = [prompt flags.prompt_suffix]; 0381 end 0382 if isfield(flags, 'ync') 0383 save_yn = questdlg(['Save ' prompt '?'],... 0384 'Save', 'Yes', 'No', 'Cancel', 'Yes'); 0385 if strcmp(save_yn, 'Cancel'), res = -1; return, end 0386 if strcmp(save_yn, 'No') 0387 if isfield(flags, 'no_no_save') 0388 I.has_changed = 0; 0389 i_down_dump(I); 0390 end 0391 res = 0; 0392 return 0393 end 0394 end 0395 pr = ['Filename to save ' prompt]; 0396 [f p] = mars_uifile('put', I.filter_spec, pr, filename); 0397 if all(f==0), res = -1, return, end 0398 filename = fullfile(p, f); 0399 end 0400 savestruct(I.data, filename); 0401 if I.verbose 0402 fprintf('%s saved to %s\n', I.title, filename); 0403 end 0404 I.file_name = filename; 0405 I.has_changed = 0; 0406 if I.leave_as_file 0407 % maintain only on file, as it has beed saved 0408 I.data = []; 0409 end 0410 res = 1; 0411 i_down_dump(I); 0412 else 0413 res = 0; 0414 end 0415 return 0416 0417 function res = i_need_save(I) 0418 res = ~i_isempty(I) & I.has_changed & I.save_if_changed; 0419 return 0420 0421 function res = is_nix(v) 0422 res = isempty(v) | is_nan(v); 0423 return 0424 0425 function res = is_nan(v) 0426 res = 0; 0427 if isnumeric(v) & ~isempty(v) 0428 res = isnan(v); 0429 end 0430 return 0431 0432 function items = i_item_list 0433 items = g_fieldnames; 0434 return 0435 0436 function I = i_up_dump(i_name) 0437 I = g_getfield(i_name); 0438 return 0439 0440 % Routines below explicity manipulate global variable 0441 0442 function I = i_dump 0443 global MARMOIRE 0444 I = MARMOIRE; 0445 return 0446 0447 function I = i_down_dump(I) 0448 global MARMOIRE 0449 MARMOIRE = setfield(MARMOIRE, I.name, I); 0450 return 0451 0452 function fns = g_fieldnames 0453 global MARMOIRE 0454 if isempty(MARMOIRE) | ~isstruct(MARMOIRE) 0455 fns = {}; 0456 else 0457 fns = fieldnames(MARMOIRE); 0458 end 0459 return 0460 0461 function r = g_getfield(fn) 0462 global MARMOIRE 0463 r = getfield(MARMOIRE, fn); 0464 return