% POP_EDITEVENTVALS - Edit events contained in an EEG dataset structure.
%               If the dataset is the only input, a window pops up
%               allowing the user to insert the relevant parameter values.
%
% Usage: >> EEGOUT = pop_editeventvals( EEG, 'key1', value1, ...
%                                    'key2', value2, ... );
% Input:
%   EEG  - EEG dataset
%
% Optional inputs:
%   'sort'        - { field1 dir1 field2 dir2 } Sort events based on field1
%                   then on optional field2. Arg dir1 indicates the sort
%                   direction (0 = increasing, 1 = decreasing).
%   'changefield' - {num field value} Insert the given value into the specified
%                   field in event num. (Ex: {34 'latency' 320.4}). Note
%                   that for latency event latencies are in millisecond
%                   compared to the time locking event not in data samples.
%   'changeevent' - {num value1 value2 value3 ...} Change the values of
%                   all fields in event num.
%   'add','append','insert' - {num value1 value2 value3 ...} Insert event
%                   before or at event num, and assign value to structure
%                   fields. Note that the latency field must be in second
%                   and will be converted to data sample. Note also that
%                   the index of the event is often irrelevant, as events
%                   will be automatically resorted by latencies.
%   'delete'      - vector of indices of events to delete
%
% Outputs:
%   EEGOUT        - EEG dataset with the selected events only
%
% Ex:  EEG = pop_editeventvals(EEG,'changefield', { 1 'type' 'target'});
%        % set field type of event number 1 to 'target'
%
% Author: Arnaud Delorme & Hilit Serby, SCCN, UCSD, 15 March 2002
%
% See also: POP_SELECTEVENT, POP_IMPORTEVENT

% Copyright (C) Arnaud Delorme, CNL / Salk Institute, 15 March 2002, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

% 03-16-02 text interface editing -sm & ad
% 03-18-02 automatic latency switching display (epoch/continuous) - ad & sm
% 03-18-02 debug soring order - ad
% 03-18-02 put latencies in ms - ad, lf & sm
% 03-29-02 debug latencies in ms - ad & sm
% 04-02-02 debugging test - ad & sm

function [EEG, com] = pop_editeventvals(EEG, varargin)

com ='';
if nargin < 1
   help pop_editeventvals;
   return;
end

