Home > marsbar > mars_armoire.m

mars_armoire

PURPOSE ^

multifunction function to get/set various stores of stuff

SYNOPSIS ^

function varargout = mars_armoire(action, item, data, filename)

DESCRIPTION ^

 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$

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

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

Generated on Wed 11-May-2022 16:26:09 by m2html © 2003-2019