if nargin >= 2 || ischar(EEG) % interpreting command from GUI or command line

    if ischar(EEG) % GUI
        gui    = 1;
        varargin = { EEG varargin{:} };

        % user data
        % ---------
        userdata  = get(gcf, 'userdata');
        EEG       = userdata{1};
        oldcom    = userdata{2};
        allfields = fieldnames(EEG.event);
        tmpind    = strmatch('urevent', allfields);
        allfields(tmpind) = [];

        % current event
        % -------------
        objevent  = findobj('parent', gcf, 'tag', 'numval');
        valnum    = str2double(get(objevent, 'string'));
        shift     = 0;

    else % command line

        gui = 0;
        if isempty(EEG.event)
            disp('Getevent: cannot deal with empty event structure');
            return;
        end

        allfields = fieldnames(EEG.event);
        tmpind = strmatch('urevent', allfields);
        allfields(tmpind) = [];

    end

    % retinterpret inputs (fix BUG 454)
    % --------------------------------
    if ~gui
        newvararg = {};
        for indfield = 1:2:length(varargin)

            com     = varargin{ indfield   };
            tmpargs = varargin{ indfield+1 };
            newvararg = { newvararg{:} com };

            if any(strcmpi({'add','insert','append'},com))
                evtind     = tmpargs{1};
                fields     = fieldnames(EEG.event);
                emptycells = cell(1,length(fields)-1);
                newvararg  = { newvararg{:}, { evtind emptycells{:} } };
                if strcmpi(com, 'append'), evtind = evtind+1; end

                for ind = 2:length( tmpargs )
                    if ~strcmpi(fields{ind-1}, 'urevent')
                        newvararg = { newvararg{:},'changefield',{ evtind fields{ind-1} tmpargs{ind} } };
                    end
                end
            else
                newvararg = { newvararg{:} tmpargs };
            end
        end
        varargin = newvararg;
    end

    % scan inputs
    % -----------
    for indfield = 1:2:length(varargin)
        if length(varargin) >= indfield+1
            tmparg = varargin{ indfield+1 };
        end

        switch lower(varargin{indfield})

     case 'goto' % ******************** GUI ONLY ***********************

      % shift time
      % ----------
      shift     = tmparg;
      valnum    = valnum + shift;
      if valnum < 1,                valnum = 1;                end
      if valnum > length(EEG.event), valnum = length(EEG.event); end
      set(objevent, 'string', num2str(valnum,5));

      % update fields
      % -------------
      for index = 1:length(allfields)

          enable = 'on';
          if isfield(EEG.event, 'type')
              if strcmpi(EEG.event(valnum).type, 'boundary'), enable = 'off'; end
          end
          if strcmp( allfields{index}, 'latency') && ~isempty(EEG.event(valnum).latency)
              if isfield(EEG.event, 'epoch')
                   value = eeg_point2lat( EEG.event(valnum).latency, EEG.event(valnum).epoch, ...
                                          EEG.srate, [EEG.xmin EEG.xmax]*1000, 1E-3);
              else value = (EEG.event(valnum).latency-1)/EEG.srate+EEG.xmin;
              end
          elseif strcmp( allfields{index}, 'duration') && ~isempty(EEG.event(valnum).duration)
              if isfield(EEG.event, 'epoch')
                   value = EEG.event(valnum).duration/EEG.srate*1000; % milliseconds
              else value = EEG.event(valnum).duration/EEG.srate;      % seconds
              end
          else
              value = getfield( EEG.event(valnum), allfields{index});
          end

          % update interface
          % ----------------
          tmpobj = findobj('parent', gcf, 'tag', allfields{index});
          if isnumeric(value)
              set(tmpobj, 'string', num2str(value,5), 'enable', enable);
          else % for strings and cells
              set(tmpobj, 'string', value, 'enable', enable);
          end

      end

      % update original
      % ---------------
      tmpobj = findobj('parent', gcf, 'tag', 'original');
      if isfield(EEG.event, 'urevent') && ~isempty(EEG.event(valnum).urevent) && EEG.event(valnum).urevent ~= valnum
           set(tmpobj, 'string', [ 'originally ' int2str(EEG.event(valnum).urevent)], ...
                       'horizontalalignment', 'center');
      else set(tmpobj, 'string', ' ');
      end
      return; % NO NEED TO SAVE ANYTHING

     case { 'append' 'insert' 'add' } % **********************************************************

      if gui
          shift     = tmparg; % shift is for adding before or after the event

          % add epoch number if data epoch
          % ------------------------------
          tmpcell    = cell(1,1+length(fieldnames(EEG.event)));
          tmpcell{1} = valnum;
          if EEG.trials > 1
              indepoch = strmatch('epoch', fieldnames(EEG.event), 'exact');
              if valnum > 1, tmpprevval = valnum-1;
              else           tmpprevval = valnum+1;
              end
              if tmpprevval <= length(EEG.event)
                  tmpcell{indepoch+1} = EEG.event(tmpprevval).epoch;
              end
          end

          % update commands
          % ---------------
          if shift
              oldcom     = { oldcom{:} 'append', tmpcell };
          else
              oldcom     = { oldcom{:} 'insert', tmpcell };
          end
      else
          if strcmpi(lower(varargin{indfield}), 'append') % not 'add' for backward compatibility
               shift = 1;
          else shift = 0;
          end
          valnum = tmparg{1};
      end

      % find ur index
      % -------------
      valnum    = valnum   + shift;
      if isfield(EEG.event, 'epoch'), curepoch = EEG.event(valnum).epoch; end
      if isfield(EEG, 'urevent') && isfield(EEG.event, 'urevent')
          % find non empty urvalnum
          urvalnum = [];
          count = 0;
          while isempty(urvalnum)
              tmpindex = mod(valnum+count-1, length(EEG.event)+1)+1;
              urvalnum = EEG.event(valnum+count).urevent;
              count = count+1;
          end
          if isfield(EEG.urevent, 'epoch'), urcurepoch = EEG.urevent(urvalnum).epoch; end
          urvalnum = urvalnum;
      end

      % update urevents
      % ---------------
      if isfield(EEG, 'urevent') && isfield(EEG.event, 'urevent')
          EEG.urevent(end+3)            = EEG.urevent(end);
          EEG.urevent(urvalnum+1:end-2) = EEG.urevent(urvalnum:end-3);
          EEG.urevent(urvalnum)         = EEG.urevent(end-1);
          EEG.urevent                   = EEG.urevent(1:end-2);
          if isfield(EEG.urevent, 'epoch'),  EEG.urevent(urvalnum).epoch = urcurepoch; end
      end

      % update events
      % -------------
      EEG.event(end+3)          = EEG.event(end);
      EEG.event(valnum+1:end-2) = EEG.event(valnum:end-3);
      EEG.event(valnum)         = EEG.event(end-1);
      EEG.event                 = EEG.event(1:end-2);
      if isfield(EEG.event, 'epoch'), EEG.event(valnum).epoch = curepoch; end;
      if isfield(EEG, 'urevent') && isfield(EEG.event, 'urevent')
          EEG.event(valnum).urevent = urvalnum;
          for index = valnum+1:length(EEG.event)
              EEG.event(index).urevent = EEG.event(index).urevent+1;
          end
      end

      % update type field
      % -----------------
      for tmpind = 1:length(allfields)
          EEG.event = checkconsistency(EEG.event, valnum, allfields{tmpind});
      end

     case 'delete' % **********************************************************

      if ~gui
          valnum = tmparg;
      end

      EEG.event(valnum) = [];

      if gui

          valnum           = min(valnum,length(EEG.event));
          set(objevent, 'string', num2str(valnum));

          % update commands
          % ---------------
          oldcom = { oldcom{:} 'delete', valnum };

      end

     case { 'assign' 'changefield' } % **********************************************************

      if gui % GUI case

          field    = tmparg;
          objfield = findobj('parent', gcf, 'tag', field);
          editval     = get(objfield, 'string');
          if ~isempty(editval) && ~isnan(str2double(editval)), editval = str2double(editval); end

          % update history
          % --------------
          oldcom = { oldcom{:},'changefield',{ valnum field editval }};

      else % command line case

          valnum  = tmparg{1};
          field   = tmparg{2};
          editval = tmparg{3};

      end

      % latency and duration case
      % -------------------------
      if strcmp( field, 'latency') && ~isempty(editval)
          if isfield(EEG.event, 'epoch')
               editval = eeg_lat2point( editval, EEG.event(valnum).epoch, ...
                                       EEG.srate, [EEG.xmin EEG.xmax]*1000, 1E-3);
          else editval = (editval- EEG.xmin)*EEG.srate+1;
          end
      end
      if strcmp( field, 'duration') && ~isempty(editval)
          if isfield(EEG.event, 'epoch')
               editval = editval/1000*EEG.srate; % milliseconds
          else editval = editval*EEG.srate;      % seconds
          end
      end

      % adapt to other formats
      % ----------------------
      EEG.event(valnum) = setfield(EEG.event(valnum), field, editval);
      EEG.event = checkconsistency(EEG.event, valnum, field);

      % update urevents
      % ---------------
      if isfield(EEG, 'urevent') && isfield(EEG.event, 'urevent')
          urvalnum  = EEG.event(valnum).urevent;

          % latency case
          % ------------
          if strcmp( field, 'latency') && ~isempty(editval)
              if isfield(EEG.urevent, 'epoch')
                  urepoch = EEG.urevent(urvalnum).epoch;

                  % find closest event latency
                  % --------------------------
                  if valnum<length(EEG.event)
                      if EEG.event(valnum+1).epoch == urepoch
                          urlatency = EEG.urevent(EEG.event(valnum+1).urevent).latency;
                          latency   = EEG.event(valnum+1).latency;
                      end
                  end
                  if valnum>1
                      if EEG.event(valnum-1).epoch == urepoch
                          urlatency = EEG.urevent(EEG.event(valnum-1).urevent).latency;
                          latency   = EEG.event(valnum-1).latency;
                      end
                  end

                  % update event
                  % ------------
                  if exist('urlatency') ~=1
                      disp('Urevent not updated: could not find other event in the epoch');
                  else
                      editval = urlatency - ( latency - editval ); % new latency value
                  end
              else
                  editval = eeg_urlatency(EEG.event, EEG.event(valnum).latency);
              end
          elseif strcmp( field, 'latency') % empty editval
              EEG.event(valnum).latency = NaN;
          end

          % duration case
          % ------------
          if strcmp( field, 'duration') && ~isempty(editval)
              if isfield(EEG.event, 'epoch')
                   editval = editval/1000*EEG.srate; % milliseconds -> point
              else editval = editval*EEG.srate;      % seconds -> point
              end
          end
          EEG.urevent = setfield(EEG.urevent, {urvalnum}, field, editval);
      end

     case 'sort' % **********************************************************

      if gui % retrieve data

          field1 = get(findobj('parent', gcf, 'tag', 'listbox1'), 'value');
          field2 = get(findobj('parent', gcf, 'tag', 'listbox2'), 'value');
          dir1   = get(findobj('parent', gcf, 'tag', 'order1'),   'value');
          dir2   = get(findobj('parent', gcf, 'tag', 'order2'),   'value');

          if field1 > 1, field1 = allfields{field1-1}; else return; end
          if field2 > 1, field1 = allfields{field2-1}; else field2 = []; end

          % update history
          % --------------
          oldcom = { oldcom{:},'sort',{ field1 dir1 field2 dir2 } };

      else % command line
          field1 = tmparg{1};
          if length(tmparg) < 2, dir1 = 0;
          else                   dir1 = tmparg{2};
          end
          if length(tmparg) < 3, field2 = [];
          else                   field2 = tmparg{3};
          end
          if length(tmparg) < 4, dir2 = 0;
          else                   dir2 = tmparg{4};
          end
      end

      % Toby edit 11/16/2005 This section is scrambling the eeg.event
      % fields. Requires further investigation.

      if ~isempty(field2)
          tmpevent = EEG.event;
          if ~ischar(EEG.event(1).(field2))
              tmparray = [ tmpevent.(field2) ];
          else tmparray = { tmpevent.(field2) };
          end
          % Commented out 11/18/2005, Toby
          % These lines were incorrectly sorting the event.latency field in
          % units of time (seconds) relevant to each event's relative
          % latency time as measured from the start of each epoch. It is
          % possible that there are occasions when it is desirable to do
          % so, but in the case of pop_mergset() it is not.

          %if strcmp(field2, 'latency') & EEG.trials > 1
          %    tmparray = eeg_point2lat(tmparray, {EEG.event.epoch}, EEG.srate, [EEG.xmin EEG.xmax], 1);
          %end
          [~, I] = mysort( tmparray );
          if dir2 == 1, I = I(end:-1:1); end
          events = EEG.event(I);
      else
          events = EEG.event;
      end
      tmpevent = EEG.event;
      if ~ischar(EEG.event(1).(field1))
           tmparray = [ tmpevent.(field1) ];
      else tmparray = { tmpevent.(field1) };
      end
      % Commented out 11/18/2005, Toby
      %if strcmp( field1, 'latency') & EEG.trials > 1
      %    tmparray = eeg_point2lat(tmparray, {events.epoch}, EEG.srate, [EEG.xmin EEG.xmax], 1);
      %end
      [~, I] = mysort( tmparray );
      if dir1 == 1, I = I(end:-1:1); end
      EEG.event = events(I);

      if gui
          % warn user
          % ---------
          warndlg2('Sorting done');
      else
          noeventcheck  = 1; % otherwise infinite recursion with eeg_checkset
      end

        end % end switch
    end % end loop

    % save userdata
    % -------------
    if gui
        userdata{1} = EEG;
        userdata{2} = oldcom;
        set(gcf, 'userdata', userdata);
        pop_editeventvals('goto', shift);
    else
        if ~exist('noeventcheck','var')
            EEG = eeg_checkset(EEG, 'eventconsistency');
            EEG = eeg_checkset(EEG, 'checkur');
        end
        if nargout > 1
            com = sprintf('EEG = pop_editeventvals(EEG, %s);', vararg2str(varargin));
        end
    end
    return;
end

% ----------------------
% graphic interface part
% ----------------------

if isempty(EEG.event)
    disp('Getevent: cannot deal with empty event structure');
    return;
end

allfields = fieldnames(EEG.event);
tmpind = strmatch('urevent', allfields);
allfields(tmpind) = [];

if nargin<2
    % add field values
    % ----------------
    geometry = { [2 0.5] };
    tmpstr = sprintf('Edit event field values (currently %d events)',length(EEG.event));
    uilist = { { 'Style', 'text', 'string', tmpstr, 'fontweight', 'bold'  } ...
               { 'Style', 'pushbutton', 'string', 'Delete event', 'callback', 'pop_editeventvals(''delete'');'  }};

    for index = 1:length(allfields)

        geometry = { geometry{:} [1 1 1 1] };

        % input string
        % ------------
        if strcmp( allfields{index}, 'latency') || strcmp( allfields{index}, 'duration')
            if EEG.trials > 1
                 inputstr =  [ allfields{index} ' (ms)'];
            else inputstr =  [ allfields{index} ' (sec)'];
            end
		else inputstr =  allfields{index};
		end

		% callback for displaying help
		% ----------------------------
        if index <= length( EEG.eventdescription )
             tmptext = EEG.eventdescription{ index };
			 if ~isempty(tmptext)
				 if size(tmptext,1) > 15,    stringtext = [ tmptext(1,1:15) '...' ];
				 else                        stringtext = tmptext(1,:);
				 end
			 else stringtext = 'no-description'; tmptext = 'no-description';
			 end
        else stringtext = 'no-description'; tmptext = 'no-description';
        end
		cbbutton = ['questdlg2(' vararg2str(tmptext) ...
					',''Description of field ''''' allfields{index} ''''''', ''OK'', ''OK'');' ];

        % create control
        % --------------
        cbedit = [ 'pop_editeventvals(''assign'', ''' allfields{index} ''');' ];
		uilist   = { uilist{:}, { }, ...
					 { 'Style', 'pushbutton', 'string', inputstr, 'callback',cbbutton  }, ...
					 { 'Style', 'edit', 'tag', allfields{index}, 'string', '', 'callback', cbedit } ...
                     { } };
    end

    % add buttons
    % -----------
    geometry = { geometry{:} [1] [1.2 0.6 0.6 1 0.6 0.6 1.2] [1.2 0.6 0.6 1 0.6 0.6 1.2] [2 1 2] };

    tpappend = 'Append event after the current event';
    tpinsert = 'Insert event before the current event';
    tporigin = 'Original index of the event (in EEG.urevent table)';
    uilist   = { uilist{:}, ...
          { }, ...
          { },{ },{ }, {'Style', 'text', 'string', 'Event Num', 'fontweight', 'bold' }, { },{ },{ }, ...
          { 'Style', 'pushbutton', 'string', 'Insert event',  'callback', 'pop_editeventvals(''append'', 0);', 'tooltipstring', tpinsert }, ...
          { 'Style', 'pushbutton', 'string', '<<',            'callback', 'pop_editeventvals(''goto'', -10);' }, ...
          { 'Style', 'pushbutton', 'string', '<',             'callback', 'pop_editeventvals(''goto'', -1);' }, ...
          { 'Style', 'edit',       'string', '1',             'callback', 'pop_editeventvals(''goto'', 0);', 'tag', 'numval' }, ...
          { 'Style', 'pushbutton', 'string', '>',             'callback', 'pop_editeventvals(''goto'', 1);' }, ...
          { 'Style', 'pushbutton', 'string', '>>',            'callback', 'pop_editeventvals(''goto'', 10);' }, ...
          { 'Style', 'pushbutton', 'string', 'Append event',  'callback', 'pop_editeventvals(''append'', 1);', 'tooltipstring', tpappend }, ...
          { }, { 'Style', 'text',  'string', ' ', 'tag', 'original' 'horizontalalignment' 'center' 'tooltipstring' tporigin } { } };

    % add sorting options
    % -------------------
    listboxtext = 'No field selected';
    for index = 1:length(allfields)
         listboxtext = [ listboxtext '|' allfields{index} ];
    end

    userdata = { EEG {} };
    inputgui( geometry, uilist, 'pophelp(''pop_editeventvals'');', ...
                                  'Edit event values -- pop_editeventvals()', userdata, 'plot');
    pop_editeventvals('goto', 0);

    % wait for figure
    % ---------------
    fig = gcf;
    waitfor( findobj('parent', fig, 'tag', 'ok'), 'userdata');
    try, userdata = get(fig, 'userdata'); close(fig); % figure still exist ?
    catch, return; end

    % transfer events
    % ---------------
    if ~isempty(userdata{2})
        com = sprintf('EEG = pop_editeventvals(EEG,%s);', vararg2str(userdata{2}));
    end
    if isempty(findstr('''sort''', com))
        if ~isempty(userdata{2}) % some modification have been done
            EEG       = userdata{1};
            disp('Checking event consistency...');
            EEG = eeg_checkset(EEG, 'eventconsistency');
            EEG = eeg_checkset(EEG, 'checkur');
        end
    else
        com = '';
        disp('WARNING: all edits discarded because of event resorting. The EEGLAB event structure');
        disp('            must contain events sorted by latency (you may obtain an event structure');
        disp('            with resorted event by calling this function from the command line).');
    end
    return;

end
return;

% format the output field
% -----------------------
function strval = reformat( val, latencycondition, trialcondition, eventnum)
    if latencycondition
        if trialcondition
            strval = ['eeg_lat2point(' num2str(val) ', EEG.event(' int2str(eventnum) ').epoch, EEG.srate,[EEG.xmin EEG.xmax]*1000, 1E-3);' ];
        else
            strval = [ '(' num2str(val) '-EEG.xmin)*EEG.srate+1;' ];
        end
    else
        if ischar(val), strval = [ '''' val '''' ];
        else            strval = num2str(val);
        end
    end

% sort also empty values
% ----------------------
function [X, I] = mysort(tmparray)
    if iscell(tmparray)
        if all(cellfun('isreal', tmparray))
            tmpempty = cellfun('isempty', tmparray);
            tmparray(tmpempty) = { 0 };
            tmparray = [ tmparray{:} ];
        end
    end
    try
        [X, I] = sort(tmparray);
    catch
        disp('Sorting failed. Check that selected fields contain uniform value format.');
        X = tmparray;
        I = 1:length(X);
    end

% checkconsistency of new event
% -----------------------------
function eventtmp = checkconsistency(eventtmp, valnum, field)

    otherval = mod(valnum+1, length(eventtmp))+1;

    if ischar(getfield(eventtmp(valnum), field)) && ~ischar(getfield(eventtmp(otherval), field))
        eventtmp(valnum) = setfield(eventtmp(valnum), field, str2double(getfield(eventtmp(valnum), field)));
    end
    if ~ischar(getfield(eventtmp(valnum), field)) && ischar(getfield(eventtmp(otherval), field))
        eventtmp(valnum) = setfield(eventtmp(valnum), field, num2str(getfield(eventtmp(valnum), field)));
    end
    if strcmpi(field, 'latency') && isempty(getfield(eventtmp(valnum), field))
        eventtmp(valnum).latency = NaN;
    end


function test_opt_test ( )

%*****************************************************************************80
%
%% TEST_OPT_TEST tests the TEST_OPT library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 October 2011
%
%  Author:
%
%    John Burkardt
%
  timestamp (  )
  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST_OPT_TEST\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the TEST_OPT library.\n' );

  test_opt_test01 ( );
  test_opt_test02 ( );
  test_opt_test03 ( );
  test_opt_test04 ( );
  test_opt_test05 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'TEST_OPT_TEST\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp (  )

  return
end


function varargout=spm_help(varargin)
% SPM help and manual facilities
% FORMAT spm_help
%_______________________________________________________________________
%  ___  ____  __  __
% / __)(  _ \(  \/  )
% \__ \ )___/ )    (   Statistical Parametric Mapping
% (___/(__)  (_/\/\_)  SPM - http://www.fil.ion.ucl.ac.uk/spm/
%
%_______________________________________________________________________
%
% SEE ALSO:   spm.man      - "About SPM"
%
% The "Help" facilities are about software and implementation. The
% underlying mathematics, concepts and operational equations have been
% (or will be) published in the peer reviewed literature and the
% interested user is referred to these sources. An intermediate
% theoretical exposition is given in the SPM course notes. This and
% other resources are available via the SPM Web site. Visit
% http://www.fil.ion.ucl.ac.uk/spm/, or press the "SPMweb" button on the
% spm_help toolbar.
%
%-----------------------------------------------------------------------
%
% spm_help sets up a GUI help system for the SPM package.
% `spm_help('Topic')` or `spm_help Topic` displays the help for a
% particular topic in the SPM help window.
%
% Help topics are displayed in a special help window. Initially, a
% representation of the SPM Menu window is drawn. Clicking buttons in
% this representation leads to the help pages for the appropriate
% topic.
%
% The SPM Help ToolBar contains controls for the help system.
%
%  ---  Routines and manual pages (generically referred to as help
%       "topics") referenced by the currently displayed help file are
%       listed in the "Referenced Topics..." pull-down menu on the help
%       interface. Selecting a routine there displays it's help
%       information.
%
%  ---  Alternatively, a specific function name can be entered into
%       the lilac editable text widget in the help window toolbar.
%       Its help will be displayed.
%
%  ---  As the tree of routines is explored, the "Previous Topics"
%       pulldown menu keeps track of recently visited help topics,
%       allowing easy backtracking and exploration of the "tree" of SPM
%       functions and dependencies.
%
% Special topic buttons lead to "About SPM", "Menu", and "Help" topics.
% "About SPM" displays the introduction file for this version of SPM.
% "Menu" displays the help representation of the SPM Menu window.
% "Help" displays this file.
%
% Multi-page help files are displayed by the help facility with pagination
% controls at the bottom right of the Graphics window.
%
% The print button enables printing to the specified Print device. This
% is set in the Defaults area, initially to a PostScript file
% "spm*.ps" in the current directory. Multi page topics are paged on
% screen, and printed page by page.
%
%
%-----------------------------------------------------------------------
% The SPM package provides help at three levels, the first two being
% available via the SPM graphical help system:
%
% (i)   Manual pages on specific topics.
%       These give an overview of specific components or topics its
%       relation to other components, the inputs and outputs and
%       references to further information.
%
%       Many of the buttons in the help menu window lead to such "man"
%       pages.  These are contained in ASCII files named spm_*.man.
%       These can be viewed on the MATLAB command line with the `help`
%       command, e.g. `help spm_help` prints out this manual file in
%       the MATLAB command window.
%
% (ii)  Help information for each routine within SPM (E.g. This is the).
%       help information for spm_help.m - the help function.)
%       This help information is the help header of the actual MATLAB
%       function, and can be displayed on the command line with the
%       `help` command, e.g. `help spm_help`.
%
%       Commented header text from that spm_*.m file is displayed in the
%       following format:
%
%        A one line description
%        FORMAT [outputs] = spm_routine(inputs);
%        inputs  -  the input arguments
%        outputs -  the output arguments
%        A short paragraph detailing what the routine does and other
%        pertinent information
%        ref:  citations
%
% (iii) SPM is (mainly) implemented as MATLAB functions and scripts.
%       These are ASCII files named spm_*.m, which can be viewed in the
%       MATLAB command window with the `type` command, e.g. `type
%       spm_help`, or read in a text editor.
%
%  ---  MATLAB syntax is very similar to standard matrix notation that
%       would be found in much of the literature on matrices. In this
%       sense the SPM routines can be used (with MATLAB) for data
%       analysis, or they can be regarded as the ultimate pseudocode
%       specification of the underlying ideas.
%
%  ---  The coding is concise but clear, and annotated with comments
%       where necessary.
%
% In addition, the MATLAB help system provides keyword searching
% through the H1 lines (the first comment line) of the help entries of
% *all* M-files found on MATLABPATH. This can be used to identify
% routines from keywords. Type `help lookfor` in the MATLAB command
% window for further details.
%
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Andrew Holmes, Karl Friston
% $Id$

%=======================================================================
% - FORMAT specifications for embedded CallBack functions
%=======================================================================
%( This is a multi function function, the first argument is an action  )
%( string, specifying the particular action function to take. Recall   )
%( MATLAB's command-function duality: `spm_help MenuWin` is equivalent )
%( to `spm_help('MenuWin')`.                                           )
%
% FORMAT spm_help
% Makes Help window visible if there is one around. Otherwise
% defaults to spm_help('!Topic','MenuWin').
%
% FORMAT spm_help('Topic')
% Defaults to spm_help('!Topic','Topic').
%
% FORMAT spm_help('!Quit')
% Hides the Help window.
% (Re)Shows the welcome splash screen if it exists.
%
% FORMAT spm_help('!DrawMenuWin')
% Draws representation of Menu window in help window, with button
% CallBacks for the appropriate help topics.
%
% FORMAT [S,Err] = spm_help('!ShortTopics',Topic)
% Returns help text (as a string) for special topics. If the topic is
% not found then S contains an error topic, and Err is true.
% Topic     - Special internal help topic
% S         - String vector containing help topic
% Err       - True if the topic is not found
%
% FORMAT spm_help('!Topic',Topic)
% Topic     - Help topic: Either name of file from which to display help,
%             or name of an internal help topic
%             Defaults to the "MenuWin" topic. (See '!ShortTopics')
% Loads file Topic (which must be on the MATLABPATH), parses it for
% references to other spm_* routines, puts these in the Help ToolBars
% "Referenced routines" pulldown (with callbacks to display their
% help), and sets up the "Previous topics" pulldown. Puts Topic in the
% editable topic window on the ToolBar. Then calls spm_help('!Disp') to
% display the help portion of the file.
% Special internal topics are tried if Topic file doesn't exist.
% "MenuWin" Topic prints a short message and displays the help
% representation of the Menu window.
%
% FORMAT spm_help('!Disp',Fname,S,F)
% Fname     - Name of file from which to display help
% S         - [Optional] String vector containing a previously read in
%             contents of file Fname
% F         - Figure to use
% Displays the help for the given file in the Help window (creating
% one if required). Paginates and provides paging buttons if necessary.
%
% FORMAT F = spm_help('!Create')
% F        - Handle of figure created
% Creates central Help window 'Tag'ged 'Help', with Help ToolBar
%
% FORMAT F = spm_help('!CreateHelpWin')
% F        - Handle of figure created
% Creates central Help window 'Tag'ged 'Help'. If one already exists
% then it's handle is returned.
%
% FORMAT spm_help('!CreateBar',F)
% F        - Handle (or 'Tag') of window to use. Defaults to 'Help'.
% Creates Help ToolBar.
%
% spm_help('!Clear',F)
% F        - Handle (or 'Tag') of window to use. Defaults to 'Help'.
% Clears help window, leaving ToolBar intact. Any 'HelpMenu' objects
% (with 'UserData' 'HelpMenu') are merely hidden, to speed up
% subsequent use.
%
% FORMAT h = spm_help('!ContextHelp',Topic)
% Sets up a small green "?" help symbol in the bottom right corner of
% the SPM Interactive window with callback to spm_help('Topic'). If SPM
% is in CommandLine mode then an appropriate message is printed in the
% MATLAB command window.
% Topic     - Help topic [defaults to name of calling function]
% h         - handle of "?" text (or 0 if CmdLine)
%_______________________________________________________________________

%-Condition arguments
%-----------------------------------------------------------------------
%-All actions begin '!' - Other (string) actions are topics
if nargin==0 || isempty(varargin{1})
    Fhelp = spm_figure('FindWin','Help');
    if ~isempty(Fhelp), set(Fhelp,'Visible','on')
    else spm_help('!Topic','MenuWin'), end
    return
elseif varargin{1}(1)~='!'
    spm_help('!Topic',varargin{:}), return
end

switch lower(varargin{1}), case '!quit'
%=======================================================================
Fhelp   = spm_figure('FindWin','Help');
set(Fhelp,'Visible','off')

case '!drawmenuwin'
%=======================================================================
% Fhelp = spm_help('!DrawMenuWin');

%-Find help window
%-----------------------------------------------------------------------
Fhelp = spm_figure('FindWin','Help');
if isempty(Fhelp), error('No Help window'), end

%-If there's already a HelpMenu then show it and return
H = findobj(Fhelp,'Tag','HelpMenu');
if ~isempty(H), set(H,'Visible','on'), return, end

%-Create window frame
%-----------------------------------------------------------------------
S      = get(Fhelp,'Position');       %-Help window position
WS     = [S(3)/600 S(4)/865 S(3)/600 S(4)/865];  %-Window scaling factors
%FS    = spm('FontSizes');            %-Scaled font sizes
PF     = spm_platform('fonts');       %-Font names (for this platform)
SPMver = spm('Ver');                  %-SPM version
O      = [170 050 000 000].*WS;       %-Offset for MenuWin plot

%-Button to emulate window title bar, frames to emulate window
%-----------------------------------------------------------------------
uicontrol(Fhelp,'Style','PushButton','String',['About ',SPMver],...
    'ForegroundColor',[0 0 0.9],...
    'ToolTipString','about this release of SPM [spm.man]',...
    'CallBack','spm_help(''spm.man'')',...
    'Position',[-4,447,406,30].*WS+O,...
    'Tag','HelpMenu')

uicontrol(Fhelp,'Style','Frame',...
    'Position',[-2,-3,404,450].*WS+O,...
    'Tag','HelpMenu')
uicontrol(Fhelp,'Style','Frame',...
    'Position',[0,0,400,445].*WS+O,...
    'Tag','HelpMenu',...
    'BackgroundColor',[1,1,1]*.8)

%-Frames & text
%-----------------------------------------------------------------------
Fmenu  = spm_figure('Findwin','Menu');
C      = get(Fmenu,'Children');
modality = spm('CheckModality');
for i = length(C):-1:1
    units = get(C(i),'Units');
    funits = get(C(i),'FontUnits');
    set(C(i),'Units','pixels');
    set(C(i),'FontUnits','points');
    c = get(C(i));
    set(C(i),'Units',units);
    set(C(i),'FontUnits',funits);

    % modality-specific help
    if isempty(c.Tag) || strcmp(c.Tag, 'frame') || ~isempty(strfind(c.Tag, modality))
        if strcmp(c.Type,'uicontrol')

            if ischar(c.UserData)
                Callback = sprintf('spm_help(''%s'')',c.UserData);
            else
                Callback = '';
            end

            uicontrol(Fhelp,...
                'BackgroundColor',c.BackgroundColor,...
                'Callback',  Callback,...
                'Enable',c.Enable,...
                'FontAngle',c.FontAngle,...
                'FontName',c.FontName,...
                'FontSize',c.FontSize,...
                'ForegroundColor',c.ForegroundColor,...
                'HorizontalAlignment',c.HorizontalAlignment,...
                'Units',c.Units,...
                'Position',c.Position + O,...
                'String',c.String,...
                'Style',c.Style,...
                'Tag', 'HelpMenu',...
                'TooltipString',c.TooltipString,...
                'Visible','on',...
                'Interruptible','on');
        end
    end
end

%-Special topics
%-----------------------------------------------------------------------
uicontrol(Fhelp,'Style','Frame','Position',[-145-2,-3,132+4,474+5].*WS+O,...
    'Tag','HelpMenu')
uicontrol(Fhelp,'Style','Frame','Position',[-145,0,132,474].*WS+O,...
    'Tag','HelpMenu',...
    'BackgroundColor',[1,1,1]*.8)
uicontrol(Fhelp,'Style','Text','String','special topics...',...
    'ForegroundColor','w','BackGroundColor',[1,1,1]*.8,...
    'FontName',PF.times,'FontAngle','Italic','FontWeight','Bold',...
    'HorizontalAlignment','Center',...
    'Position',[-133-5 447-10 110+10 30].*WS+O,...
    'Tag','HelpMenu')
uicontrol(Fhelp,'String','Data format','ForegroundColor','b',...
    'ToolTipString',...
    'about the image format used by SPM [spm_format.man]',...
    'Position',[-133 410 110 30].*WS+O,...
    'CallBack','spm_help(''spm_format.man'')',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','Templates','ForegroundColor','b',...
    'ToolTipString',...
    'templates and other data [spm_templates.man]',...
    'Position',[-133 370 110 30].*WS+O,...
    'CallBack','spm_help(''spm_templates.man'')',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 330 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 290 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 250 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 210 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 170 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 130 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','Random FX','ForegroundColor','b',...
    'ToolTipString','about implementing random effects analyses in SPM [spm_RandFX.man]',...
    'Position',[-133 090 110 30].*WS+O,...
    'CallBack','spm_help(''spm_RandFX.man'')',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','','ForegroundColor','b',...
    'ToolTipString','',...
    'Position',[-133 050 110 30].*WS+O,...
    'CallBack','',...
    'Enable','off',...
    'Tag','HelpMenu','Interruptible','on')
uicontrol(Fhelp,'String','SPM motd','ForegroundColor','b',...
    'ToolTipString','SPM startup "message of the day"',...
    'Position',[-133 010 110 30].*WS+O,...
    'CallBack','spm_help(''spm_motd.man'')',...
    'Tag','HelpMenu','Interruptible','on')

uicontrol(Fhelp,'String','Copyright (c) Wellcome Trust Centre for NeuroImaging',...
    'ForegroundColor',[1 1 1]*.6,'BackgroundColor','w',...
    'ToolTipString','Copyleft information',...
    'FontName',PF.times,'FontSize',8,...
    'Position',[-145 -030 549 015].*WS+O,...
    'CallBack','spm_help(''spm_LICENCE.man'')',...
    'Tag','HelpMenu','Interruptible','on')

case '!shorttopics'
%=======================================================================
% [S,Err] = spm_help('!ShortTopics',Topic)
if nargin<2, Topic='!Topics'; else Topic=varargin{2}; end

Usep = sprintf('%%%s',repmat('_',1,71));
Err = 0;

if strcmp(Topic,'!Topics')
    S = 'MenuWin';
elseif strcmp(Topic,'spm_motd.man')
    S = sprintf([...
        '\n%% %s : Message of the Day\n',...
        '%s\n%%\n',...
        '%% No message of the day has been set.\n',...
        '%%\n',...
        '%% File spm_motd.man can contain the message, which is',...
            ' displayed at startup\n',...
        '%% if it exists.\n',...
        '%s\n%%\n%% The FIL methods group\n'],...
        spm('Ver'),Usep,Usep);
elseif strcmp(Topic,'MenuWin')
    S = sprintf([...
        '\n%% %s : Statistical Parametric Mapping\n',...
        '%s\n%%\n',...
        '%% SPM functions are called from the main SPM Menu ',...
            'window.\n',...
        '%% Click the buttons in the representation below ',...
            'for help on that topic...\n',...
        '%%\n',...
        '%% Click on "Help" for information on SPM documentation, ',...
            'and instructions\n',...
        '%% on using the SPM help system.\n',...
        '%s\n%%\n%% The FIL methods group\n'],...
        spm('Ver'),Usep,Usep);
elseif strcmp(Topic,'SPM_Modality')
    S = sprintf([...
        '\n%% SPM Modality switch\n',...
        '%s\n%%\n',...
        '%% The modality switch on the SPM Menu interface ',...
            'allows switching\n',...
        '%% between PET/SPECT and fMRI modalities on the fly,',...
            'without re-starting SPM\n',...
        '%%\n',...
        '%% Operating the switch sets the MODALITY global ',...
            'variable,\n',...
        '%% sets the package defaults to those specified for ',...
            'the current modality\n',...
        '%% and changes the interface so that the appropriate ',...
            'routines\n',...
        '%% lie behind the buttons.\n',...
        '%%\n',...
        '%% See also: spm.m spm_defaults.m\n',...
        '%s\n%%\n%% Andrew Holmes\n'],Usep,Usep);
elseif strcmp(Topic,'MenuWin_Toolboxes_PullDown')
    S = sprintf([...
        '\n%% SPM Menu window: Toolboxes button\n',...
        '%s\n%%\n',...
        '%% The Toolboxes pulldown provides access to SPM ',...
            'toolboxes installed in\n',...
        '%% the toolbox subdirectory of the SPM installation ',...
            'directory.\n',...
        '%%\n',...
        '%% SPM toolboxes are defined as the contents of sub-',...
            'directories of\n',...
        '%% fullfile(spm(''Dir''),''toolbox'') - the ''toolbox'' ',...
            'subdirectory of the SPM\n',...
        '%% directory. For SPM to pick a toolbox up, there must be ',...
            'a single mfile\n',...
        '%% in the directory whose name ends with the toolbox ',...
            'directory name. (I.e.\n',...
        '%% A toolbox called "test" would be in the "test" ',...
            'subdirectory of\n',...
        '%% spm(''Dir''), with a single file named *test.m.) This ',...
            'M-file is\n',...
        '%% regarded as the launch file for the toolbox.\n',...
        '%%\n',...
        '%% On selecting the toolbox from the pulldown menu, the ',...
            'toolbox directory\n',...
        '%% is prepended to the MATLAB path (unless it is already ',...
            'on the path), and\n',...
        '%% the toolbox launch program executed (with no ',...
            'arguments).\n',...
        '%%\n',...
        '%% For help on specific toolboxes, refer to the toolbox ',...
            'itself.\n',...
        '%%\n',...
        '%% The toolbox directory for this installation of SPM is:\n',...
        '%%     %s\n',...
        '%%\n',...
        '%% SPM toolboxes are only identified at SPM startup.\n',...
        '%%\n',...
        '%% If there are no toolboxes installed, then the button ',...
            'is disabled.\n',...
        '%%\n',...
        '%% See spm.m for programmers help with toolbox handling.\n',...
        '%s\n%%\n%% Andrew Holmes\n'],...
        Usep,fullfile(spm('Dir'),'toolbox'),Usep);
elseif strcmp(Topic,'MenuWin_Utils_PullDown')
    S = sprintf([...
        '\n%% SPM Menu window: PullDown menu of utilities...\n',...
        '%s\n%%\n',...
        '%% 1) CD:        Calls spm_get prompting for a directory,\n',...
        '%%               and changes directory to it.\n',...
        '%% 2) PWD:       Reports the present working directory.\n',...
        '%%               \n',...
        '%% 3) delete:    GUI file deletion utility:\n',...
        '%%               Prompts for files to delete using',...
            ' spm_get.\n',...
        '%%               Seeks confirmation before deleting\n.',...
        '%%               List of deleted files reported in MATLAB',...
            ' command window.\n',...
        '%% 4) Show SPM:  Pops up all the figures for this SPM',...
            ' session.\n',...
        '%%               Typing "spm show" in the MATLAB command',...
            ' window has the\n',...
        '%%               same effect.\n',...
        '%% 5) Run mFile: Prompts for selection of a MATLAB M-file',...
            ' (script or function with\n',...
        '%%               no arguments), which is then run in the',...
            ' base workspace.\n',...
        '%% 6) SPMweb:    Opens http://www.fil.ion.ucl.ac.uk/spm/',...
            ' in your web browser.\n',...
        '%%               See MATLAB `web` command for details.\n',...
        '%s\n%%\n%% Andrew Holmes\n'],Usep,Usep);
elseif strcmp(Topic,'QuitSPM')
    S = sprintf([...
        '\n%% Quit SPM and cleanup the MATLAB workspace\n',...
        '%s\n%%\n',...
        '%% Closes all windows, clears workspace variables,',...
            ' global variables\n',...
        '%% compiled functions and MEX links. This leaves',...
            ' MATLAB in "startup" condition\n',...
        '%% except that it doesn''t release the memory claimed',...
            ' during the session.\n',...
        '%% You need to `quit` MATLAB to release it''s memory.',...
            ' It is advisable to do.\n',...
        '%% reasonably frequently, especially after large',...
            ' analyses.\n',...
        '%%\n',...
        '%% It is *not* necessary to quit SPM after an SPM error.',...
            ' Simply `clear` the \n',...
        '%% workspace (type `clear` in the MATLAB command window)',...
            ' and clear the\n',...
        '%% "Results" and "Interactive" with the `clear` button',...
            ' in the "Results" window.\n',...
        '%% (SPMs defaults are held as global variables.)\n',...
        '%%\n',...
        '%% The `Close` button in the "Help" window merely hides',...
            ' the SPMhelp window.\n',...
        '%%\n',...
        '%s\n%%\n%% The FIL methods group\n'],Usep,Usep);
else
    doc = spm_jobman('help',Topic);
    if ~isempty(doc),
        doc = char(doc{:});
        doc = [repmat('% ',size(doc,1),1) doc repmat(sprintf('\n'),size(doc,1),1)]';
        S   = doc(:)';
    else
        S = sprintf([...
            '\n%% ! - Topic not found\n',...
            '%s\n%%\n',...
            '%% This topic is not recognised by the help system.\n',...
            '%s\n%%\n'],Usep,Usep);
        Err = 1;
    end
end
varargout = {S,Err};

case '!topic'
%=======================================================================
% spm_help('!Topic',Topic)
if nargin<2, Topic='MenuWin'; else Topic=varargin{2}; end

%-Find (or create) help window.
%-----------------------------------------------------------------------
Fhelp = spm_figure('FindWin','Help');
if isempty(Fhelp)
    Fhelp = spm_help('!Create');
else
    set(Fhelp,'Visible','on')
end
set(Fhelp,'Pointer','Watch')
HD = get(Fhelp,'UserData');

%-Sort out integer Topic parameters from uicontrol CallBacks
%-----------------------------------------------------------------------
%-PrevTopics passes Topic as PullDown value, Back button passes -1
if ~ischar(Topic)
    PrevTopics = get(HD.hPrevTopics,'String');
    if Topic<0
        %-Go back to last topic - find PrevTopics pulldown
        if isempty(get(HD.hTopic,'String'))
            %-Topic string is empty - no topic displayed
            % go to last displayed topic (if there was one)
            Topic = min(size(PrevTopics,1),2);
        else
            %-Current topic is second, previous is third
            Topic = min(size(PrevTopics,1),3);
        end
    end
    if Topic==1, set(Fhelp,'Pointer','Arrow'), return, end
    if Topic>2, PrevTopics(2:Topic-1,:)=[]; end
    Topic = deblank(PrevTopics(2,:));
    bDonePrevTopics = 1;
else
    bDonePrevTopics = 0;
end
bMenuWin = strcmp(Topic,'MenuWin');

%-Load text file or get text from 'ShortTopics'
%-----------------------------------------------------------------------
Fname = which(Topic);           %-Try to find file on MATLABPATH
if isempty(Fname) || ...        %-No such file
    exist(Fname,'file')==3      %-Watch out for MEX files!
    Fname = which([Topic,'.m']);    %-if MEX, try *.m file
end
fid = fopen(Fname,'rt');            %-Try to open file

if fid<0                %-No file - try 'ShortTopics'
    [S,Err] = spm_help('!ShortTopics',Topic);
else                    %-Read help text from file
    S = fread(fid,'*char')';
    Err = 0;
    fclose(fid);
end

%-Display the current help comments
%-----------------------------------------------------------------------
spm_help('!Disp',Topic,S,Fhelp);

%-Display MenuWin graphic if required
%-----------------------------------------------------------------------
if bMenuWin
    spm_help('!DrawMenuWin')
end

%-Sort out control objects
%-----------------------------------------------------------------------
set(HD.hTopic,'String',Topic);

%-If unknown topic then return
%-----------------------------------------------------------------------
if Err, set(Fhelp,'Pointer','Arrow'), return, end

%-Sort out previous topics pulldown
%-For "PrevTopics" & "Back" buttons we did this above
%-----------------------------------------------------------------------
if ~bDonePrevTopics
    PrevTopics = get(HD.hPrevTopics,'String');
    Prompt     = PrevTopics(1,:); PrevTopics(1,:)=[];
    %-Add current topic to previous topics if not same as one before
    if ~strcmp(Topic,deblank(PrevTopics(1,:)))
        PrevTopics = char(Topic,PrevTopics);
    end
    % %-Delete any replications of Topic within PrevTopics
    % if size(PrevTopics,1)>1
    %   IDRows = 1+find(all( PrevTopics(2:size(PrevTopics,1),:) ==...
    %       repmat(PrevTopics(1,:),size(PrevTopics,1)-1,1),2));
    %   PrevTopics(IDRows,:)=[];
    % end
    %-Truncate to 20 items max
    if size(PrevTopics,1)>20, PrevTopics(21:size(PrevTopics,1),:)=[]; end
    PrevTopics=char(Prompt,PrevTopics);
end
%-Update popup
set(HD.hPrevTopics,'String',PrevTopics,'Value',1)

%-Find referenced topics
%-----------------------------------------------------------------------
RefdTopics = cellstr(get(HD.hRefdTopics,'String'));
RefdTopics = {RefdTopics{1};Topic}; %-Add current topic at top of references
q     = findstr(S,'spm_');      %-Find 'spm_' strings
for i = 1:length(q)
    d = (0:31) + q(i);          %-32 is max "spm_*" length considered
    Q = S(d(d <= length(S)));       %-string (len<=32) starting "spm_"
    d = find(Q==';'|Q=='('|Q==')'|Q==10|Q=='.'|Q==','|Q==' ');
    if length(d)            %-Look for filename extensions
    if any(strncmp(Q(min(d):end),{'.man','.txt'},4))
        Q = Q(1:min(d)+3);
    elseif any(strncmp(Q(min(d):end),{'.c','.h'},2))
        Q = Q(1:min(d)+1);
    else
        Q = Q(1:min(d)-1);
    end
    if exist(Q,'file') == 2 && ~any(strcmp(RefdTopics,Q))
        RefdTopics = [RefdTopics;{Q}]; end
    else
    warning('spm_* string end not found - neglecting reference')
    end
end
RefdTopics(2)=[];           %-Delete current Topic from references
%-Update popup
set(HD.hRefdTopics,'String',RefdTopics,'Value',1)

set(Fhelp,'Pointer','Arrow')

case '!disp'
%=======================================================================
% spm_help('!Disp',Fname,S,F,TTitle)
if nargin<5, TTitle=''; else TTitle=varargin{5}; end
if nargin<4, F='Help'; else F=varargin{4}; end
if nargin<3, S=''; else S=varargin{3}; end
if nargin<2, Fname='spm.man'; else Fname=varargin{2}; end
if isempty(TTitle), TTitle=Fname; end

%-Find (or create) window to print in
Fhelp = spm_figure('FindWin',F);
if isempty(Fhelp)
    Fhelp = spm_help('!CreateHelpWin');
else
    set(Fhelp,'Visible','on');
end
FS = spm('FontSizes');
PF = spm_platform('fonts');
set(Fhelp,'Pointer','Watch')
spm_help('!Clear',Fhelp)

%-Parse text file/string
%-----------------------------------------------------------------------
if isempty(S)
    %-Load text file or get text from 'ShortTopics'
    fid = fopen(Fname,'rt');
    if fid<0
        [S,Err] = spm_help('!ShortTopics',Fname);
    else
        S = fread(fid,'*char')';
        % Err = 0;
        fclose(fid);
    end
end
q     = min([length(S),findstr(S,char([10 10]))]);  % find empty lines
q     = find(S(1:q(1)) == 10);              % find line breaks

figure(Fhelp)
hAxes = axes('Position',[0.028,0.05,0.85,0.85],...
        'DefaultTextInterpreter','none',...
        'Units','Points','Visible','off');
AxPos = get(hAxes,'Position'); set(hAxes,'YLim',[0,AxPos(4)])

dy = FS(10)*1.2; y0 = floor(AxPos(4)) -dy;

text(-0.03,y0,TTitle,'FontSize',FS(14),'FontWeight','bold');
y     = y0 - FS(14);

%-Loop over pages & lines of text
%-----------------------------------------------------------------------
FmtLine = 1;
for i = 1:(length(q) - 1)
    d = S((q(i) + 1):(q(i + 1) - 1));

    %-Display help text lines
    if d(1) == abs('%');
        %-For some reason, '|' characters cause a CR.
        d = strrep(d,'|','I');
        h = text(0,y,d(2:length(d)),...
            'FontName',PF.courier,'FontSize',FS(9));
        if FmtLine
            set(h,'FontWeight','bold',...
                'FontName',PF.times,'FontSize',FS(10));
            y = y - FS(8);
            FmtLine=0;
        end
        y = y - dy;
    end

    %-Paginate if necessary
    if y<0
        text(0.5,-10,['Page ',num2str(length(hAxes))],...
            'FontSize',FS(8),'FontAngle','Italic')
        spm_figure('NewPage',[hAxes(end);get(hAxes(end),'Children')])
        hAxes = [hAxes,axes('Units','Points','Position',AxPos,...
                'DefaultTextInterpreter','none',...
                'YLim',[0,AxPos(4)],'Visible','off')];
        y     = y0;
    end
end

%-Register last page if paginated
if length(hAxes)>1
    text(0.5,-10,sprintf('Page %d/%d',length(hAxes)*[1,1]),...
        'FontSize',FS(8),'FontAngle','italic')
    spm_figure('NewPage',[hAxes(end);get(hAxes(end),'Children')])
end

set(Fhelp,'Pointer','Arrow')

case '!create'
%=======================================================================
% F = spm_help('!Create')
%-Condition arguments

F = spm_help('!CreateHelpWin');
spm_help('!CreateBar',F)
spm_figure('WaterMark',F,spm('Ver'),'WaterMark',-45,1)
varargout = {F};

case '!createhelpwin'
%=======================================================================
% F = spm_help('!CreateHelpWin')

F = spm_figure('FindWin','Help');
if any(F), return, end

%-Draw window
%-----------------------------------------------------------------------
WS     = spm('WinScale');           %-Window scaling factors
FS     = spm('FontSizes');          %-Scaled font sizes
PF     = spm_platform('fonts');     %-Font names (for this platform)
S0     = spm('WinSize','0',1);      %-Screen size (of the current monitor)

F      = figure('IntegerHandle','off',...
    'Position',[S0(1)+S0(3)/2,S0(2),0,0] + [-300 008 600 865].*WS,...
    'Resize','off',...
    'Name',sprintf('%s%s: SPMhelp',spm('ver'),spm('GetUser',' (%s)')),...
    'NumberTitle','off',...
    'Tag','Help',...
    'MenuBar','none',...
    'Units','Pixels',...
    'DefaultTextInterpreter','none',...
    'DefaultTextFontName',PF.helvetica,...
    'DefaultTextFontSize',FS(12),...
    'DefaultUicontrolFontName',PF.helvetica,...
    'DefaultUicontrolFontSize',FS(12),...
    'DefaultUicontrolInterruptible','on',...
    'Color','w',...
    'ColorMap',gray(64),...
    'DefaultTextColor','k',...
    'DefaultAxesColor','w',...
    'DefaultAxesXColor','k',...
    'DefaultAxesYColor','k',...
    'DefaultAxesZColor','k',...
    'DefaultAxesFontName',PF.helvetica,...
    'DefaultPatchFaceColor','k',...
    'DefaultPatchEdgeColor','k',...
    'DefaultSurfaceEdgeColor','k',...
    'DefaultLineColor','k',...
    'PaperType','A4',...
    'PaperUnits','normalized',...
    'PaperPosition',[.0726 .0644 .854 .870],...
    'InvertHardcopy','off',...
    'Visible','on');
varargout = {F};

case '!createbar'
%=======================================================================
% spm_help('!CreateBar',F)
%-----------------------------------------------------------------------
% Print | Clear | Current Topic     | Back | Menu | Help | SPMweb
% About SPMver  | Referenced Topics |   Previous Topics  | Close
%-----------------------------------------------------------------------

% if nargin<2, F='Help'; else F=varargin{2}; end
F = spm_figure('FindWin','Help');
if isempty(F)
    error('Help figure not found')
elseif ~strcmp(get(F,'Tag'),'Help')
    error('Specified figure is not an SPM Help window')
end

%-Get position and size parameters
%-----------------------------------------------------------------------
cF    = get(0,'CurrentFigure');
set(0,'CurrentFigure',F)
set(F,'Units','Pixels');
P     = get(F,'Position'); P = P(3:4);      % Figure dimensions {pixels}
S_Gra = P./[600,865];               % x & y scaling coefs

nBut  = 11;
nGap  = 4;
sx    = floor(P(1)./(nBut+(nGap+2)/6));     % uicontrol object width
dx    = floor(2*sx/6);              % inter-uicontrol gap
sy    = floor(20*S_Gra(1));         % uicontrol object height
x0    = dx/2;                   % initial x position
x     = dx/2;                   % uicontrol x position
y     = P(2) - sy;              % uicontrol y position
y2    = P(2) - 2.25*sy;             % uicontrol y position
FS    = round(10*min(S_Gra));            % uicontrol font size

%-Delete any existing HelpBar objects
%-----------------------------------------------------------------------
spm_figure('Clear',F,'HelpBar')

%-Create Frame for controls
%-----------------------------------------------------------------------
uicontrol(F,'Style', 'Frame',...
    'Position',[-4 (P(2) - 2.50*sy) P(1)+8 2.50*sy+4],...
    'Tag','HelpBar','HandleVisibility','callback');

%-Create uicontrol objects
%-----------------------------------------------------------------------
uicontrol(F,'String','Print' ,'Position',[x y sx sy],...
    'ToolTipString','print topic',...
    'FontSize',FS,...
    'CallBack','spm_figure(''Print'',[])',...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor','b'); x = x+sx;

uicontrol(F,'String','Clear' ,'Position',[x y sx sy],...
    'ToolTipString','clear topic',...
    'FontSize',FS,...
    'CallBack','spm_help(''!Clear'',gcf)',...
    'Interruptible','off','BusyAction','cancel',...
        'Tag','HelpBar','HandleVisibility','callback',...
        'ForegroundColor','b'); x = x+sx+dx;

uicontrol(F,'String',sprintf('About %s',spm('Ver')),...
    'ToolTipString','about this release of SPM',...
    'FontSize',FS,...
    'Position',[x0 y2 2*sx sy],...
    'HorizontalAlignment','Center',...
    'CallBack','spm_help(''spm.man'')',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor',[0 0.5 0])

uicontrol(F,'Style','Text','String','Topic ',...
    'FontSize',FS,...
    'Position',[x y sx sy],...
    'HorizontalAlignment','Right',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor','k')

hRefdTopics = uicontrol(F,'Style','PopUp',...
    'ToolTipString','topics referenced by the current topic',...
    'FontSize',FS,...
    'Position',[x-1,y2-1,4*sx+1,sy],...
    'String',{'Referenced Topics...'},...
    'CallBack',...
        'spm_help(''!PullDownTopic'')',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'HorizontalAlignment','Left',...
    'ForegroundColor','k'); x = x+sx;

hTopic = uicontrol(F,'Style','Edit','String','',...
    'ToolTipString','type/edit topic name here & press RETURN',...
    'FontSize',FS,...
    'Position',[x y 3*sx sy],...
    'CallBack','spm_help(get(gco,''String''))',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor','k','BackgroundColor',[.8,.8,1],...
    'HorizontalAlignment','Center'); x = x+3*sx+dx;

uicontrol(F,'String','Back',   'Position',[x y sx sy],...
    'ToolTipString','return to last topic',...
    'FontSize',FS,...
    'CallBack','spm_help(''!Topic'',-1)',...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback')

hPrevTopics = uicontrol(F,'Style','PopUp',...
    'ToolTipString','return to a previous topic',...
    'FontSize',FS,...
    'Position',[x-1,y2-1,3*sx+2*dx+1,sy],...
    'String',char('Previous Topics...','MenuWin'),...
    'CallBack',...
    'if get(gcbo,''Value'')~=1,spm_help(get(gcbo,''Value'')),end',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'HorizontalAlignment','Left',...
    'ForegroundColor','k'); x = x+sx+dx;

uicontrol(F,'String','Menu',   'Position',[x y sx sy],...
    'ToolTipString','draw SPMmenu for exploring help',...
    'FontSize',FS,...
    'CallBack','spm_help(''MenuWin'')',...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor',[0 0.5 0]), x = x+sx+dx;

uicontrol(F,'String','Help',   'Position',[x y sx sy],...
    'ToolTipString','help on using the SPMhelp browser',...
    'FontSize',FS,...
    'CallBack','spm_help(''spm_help.m'')',...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor',[0 0.5 0]), x = x+sx+dx;

uicontrol(F,'String','SPMweb', 'Position',[x y sx+dx/2 sy],...
    'ToolTipString',...
    'launch web browser - http://www.fil.ion.ucl.ac.uk/spm/',...
    'FontSize',FS,...
    'FontWeight','Bold','FontName',spm_platform('font','courier'),...
    'CallBack',['set(gcbf,''Pointer'',''Watch''),',...
            'web(''http://www.fil.ion.ucl.ac.uk/spm'');',...
            'set(gcbf,''Pointer'',''Arrow'')'],...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor','k')

uicontrol(F,'String','Close',   'Position',[x y2 sx+dx/2 sy],...
    'ToolTipString','close the SPMhelp browser',...
    'FontSize',FS,...
    'CallBack','spm_help(''!Quit'')',...
    'Interruptible','off','BusyAction','cancel',...
    'Tag','HelpBar','HandleVisibility','callback',...
    'ForegroundColor','r')

%-Store useful handles in UserData of Help figure
%-----------------------------------------------------------------------
set(F,'UserData',struct(...
    'hTopic',hTopic,...
    'hPrevTopics',hPrevTopics,...
    'hRefdTopics',hRefdTopics) )

%-Create FigureKeyPressFcn to edit Topic widget
%-----------------------------------------------------------------------
set(F,'KeyPressFcn',[...
    'spm_help(''!FigKeyPressFcn'',',...
        'getfield(get(gcbf,''UserData''),''hTopic''),',...
        'get(gcbf,''CurrentCharacter''))'] )

%-Reset CurrentFigure
set(0,'CurrentFigure',cF);

case '!pulldowntopic'
%=======================================================================
% spm_help('!PullDownTopic')
Topic = get(gcbo,'Value');
if Topic==1, return, end
Topics = get(gcbo,'String');
spm_help('!Topic',Topics{Topic})

case '!figkeypressfcn'
%=======================================================================
% spm_help('!FigKeyPressFcn',h,ch)
if nargin<2, error('Insufficient arguments'); else h=varargin{2};  end
if nargin<3, ch=get(gcf,'CurrentCharacter');  else ch=varargin{3}; end

tmp = get(h,'String');

if isempty(ch)
    %- shift / control / &c. pressed
    return
elseif abs(ch)==13
    %-Goto topic
    spm_help(tmp)
    return
elseif any(abs(ch)==32:126)
    tmp = [tmp, ch];
elseif abs(ch)==21
    %- ^U - kill
    tmp = '';
elseif any(abs(ch)==[8,127])
    %-BackSpace or Delete
    if ~isempty(tmp), tmp(length(tmp))=''; end
else
    %-Illegal character
    return
end
set(h,'String',tmp)

case '!clear'
%=======================================================================
% spm_help('!Clear',F)
%-Clear window, leaving 'ToolBar' 'Tag'ed objects, hiding 'HelpMenu'
% 'Tag'ged objects. Clear "Topic" widget.
%-Note: spm_help('!Disp') calls spm_help('!Clear'), so we might be
% working in the 'Graphics' window.

%-Sort out arguments
%-----------------------------------------------------------------------
if nargin<2, F='Help'; else F = varargin{2}; end
F=spm_figure('FindWin',F);
if isempty(F), return, end

%-If not 'Help' window, then just do a normal clear.
if ~strcmp(get(F,'Tag'),'Help'), spm_figure('Clear',F), return, end
HD = get(F,'UserData');

%-Clear figure & make any 'HelpMenu' objects invisible
%-----------------------------------------------------------------------
for h = findobj(get(F,'Children'),'Flat','HandleVisibility','on')'
    if ~strcmp(get(h,'Tag'),'HelpMenu'), delete(h), end
end
set(findobj(F,'Tag','HelpMenu'),'Visible','off')
try
    set(HD.hTopic,'String','');
    RefdTopics = cellstr(get(HD.hRefdTopics,'String'));
    set(HD.hRefdTopics,'Value',1,'String',RefdTopics{1})
end

case '!contexthelp'
%=======================================================================
% h = spm_help('!ContextHelp',Topic)

%-If no topic specified, use caller function
%-----------------------------------------------------------------------
if nargin<2
    st = dbstack;
    if length(st)>1, Topic=st(2).name; else Topic='MenuWin'; end
else
    Topic=varargin{2};
end

%-----------------------------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
if spm('CmdLine')
    fprintf('\nSPM: Type `help %s` for help on this routine.\n',Topic)
    if nargout>0, varargout = {0}; end
elseif ~isempty(Finter)
    delete(findobj(Finter,'Tag','ContextHelp'))
    S2 = get(Finter,'Position');
    hAx = axes('Parent',Finter,...
        'Units','Pixels',...
        'Position',[S2(3)-23 05 20 25],...
        'Tag','ContextHelp',...
        'Visible','off');
    h = text(0.5,0.5,'?',...
        'Parent',hAx,...
        'Color','g',...
        'FontName',spm_platform('font','helvetica'),...
        'FontSize',spm('FontSize',14),...
        'FontWeight','bold','FontAngle','italic',...
        'HorizontalAlignment','Center',...
        'VerticalAlignment','middle',...
        'ButtonDownFcn',['spm_help(''',Topic,''')']);
    if nargout>0, varargout = {h}; end
end

otherwise
%=======================================================================
error('Unknown action string')

%=======================================================================
end


function [dim, index, attr_name, dim_name, values]=cosmo_dim_find(ds, dim_label, raise)
% find dimension attribute in dataset
%
% [dim, index, attr_name, dim_name]=cosmo_dim_find(ds, dim_label[, raise])
%
% Inputs:
%   ds              dataset struct
%   dim_label       dimension label
%   raise           if true, raise an error if the dimension is not found.
%                   Default: false
%
% Outputs:
%   dim             dimension where dim_label was found, 1=sample
%                   dimension, 2=feature dimension
%   index           position where dim_label was found, so that:
%                     ds.a.(dim_name).values{index}==dim_label
%   attr_name       'sa' if dim==1, 'fa' if dim==2
%   dim_name        'sdim' if dim==1, 'fdim' if dim==2
%   values          the values associated with the dimension
%
% Examples:
%     % fMRI dataset, find first voxel dimension
%     ds=cosmo_synthetic_dataset('type','fmri');
%     [dim, index, attr_name, dim_name, values]=cosmo_dim_find(ds,'i');
%     disp(dim)
%     %|| 2
%     disp(index)
%     %|| 1
%     disp(attr_name)
%     %|| fa
%     disp(dim_name)
%     %|| fdim
%     cosmo_disp(values)
%     %|| [ 1 2 3 ]
%
%     % MEEG time-frequency dataset, find 'time' dimension
%     ds=cosmo_synthetic_dataset('type','timefreq','size','big');
%     [dim, index, attr_name, dim_name, values]=cosmo_dim_find(ds,'time');
%     disp(dim)
%     %|| 2
%     disp(index)
%     %|| 3
%     disp(attr_name)
%     %|| fa
%     disp(dim_name)
%     %|| fdim
%     cosmo_disp(values)
%     %|| [ -0.2000 -0.1500 -0.1000 -0.0500 0 ]
%     %
%     % move 'time' from feature to sample dimension
%     dst=cosmo_dim_transpose(ds,'time',1);
%     [dim, index, attr_name, dim_name, values]=cosmo_dim_find(dst,'time');
%     disp(dim)
%     %|| 1
%     disp(index)
%     %|| 1
%     disp(attr_name)
%     %|| sa
%     disp(dim_name)
%     %|| sdim
%     cosmo_disp(values)
%     %|| [ -0.2000 ; -0.1500 ; -0.1000 ; -0.0500 ; 0 ]
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    if nargin<3, raise=true; end

    is_singleton=ischar(dim_label);
    if is_singleton
        dim_label={dim_label};
    elseif ~iscellstr(dim_label)
        error('Second input must be string or cell with strings');
    end

    nlabel=numel(dim_label);

    dim=[];
    index=zeros(nlabel,1);
    values=cell(nlabel,1);
    for k=1:nlabel
        [d,i,an,dn,vs]=find_singleton(ds,dim_label{k},raise);

        if k==1
            dim=d;
            attr_name=an;
            dim_name=dn;
        end

        if isempty(d) || dim~=d || ~isequal(attr_name,an) || ...
                                    ~isequal(dim_name,dn)
            dim=[];
            break;
        end

        index(k)=i;
        values{k}=vs;
    end

    if isempty(dim)
        index=[];
        attr_name=[];
        dim_name=[];
        values=[];

        if raise
            error('Unable to find all labels in the same dimension: %s',...
                        cosmo_strjoin(dim_label,', '));
        end
    else
        if dim==1
            index=index';
            values=values';
        end
        if is_singleton
            values=values{1};
        end
    end

function [dim, index, attr_name, dim_name, values]=find_singleton(ds,...
                                                        dim_label,raise)
    infixes='sf';
    for dim=1:numel(infixes)
        infix=infixes(dim);

        attr_name=[infix 'a'];
        dim_name=[infix 'dim'];

        if cosmo_isfield(ds, ['a.' dim_name '.labels'])
            labels=ds.a.(dim_name).labels;
            m=cosmo_match(labels, dim_label);
            if any(m)
                index=find(m);
                if numel(index)>1 && raise
                    error('Duplicate label %s in .a.%s.labels', ...
                                    dim_label, dim_name);
                elseif ~cosmo_isfield(ds, ['a.' dim_name '.values'],...
                                                        raise)
                    % not all fields present

                else
                    values=ds.a.(dim_name).values{index};
                    return
                end
            end
        end
    end

    dim=[];
    index=[];
    attr_name=[];
    dim_name=[];
    values=[];

    if raise
        error('Not found: dimension label %s', dim_label);
    end