function io_save(fname, varargin)
% Like MATLAB's builtin save(), except that the fname can be platform-independent.
% io_save(Filename, Arguments...)
%
% Also, additional -options are supported, which offer additional fault-tolerance and automation,
% especially for long-running batch scripts.
%
% In:
%   Filename    :   Platform-Independent file name. If no ending is given, .mat will be assumed.
%                   If the ending is .sto, then BCILAB's Storage file format will be used (10-50x as
%                   fast as .mat).
%
%   Arguments...:   optional arguments to save; in addition to what is supported by save(), the following options are allowed:
%                   -makedirs   : make directories (recursively), if possible
%                   -retryinput : ask the user whether he/she wants to retry creating the directory or the file, if unsuccessful (using input())
%                   -attributes attribs : specify file/directory attributes; these are the second or second and third argument to be passed to
%                                         the fileattrib() function, separated by a comma, to set attributes for the current user or specific user groups,
%                                         e.g., '+w' or '+w','a'.
%                   -prunehandles : prune unreferenced variables from anonymous function handles; these are normally not used but saved anyway since cases
%                                   can be constructed in which they are actually needed by the function (e.g. using evalin('caller',...))
%                                   since these can be extremely large, they can easily cause save() to fail
%                   -serialized : compress the data using hlp_serialize
%                                 (when loading with io_load, the data will be automatically uncompressed,
%                                 not so when using load directly)
%                   -dataformat format : specify the data format to use for saving; the default is
%                                        'v7.3', but for interop with some software, 'v7' is needed
%
% Example:
%   % save the variables a,b, and c to a .mat file
%   io_save('store:/myoutput.mat', 'a','b','c');
%
%   % as before, but try to create directories if necessary
%   io_save('store:/myoutput.mat', 'a','b','c','-makedirs');
%
%   % like before, but this time prompt the user to re-try if an error occurs
%   io_save('store:/myoutput.mat', 'a','b','c','-makedirs','-retryinput');
%
%   % like before, but this time make the created file writable for all users
%   io_save('store:/myoutput.mat', 'a','b','c','-attributes','''+w'',''a''');
%
% See also:
%   save, io_load
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-06-21

% translate the file name
fname = env_translatepath(fname);
if length(fname)<4 || ~any(fname(end-3:end-1)=='.')
    fname = [fname '.mat']; end

% parse arguments...
saveargs = {};        % default args to save()
variables = {};       % names of variables to save (subset of saveargs)
makedirs = false;     % whether to create directories
retryinput = false;   % whether to ask the user for a retry
prunehandles = false; % whether to prune unreferenced workspace variables from function handles in arguments
serialized = false;   % whether to write variables as serialized
nooverwrite = false;  % whether to disable overwriting existing files
dataformat = 'v7.3';  % data format to use
fileattriblist = [];  % the list of attributes for files & directories
i = 1;
while i <= length(varargin)
    arg = varargin{i};
    if ischar(arg) && ~isempty(arg) && arg(1) == '-'
        % we are dealing with an option argument
        switch(strtrim(arg))
            case '-makedirs'
                makedirs = true;
            case '-retryinput'
                retryinput = true;
            case '-attributes'
                fileattriblist = varargin{i+1};
                i = i+1; % skip next arg, too
            case '-prunehandles'
                prunehandles = true;
            case {'-serialized','-serialize'}
                serialized = true;
            case '-nooverwrite'
                nooverwrite = true;
            case '-dataformat'
                nextarg = varargin{i+1};
                i = i+1;
                if ~strcmp(nextarg, 'default')
                    if nextarg(1) ~= 'v'
                        disp('WARNING: io_save dataformat is expected to begin with a "v" character.'); end
                    dataformat = nextarg;
                end
            otherwise
                % regular argument; append it
                saveargs{end+1} = arg;
        end
    elseif ~isempty(arg)
        % append the argument
        saveargs{end+1} = arg;
        variables{end+1} = arg;
    end
    i = i+1; % go to next argument
end

% prepend save format option
saveargs = [{['-' dataformat]} saveargs];

% move variables into a local workspace for optional processing
if isempty(variables)
    % no variables listed: obtain all
    varinfos = evalin('caller','whos');
    variables = {varinfos.name};
    saveargs = [saveargs variables];
end
for k=1:length(variables)
    wkspace.(variables{k}) = evalin('caller',variables{k}); end

% prune their handles
if prunehandles
    for k=1:length(variables)
        wkspace.(variables{k}) = utl_prune_handles(wkspace.(variables{k})); end
end

% serialize the variables
if serialized
    for k=1:length(variables)
        wkspace.(variables{k}) = hlp_serialize(wkspace.(variables{k})); end
    wkspace.is_serialized__ = ['variables were serialized using ' hlp_funcversion(@hlp_serialize) '; use hlp_deserialize (see File Exchange) to return them to their original form.'];
    saveargs{end+1} = 'is_serialized__';
end

% reformat the save-args into a string
tmp = [saveargs' repmat({' '},length(saveargs),1)]';
saveargs = [tmp{:}];

% reformat the fileattriblist into a cell array...
fileattriblist = eval(['{' fileattriblist '}']);

while 1
    if makedirs
        % first ensure that the directory exists
        while 1
            try
                io_mkdirs(fname,fileattriblist);
                break;
            catch
                warning('BCILAB:io_save:cannot_create_directory',['Error creating target directory ' fname '. Please review your permissions.']);
                if retryinput
                    if strcmp(input('Would you like to retry? [yes/no]: ','s'),'no')
                        disp('Did not save the data.');
                        break;
                    end
                else
                    break;
                end
            end
        end
    end

    % we have copied the variables to be saved into the 'wkspace' variable; now save
    try
        if nooverwrite && exist(fname,'file')
            return; end
        if length(fname)>4 && strcmpi(fname(end-3:end),'.sto')
            % save as .sto file (fast)
            bytes = hlp_serialize(wkspace);
            f = fopen(fname,'w');
            fwrite(f,bytes);
            fclose(f);
        else
            % save as .mat file (slow)
            save_workspace(fname,saveargs,wkspace);
        end
        if ~isempty(fileattriblist)
            % try to set file fileattriblist...
            if ~isempty(fileattriblist)
                warning off MATLAB:FILEATTRIB:SyntaxWarning
                fileattrib(fname,fileattriblist{:});
            end
        end
        break;
    catch e
        warning('BCILAB:io_save:cannot_save_file',['Error saving target file ' fname '. Please review your permissions: ' e.message]);
        if retryinput
            if strcmp(input('Would you like to retry? [yes/no]','s'),'no')
                disp('Did not save the data.');
                break;
            end
        else
            break;
        end
    end
end

% save, given a workspace of variables
function save_workspace(fname__,saveargs__,workspace__)
for fn__=fieldnames(workspace__)'
    eval([fn__{1} ' = workspace__.(fn__{1});']); end
clear workspace__ fn__;
eval(['save ''' fname__ ''' ' saveargs__ ';']);


function writeCbToSBML(model,fileName)
%writeCbToSBML write a COBRA model to SBML
%
% writeCbToSBML(model,fileName)
%
%INPUTS
% model         COBRA model structure
% fileName      Name of xml file output
%
        sbmlModel = convertCobraToSBML(model);
        OutputSBML(sbmlModel, fileName);


function [varargout] = plus(varargin)

% PLUS compile the missing mex file on the fly

% Copyright (C) 2012, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% remember the original working directory
pwdir = pwd;

% determine the name and full path of this function
funname = mfilename('fullpath');
mexsrc  = [funname '.c'];
[mexdir, mexname] = fileparts(funname);

try
  % try to compile the mex file on the fly
  warning('trying to compile MEX file from %s', mexsrc);
  cd(mexdir);
  mex(mexsrc);
  cd(pwdir);
  success = true;

catch
  % compilation failed
  disp(lasterr);
  error('could not locate MEX file for %s', mexname);
  cd(pwdir);
  success = false;
end

if success
  % execute the mex file that was just created
  funname   = mfilename;
  funhandle = str2func(funname);
  [varargout{1:nargout}] = funhandle(varargin{:});
end


function [y,varname]=xml2cell(filename_or_xml_string)

%XML2CELL reads non-MbML compliant xmlfile into matlab nested cell arrays
%
% Syntax [y,varname]=xmlfile2cell(filename_or_xml_string)
%
% Description:
%   1. Convert any non-MbML xml into MbML compliant string
%   2. Stores individual structures as nested cell arrays
%
% If it cannot be garanteed that non-MbML compliant XML has an
% internal referential consistency for convenient conversion to structures,
% this function builds the m-variable object model (MOM) as nested
% cell arrays. This approach ignores the possible dimensionality of the
% object and stores each entry, in a single cell, nested at the
% appropriate level.
%
% Note 1 : if your XML string is MbML compliant use XML2MAT instead
% Note 2 : if your XML structure has consistent dimensionality use XML2STRUCT instead
%
% See also: XML2STRUCT, XML2MAT
%
% Jonas Almeida, almeidaj@musc.edu, 19 May 2003, MAT4NAT Tbox

% is this a xml string or xml filename ?
if filename_or_xml_string(1)=='<'
    y=filename_or_xml_string;
else
    y=strrep(file2str(filename_or_xml_string),'''','''''');
end

% convert first to MbML compliant string and then onto an m-variable
[y,varname]=xml2mat(mbmling(y,1));


classdef MetaDataTagsClass  < FileLoadSaveClass

    properties
        tags
    end

    methods

        % -------------------------------------------------------
        function obj = MetaDataTagsClass(varargin)
            % Set class properties not part of the SNIRF format
            obj.SetFileFormat('hdf5');
            obj.tags.SubjectID = 'default';
            obj.tags.MeasurementDate = datestr(now,29);
            obj.tags.MeasurementTime = datestr(now,'hh:mm:ss');
            obj.tags.LengthUnit = 'mm';
            obj.tags.TimeUnit = 'unknown';
            obj.tags.FrequencyUnit = 'unknown';
            obj.tags.AppName  = 'homer3-DataTree';

            if nargin==1 && ~isempty(varargin{1})
                obj.SetFilename(varargin{1});
                obj.Load();
            end
            if nargin==2
                obj.tags.LengthUnit = varargin{2};
            end
        end

        % -------------------------------------------------------
        function err = LoadHdf5(obj, fileobj, location)
            err = 0;

            % Arg 1
            if ~exist('fileobj','var') || (ischar(fileobj) && ~exist(fileobj,'file'))
                fileobj = '';
            end

            % Arg 2
            if ~exist('location', 'var') || isempty(location)
                location = '/nirs/metaDataTags';
            elseif location(1)~='/'
                location = ['/',location];
            end

            % Error checking
            if ~isempty(fileobj) && ischar(fileobj)
                obj.SetFilename(fileobj);
            elseif isempty(fileobj)
                fileobj = obj.GetFilename();
            end
            if isempty(fileobj)
               err = -1;
               return;
            end

            %%%%%%%%%%%% Ready to load from file

            try
                % Reset tags
                obj.tags = struct();

                % Open group
                [gid, fid] = HDF5_GroupOpen(fileobj, location);

                metaDataStruct = h5loadgroup(gid);
                tags = fieldnames(metaDataStruct); %#ok<*PROPLC>
                for ii=1:length(tags)
                    eval(sprintf('obj.tags.%s = metaDataStruct.%s;', tags{ii}, tags{ii}));
                end

                HDF5_GroupClose(fileobj, gid, fid);

                % Detect old or invalid metadatatag data
                assert(obj.IsValid());

            catch

                err = -1;

            end
            obj.SetError(err);

        end

        % -------------------------------------------------------
        function SaveHdf5(obj, fileobj, location) %#ok<*INUSD>
            % Arg 1
            if ~exist('fileobj', 'var') || isempty(fileobj)
                error('Unable to save file. No file name given.')
            end

            % Arg 2
            if ~exist('location', 'var') || isempty(location)
                location = '/nirs/metaDataTags';
            elseif location(1)~='/'
                location = ['/',location]; %#ok<*NASGU>
            end

            if ~exist(fileobj, 'file')
                fid = H5F.create(fileobj, 'H5F_ACC_TRUNC', 'H5P_DEFAULT', 'H5P_DEFAULT');
                H5F.close(fid);
            end
            props = propnames(obj.tags);
            for ii = 1:length(props)
                eval(sprintf('hdf5write_safe(fileobj, [location, ''/%s''], obj.tags.%s);', props{ii}, props{ii}));
            end
        end

        % -------------------------------------------------------
        function b = IsValid(obj)
            b = false;

            % Use latest required fields to determine if we're loading old
            % metaDataTag format version of SNIRF spec
            if ~isproperty(obj.tags, 'FrequencyUnit')
                return;
            end

            b = true;
        end

        % -------------------------------------------------------
        function B = eq(obj, obj2)
            B = false;
            props1 = propnames(obj.tags);
            props2 = propnames(obj2.tags);
            for ii=1:length(props1)
                if ~isproperty(obj2.tags, props1{ii})
                    return;
                end
                if eval(sprintf('~strcmp(obj.tags.%s, obj2.tags.%s)', props1{ii}, props1{ii}))
                    return;
                end
            end
            for ii=1:length(props2)
                if ~isproperty(obj.tags, props2{ii})
                    return;
                end
                if eval(sprintf('~strcmp(obj.tags.%s, obj2.tags.%s)', props2{ii}, props2{ii}))
                    return;
                end
            end
            B = true;
        end

        % -------------------------------------------------------
        function Add(obj, key, value) %#ok<INUSL>
            key(key==' ') = '';
            eval(sprintf('obj.tags.%s = value', key));
        end

        % ----------------------------------------------------------------------------------
        function val = Get(obj, name)
            val = [];
            if ~exist('name', 'var')
                return;
            end
            if isfield(obj.tags, name)
                val = eval( sprintf('obj.tags.%s;', name) );
            end
        end

        % ----------------------------------------------------------------------------------
        function Set(obj, name, value)
            if ~exist('name', 'var') || ~exist('value', 'var')
                retrun
            end
            eval( sprintf('obj.tags.%s = ''%s'';', name, value) )
        end

        % ----------------------------------------------------------------------------------
        function SetLengthUnit(obj, unit)
            if isempty(obj)
                return
            end
            obj.tags.LengthUnit = unit;
        end

        % ----------------------------------------------------------------------------------
        function val = GetLengthUnit(obj)
            val = '';
            if isempty(obj)
                return
            end
            val = obj.tags.LengthUnit;
        end

        % ----------------------------------------------------------------------------------
        function nbytes = MemoryRequired(obj)
            nbytes = 0;
            fields = propnames(obj.tags);
            for ii=1:length(fields)
                nbytes = nbytes + eval(sprintf('sizeof(obj.tags.%s)', fields{ii}));
            end
        end

    end
end


function [r] = issubfield(s, f)

% ISSUBFIELD tests for the presence of a field in a structure just like the standard
% Matlab ISFIELD function, except that you can also specify nested fields
% using a '.' in the fieldname. The nesting can be arbitrary deep.
%
% Use as
%   f = issubfield(s, 'fieldname')
% or as
%   f = issubfield(s, 'fieldname.subfieldname')
%
% This function returns true if the field is present and false if the field
% is not present.
%
% See also ISFIELD, GETSUBFIELD, SETSUBFIELD

% Copyright (C) 2005, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id: issubfield.m 2885 2011-02-16 09:41:58Z roboos $

try
  getsubfield(s, f);    % if this works, then the subfield must be present
  r = true;
catch
  r = false;                % apparently the subfield is not present
end


function s = report_this_filefun(level)
    % this script replaced all the individual "disp('This is ...') messages
    % it can be used to track which files are used, or do some function/file specific
    % setup
    %  REPORT_THIS_FILEFUN() display message if ANY level has been set
    %  REPORT_THIS_FILEFUN( LEVEL ) print message if

    %called by script or function?
    % modified from: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/

    ZG = ZmapGlobal.Data;

    %% change debugging behavior
    if nargin==0
        level=1;
    end

    if ~ZG.debug || level < ZG.debugLevel
        return
    end

    %% ok. Now do the reporting

    dbk=dbstack('-completenames');

    thecaller = 'user interaction';
    thefun = '- none -'; %#ok<NASGU>
    thefilename = '<anon. funct>'; %#ok<NASGU>
    line = nan;

    switch numel(dbk)
        case 1
            msg.dbdisp('nothing to report. [no stack to speak of]')
            return
        case 2
            thefun = dbk(2).name;
            thefilename=dbk(2).file;
        otherwise
            thefun = dbk(2).name;
            thefilename=dbk(2).file;
            thecaller = dbk(3).name;
            line = dbk(3).line;
    end
    % test to see if it is a script

    try
        [~] = nargin(thefun); % errors if not a function
        fn_id_tag = 'function';
    catch ME
        if strcmp(ME.identifier, 'MATLAB:nargin:isScript')
            fn_id_tag = 'script';
        else
            fn_id_tag =  '';
        end
    end
    if nargin > 0
        s=sprintf('%s %s [%s]\n  called by %s line %d\n\n', fn_id_tag, thefun, thefilename, thecaller, line);
    else
        s=sprintf('[%s]\n  called by %s line %d\n', thefilename, thecaller, line);
    end
    msg.dbdisp(s,['in ' fn_id_tag ' [' thefun ']']);
end


classdef GemImageDisplayParameters < CoreBaseClass
    % GemImageDisplayParameters. Parameters for visualising an image
    %
    %     Licence
    %     -------
    %     Part of the TD Pulmonary Toolkit. https://github.com/tomdoel/pulmonarytoolkit
    %     Author: Tom Doel, 2013.  www.tomdoel.com
    %     Distributed under the GNU GPL v3 licence. Please see website for details.
    %

    events
        OpacityChanged
        ShowImageChanged
    end

    properties (SetObservable)
        Window = 1600          % The image window (in HU for CT images)
        Level = -600           % The image level (in HU for CT images)
        Opacity = 100
        ShowImage = true
        BlackIsTransparent = false
        OpaqueColour
    end
end


function ubj=saveubjson(rootname,obj,varargin)
%
% ubj=saveubjson(obj)
%    or
% ubj=saveubjson(rootname,obj,filename)
% ubj=saveubjson(rootname,obj,opt)
% ubj=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)
%
% Convert a MATLAB object  (cell, struct, array, table, map, graphs ...)
% into a Universal Binary JSON (UBJSON, Draft-12) or a MessagePack binary stream
%
% author: Qianqian Fang (q.fang <at> neu.edu)
% initially created on 2013/08/17
%
% Format specifications:
%    Binary JData (BJData):https://github.com/NeuroJSON/bjdata
%    UBJSON:               https://github.com/ubjson/universal-binary-json
%    MessagePack:          https://github.com/msgpack/msgpack
%
% This function is the same as calling "savebj(..,'ubjson',1,'endian','B')"
% By default this function creates UBJSON-compliant output without the
% newly added uint16(u), uint32(m), uint64(M) and half-precision float (h)
% data types and use Big-Endian for all numerical values as in UBJSON
% Draft-12.
%
% This function by default still enables an optimized ND-array format for efficient
% array storage. To ensure the output compatible to UBJSON Draft-12, one should use
% "saveubjson(...,'NestArray',1)" or "savebj(...,'ubjson',1,'NestArray',1)"
%
% input:
%      rootname: the name of the root-object, when set to '', the root name
%           is ignored, however, when opt.ForceRootName is set to 1 (see below),
%           the MATLAB variable name will be used as the root name.
%      obj: a MATLAB object (array, cell, cell array, struct, struct array,
%           class instance)
%      filename: a string for the file name to save the output UBJSON data
%      opt: a struct for additional options, ignore to use default values.
%           opt can have the following fields (first in [.|.] is the default)
%
%           opt can be replaced by a list of ('param',value) pairs. The param
%           string is equivallent to a field in opt and is case sensitive.
%
%           Please type "help savebj" for details for all supported options.
%
% output:
%      ubj: a binary string in the UBJSON format (see http://ubjson.org)
%
% examples:
%      jsonmesh=struct('MeshVertex3',[0 0 0;1 0 0;0 1 0;1 1 0;0 0 1;1 0 1;0 1 1;1 1 1],...
%               'MeshTet4',[1 2 4 8;1 3 4 8;1 2 6 8;1 5 6 8;1 5 7 8;1 3 7 8],...
%               'MeshTri3',[1 2 4;1 2 6;1 3 4;1 3 7;1 5 6;1 5 7;...
%                          2 8 4;2 8 6;3 8 4;3 8 7;5 8 6;5 8 7],...
%               'MeshCreator','FangQ','MeshTitle','T6 Cube',...
%               'SpecialData',[nan, inf, -inf]);
%      saveubjson(jsonmesh)
%      saveubjson('',jsonmesh,'meshdata.ubj')
%      saveubjson('mesh1',jsonmesh,'FileName','meshdata.msgpk','MessagePack',1)
%      saveubjson('',jsonmesh,'KeepType',1)
%
% license:
%     BSD or GPL version 3, see LICENSE_{BSD,GPLv3}.txt files for details
%
% -- this function is part of JSONLab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab)
%

if(nargin==1)
    ubj=savebj('',rootname,'ubjson',1,'endian','B');
elseif(length(varargin)==1 && ischar(varargin{1}))
    ubj=savebj(rootname,obj,'FileName',varargin{1},'ubjson',1,'endian','B');
else
    ubj=savebj(rootname,obj,varargin{:},'ubjson',1,'endian','B');
end


function view=loadResStdMap(view)
%
% view=loadMeanMap(view)
%
% Checks view.mapName field of the view structure.
% If view.mapName is not "resStdMap", then load it,
% and call setParameterMap.
%
% If you change this function make parallel changes in:
%   loadCorAnal, loadResStdMap, loadStdMap, loadMeanMap
%
% djh, 7/16/99, modified from loadCorAnal

if ~strcmp(view.mapName,'resStdMap')
   pathStr=fullfile(dataDir(view), 'resStdMap.mat');
   if ~exist(pathStr,'file')
     warning(['No ',pathStr,' file.  Run compute resStd map from Analysis menu.']);
     return
   else
      disp(['loading resStdMap from ',pathStr]);
      load(pathStr);
      view=setParameterMap(view,map,'resStdMap');
   end
end


function varargout = bst_navigator( varargin )
% BST_NAVIGATOR: Navigation through the Brainstorm database.
%
% USAGE : bst_navigator( 'DbNavigation', action, iDataSets ) : Process only the selected datasets
%         bst_navigator( 'DbNavigation', action )            : Process all the loaded datasets
%         bst_navigator( 'CreateNavigatorMenu', jMenuNavigator)
%
% INPUT : action    : possible values {'NextData', 'NextSubject', 'NextCondition', 'NextResult', 'Previous...'}
%         iDataSets : indices of GlobalData.DataSet array

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2008-2015

eval(macro_method);
end

%% ===== NAVIGATION =====
function DbNavigation( action, iDataSets )
    global GlobalData mutexNavigator timerNavigator;
    % ===== MUTEX =====
    % Use a mutex to prevent the function from being executed more than once at the same time
    if isempty(mutexNavigator) || (mutexNavigator > 1)
        % Entrance accepted
        timerNavigator = tic;
        mutexNavigator = 0;
    else
        % Entrance rejected (another call is not finished,and was call less than 1 seconds ago)
        if ~isempty(timerNavigator)
            mutexNavigator = toc(timerNavigator);
        else
            mutexNavigator = toc;
        end
        disp('Call to bst_navigator() ignored...');
        return
    end

    % ===== PARSE INPUTS =====
    % If no DataSets are specified : process all datasets
    if (nargin < 2)
        iDataSets = 1:length(GlobalData.DataSet);
    end
    % Interpret action
    action = lower(action);
    if ~isempty(strfind(action, 'next'))
        moveDirection = 1;
        action = strrep(action, 'next', '');
    elseif ~isempty(strfind(lower(action), 'previous'))
        moveDirection = -1;
        action = strrep(action, 'previous', '');
    else
        moveDirection = 0;
    end
    % Get protocol information
    ProtocolSubjects = bst_get('ProtocolSubjects');

%% ===== FOR EACH DATA SET =====
    iDSclose = [];
    for iDS = iDataSets
        % ===== CHECK DATASET =====
        % Two necessary conditions : have a SubjectFile defined, and have at least one figure displayed
        if isempty(GlobalData.DataSet(iDS).SubjectFile) || isempty(GlobalData.DataSet(iDS).Figure)
            % Closing all datasets figures
            % close([GlobalData.DataSet(iDS).Figure.hFigure]);
            continue
        end
        % Save modifications
        if GlobalData.DataSet(iDS).Measures.isModified
            panel_record('SaveModifications', iDS);
        end

        % ===== GET DATASET INFORMATION =====
        % === SUBJECT ===
        [sOldSubject, iOldSubject] = bst_get('Subject', GlobalData.DataSet(iDS).SubjectFile);
        % === STUDY ===
        if isempty(GlobalData.DataSet(iDS).StudyFile)
            isNoStudyFile = 1;
        else
            isNoStudyFile = 0;
            [sOldStudy, iOldStudy] = bst_get('Study', GlobalData.DataSet(iDS).StudyFile);
            if isempty(sOldStudy)
                isNoStudyFile = 1;
            end
        end
        % === DATA ===
        if isempty(GlobalData.DataSet(iDS).DataFile)
            isNoDataFile = 1;
        else
            isNoDataFile = 0;
            [tmp__, tmp__, iOldData] = bst_get('DataFile', GlobalData.DataSet(iDS).DataFile);
            if isempty(iOldData)
                isNoDataFile = 1;
            end
        end

        % ===== GET NEW LOCATION =====
        isSubjectFileChanged = 0;
        isStudyFileChanged   = 0;
        isDataFileChanged    = 0;
        isResultsFileChanged = 0;
        waitMsg = '';
        % Switch according to action
        switch lower(action)
            % ===== SUBJECT =====
            case 'subject'
                % === NEW SUBJECT ===
                % Get next/previous subject
                iNewSubject = iOldSubject + moveDirection;
                if (iNewSubject <= 0) || (iNewSubject > length(ProtocolSubjects.Subject))
                    continue
                end
                % Switching to next subject
                sNewSubject = bst_get('Subject', iNewSubject);
                newSubjectName = ProtocolSubjects.Subject(iNewSubject).Name;
                newSubjectFile = ProtocolSubjects.Subject(iNewSubject).FileName;
                isSubjectFileChanged = 1;
                % === NEW STUDY ===
                % If study file is defined
                if ~isNoStudyFile
                    % Get study with NEW subject AND OLD condition
                    newConditionPath = bst_fullfile(newSubjectName, sOldStudy.Condition{:});
                    [sNewStudy, iNewStudy] = bst_get('StudyWithCondition', newConditionPath);
                    % If no study was found
                    if isempty(sNewStudy)
                        continue
                    end
                    isStudyFileChanged = 1;
                else
                    iNewStudy = [];
                    sNewStudy = [];
                end
                % === NEW DATA ===
                % If a DataFile is defined
                if ~isNoDataFile
%                     % If data index is not valid anymore
%                     if (iOldData > length(sNewStudy.Data))
%                         continue
%                     end
%                     iNewData = iOldData;
                    % Cannot switch to an empty folder
                    if isempty(sNewStudy.Data)
                        continue;
                    end
                    % Always the first file of the new condition
                    iNewData = 1;
                    sNewData = sNewStudy.Data(iNewData);
                    % Cannot switch to a continuous raw file
                    if strcmpi(sNewData.DataType, 'raw')
                        continue;
                    end
                    % Display progress bar
                    waitMsg = ['Switching to subject #' num2str(iNewSubject) ': "' newSubjectName '"...' ];
                    % Ask to update the current datafile
                    isDataFileChanged = 1;
                end

            % ===== CONDITION =====
            case 'condition'
                % === NEW SUBJECT ===
                sNewSubject = sOldSubject;
                newSubjectName = sNewSubject.Name;
                newSubjectFile = sNewSubject.FileName;
                % === NEW STUDY ===
                % If study file is defined
                if ~isNoStudyFile
                    % Get all the conditions available for the current subject
                    availableConditions = bst_get('ConditionsForSubject', sOldStudy.BrainStormSubject);
                    % Find current condition in conditions list
                    iOldCond = [];
                    for i=1:length(availableConditions)
                        if isequal(availableConditions{i}, sOldStudy.Condition{1})
                            iOldCond = i;
                            break;
                        end
                    end
                    if isempty(iOldCond)
                        continue;
                    end
                    % Get next/previous condition
                    iNewCond = iOldCond + moveDirection;
                    if (iNewCond <= 0) || (iNewCond > length(availableConditions))
                        continue
                    end
                    % Get study with NEW subject AND OLD condition
                    newConditionPath = bst_fullfile(newSubjectName, availableConditions{iNewCond});
                    [sNewStudy, iNewStudy] = bst_get('StudyWithCondition', newConditionPath);
                    % If no study was found
                    if isempty(sNewStudy)
                        continue
                    end
                    isStudyFileChanged = 1;
                end
                % === NEW DATA ===
                % If a DataFile is defined
                if ~isNoDataFile
%                     % If data index is not valid anymore
%                     if (iOldData > length(sNewStudy.Data))
%                         continue
%                     end
%                     iNewData = iOldData;
                    % Cannot switch to an empty folder
                    if isempty(sNewStudy.Data)
                        continue;
                    end
                    % Always the first file of the new condition
                    iNewData = 1;
                    sNewData = sNewStudy.Data(iNewData);
                    % Cannot switch to a continuous raw file
                    if strcmpi(sNewData.DataType, 'raw')
                        continue;
                    end
                    % Display progress bar
                    waitMsg = sprintf('Switching to condition "%s"...', newConditionPath);
                    % Ask to update the current datafile
                    isDataFileChanged = 1;
                end

            % ===== DATA =====
            case 'data'
                % If DataFile or StudyFile is not defined
                % => Changing Data cannot have consequences : go to next DataSet
                if isNoStudyFile || isNoDataFile
                    continue
                end
                % === NEW STUDY ===
                sNewStudy   = sOldStudy;
                iNewStudy   = iOldStudy;
                % === NEW SUBJECT ===
                sNewSubject = sOldSubject;
                newSubjectFile = sNewSubject.FileName;

                % ===== DATA LISTS =====
                % Get standardized comments
                listComments = cellfun(@str_remove_parenth, {sOldStudy.Data.Comment}, 'UniformOutput', 0);
                % Remove empty matrices
                iEmpty = cellfun(@isempty, listComments);
                if ~isempty(iEmpty)
                    listComments(iEmpty) = {''};
                end
                % Group comments
                [tmp,tmp,iData2List] = unique(listComments);
                % If current file is in a group of trials and not the last one
                iList = iData2List(iOldData);
                % Move to the next one in the trial group
                if (moveDirection == 1) && (nnz(iData2List(iOldData+1:end) == iList) > 0)
                    iNewData = iOldData + find(iData2List(iOldData+1:end) == iList, 1, 'first');
                % Move to the pervious one in the trial group
                elseif (moveDirection == -1) && (nnz(iData2List(1:iOldData-1) == iList) > 0)
                    iNewData = find(iData2List(1:iOldData-1) == iList, 1, 'last');
                % Else go to the next/previous file in the .Data structure
                else
                    iNewData = iOldData + moveDirection;
                end

                % === NEW DATA ===
                % Is action valid ?
                if (iNewData <= 0) || (iNewData > length(sOldStudy.Data))
                    continue
                end
                sNewData = sNewStudy.Data(iNewData);
                % Cannot switch to a continuous raw file
                if strcmpi(sNewData.DataType, 'raw')
                    continue;
                end
                % Display progress bar
                waitMsg = sprintf('Switching to data file #%d: "%s"...', iNewData, sNewData.Comment);
                % Ask to update the current datafile
                isDataFileChanged = 1;

            % ===== RESULT =====
            case 'results'
                % If DataFile or StudyFile is not defined
                % => Changing Results cannot have consequences : go to next DataSet
                if isNoStudyFile || isNoDataFile
                    continue;
                end
                % Current ResultsFiles changed
                isResultsFileChanged = 1;
                % But associated data file did not change
                sNewSubject = sOldSubject;
                newSubjectFile = sNewSubject.FileName;
                sNewStudy = sOldStudy;
                sNewData  = sNewStudy.Data(iOldData);
                % Waiting message
                switch (moveDirection)
                    case 1
                        waitMsg = 'Switching to next results file...';
                    case -1
                        waitMsg = 'Switching to previous results file...';
                end
            otherwise
                bst_error(['Invalid action: "' action '"']);
                return
        end

%% ===== UPDATE GLOBAL STRUCTURES =====
        % === COLLAPSE PREVIOUS NODE ===
        if isSubjectFileChanged
            panel_protocols('CollapseAncestor', 'subject');
        elseif isStudyFileChanged
            panel_protocols('CollapseAncestor', 'study');
        elseif isDataFileChanged
            panel_protocols('CollapseAncestor', 'data');
        end

        % === DATAFILE CHANGED ===
        if isDataFileChanged
            % If other DataSets already exist for the new DataFile, unload them
            iOldDS = bst_memory('GetDataSetData', sNewData.FileName);
            if ~isempty(iOldDS)
                bst_memory('UnloadDataSets', iOldDS);
            end
            % Empty loaded data
            GlobalData.DataSet(iDS).Measures = db_template('Measures');
            % Select new data in tree
            %panel_protocols('SelectNode', [], 'data', iNewStudy, iNewData);
            panel_protocols('SelectNode', [], sNewData.FileName);
        % === STUDY CHANGED ===
        elseif isStudyFileChanged
            % Select new data in tree (for the SUBJECTS and STUDIES exploration modes)
            panel_protocols('SelectStudyNode', iNewStudy);
        % === SUBJECT CHANGED ===
        elseif isSubjectFileChanged
            % Select new data in tree (for the SUBJECTS and STUDIES exploration modes)
            panel_protocols('SelectNode', [], 'subject', iNewSubject, -1);
            panel_protocols('SelectNode', [], 'studysubject', -1, iNewSubject);
        end

        % === UPDATE DATASET ===
        GlobalData.DataSet(iDS).SubjectFile = newSubjectFile;
        % StudyFile
        if ~isNoStudyFile
            GlobalData.DataSet(iDS).StudyFile = sNewStudy.FileName;
        end
        % DataFile
        if ~isNoDataFile
            GlobalData.DataSet(iDS).DataFile = sNewData.FileName;
            % Old time window
            oldTime = GlobalData.UserTimeWindow.Time;
            % Force data reload
            bst_memory('LoadDataFile', sNewData.FileName, 1, 0);
            % If the time was modified
            if any(abs(GlobalData.DataSet(iDS).Measures.Time - GlobalData.UserTimeWindow.Time) > 1e-6)
                % Change the current time window
                GlobalData.UserTimeWindow.Time            = GlobalData.DataSet(iDS).Measures.Time;
                GlobalData.UserTimeWindow.SamplingRate    = GlobalData.DataSet(iDS).Measures.SamplingRate;
                GlobalData.UserTimeWindow.NumberOfSamples = GlobalData.DataSet(iDS).Measures.NumberOfSamples;
                % Change current time if necessary
                if (GlobalData.UserTimeWindow.CurrentTime < GlobalData.UserTimeWindow.Time(1)) || (GlobalData.UserTimeWindow.CurrentTime > GlobalData.UserTimeWindow.Time(2))
                    panel_time('SetCurrentTime', GlobalData.UserTimeWindow.Time(1));
                % Update time panel
                elseif any(abs(oldTime - GlobalData.UserTimeWindow.Time) > 1e-6)
                    panel_time('UpdatePanel');
                end
                % Close all the other datasets
                iDSclose = setdiff(1:length(GlobalData.DataSet), iDS);
            end
        else
            % Reset UserTimeWindow
            GlobalData.UserTimeWindow.Time            = [];
            GlobalData.UserTimeWindow.SamplingRate    = [];
            GlobalData.UserTimeWindow.NumberOfSamples = 0;
        end
        % === CHANNEL FILE ===
        isUpdatedChannelFile = 0;
        if ~isempty(iNewStudy)
            % Get old channel file
            oldChannelFile = GlobalData.DataSet(iDS).ChannelFile;
            % Get new channel
            sNewChannel = bst_get('ChannelForStudy', iNewStudy);
            % If ChannelFile changed
            if ~isempty(sNewChannel) && ~file_compare(sNewChannel.FileName, oldChannelFile)
                newChannelFile = sNewChannel.FileName;
                isUpdatedChannelFile = 1;
                % Update GlobalData structure
                GlobalData.DataSet(iDS).ChannelFile = newChannelFile;
                % Load new channel file
                ChannelMat = in_bst_channel(newChannelFile);
                GlobalData.DataSet(iDS).Channel         = ChannelMat.Channel;
                GlobalData.DataSet(iDS).MegRefCoef      = ChannelMat.MegRefCoef;
                GlobalData.DataSet(iDS).Projector       = ChannelMat.Projector;
                GlobalData.DataSet(iDS).Clusters        = ChannelMat.Clusters;
                GlobalData.DataSet(iDS).IntraElectrodes = ChannelMat.IntraElectrodes;
            end
        end

        % === GET NEW RESULTS ===
        % Get results loaded in DataSet
        if ~isempty(GlobalData.DataSet(iDS).Results)
            oldResultsFiles = {GlobalData.DataSet(iDS).Results.FileName};
        else
            oldResultsFiles = {};
        end
        % New results
        newResultsFiles = cell(1, length(oldResultsFiles));
        if (isDataFileChanged || isResultsFileChanged) && ~isempty(oldResultsFiles)
            % Unload all DataSet results
            bst_memory('UnloadDataSetResult', iDS, 1:length(GlobalData.DataSet(iDS).Results));

            % For each old ResultsFile, build the new ResultsFile (or [] if no equivalent)
            for i=1:length(oldResultsFiles)
                % Get old result file indice in sOldStudy
                [tmp__, tmp__, iOldResult] = bst_get('ResultsFile', oldResultsFiles{i});
                % Get old list of available results in previous sOldStudy
                [tmp__, tmp__, iOldAvailableResults] = bst_get('ResultsForDataFile', sOldStudy.Result(iOldResult).DataFile, iOldStudy);
                % Get indice of old result file in available results
                iOldResultInAvailable = find(iOldAvailableResults == iOldResult);

                % === RESULTS NAVIGATION ===
                if isResultsFileChanged
                    % Data file did not change
                    iNewAvailableResults = iOldAvailableResults;
                    % Get next or previous result
                    iNewResultInAvailable = iOldResultInAvailable + moveDirection;
                    % If requested ResultsFile is NOT available
                    if (iNewResultInAvailable <= 0) || (iNewResultInAvailable > length(iOldAvailableResults))
                        iNewResultInAvailable = [];
                    end
                % === DATA/STUDY NAVIGATION ===
                else
                    % Get all the results available for the new DataFile
                    [tmp__, tmp__, iNewAvailableResults] = bst_get('ResultsForDataFile', sNewData.FileName, iNewStudy);
                    % If equivalent ResultsFile is found for new data
                    if (iOldResultInAvailable <= length(iNewAvailableResults))
                        iNewResultInAvailable = iOldResultInAvailable;
                    else
                        iNewResultInAvailable = [];
                    end
                end

                % If a new results file was found
                if ~isempty(iNewResultInAvailable)
                    iNewResult = iNewAvailableResults(iNewResultInAvailable);
                    newResultsFiles{i} = sNewStudy.Result(iNewResult).FileName;
                else
                    newResultsFiles{i} = [];
                end
            end
        end

%% ===== UPDATE FIGURES =====
        % Process each figure : update or close it
        iFig = 1;
        isDSUnloaded = 0;
        while ~isDSUnloaded && (iDS <= length(GlobalData.DataSet)) && (iFig <= length(GlobalData.DataSet(iDS).Figure))
            Figure = GlobalData.DataSet(iDS).Figure(iFig);
            isFigureRemoved = 0;
            % Update figure appdata
            setappdata(Figure.hFigure, 'SubjectFile', GlobalData.DataSet(iDS).SubjectFile);
            if ~isNoStudyFile
                setappdata(Figure.hFigure, 'StudyFile', GlobalData.DataSet(iDS).StudyFile);
            end
            if ~isNoDataFile
                setappdata(Figure.hFigure, 'DataFile', GlobalData.DataSet(iDS).DataFile);
            end

            % Switch according to figure type
            switch(Figure.Id.Type)
                % === DATA TIME SERIES ===
                case 'DataTimeSeries'
                    if ~isDataFileChanged
                        % Nothing to do
                    elseif ~isempty(Figure.Id.Modality) && (Figure.Id.Modality(1) ~= '$')
                        % Display progress bar
                        bst_progress('start', 'Database navigator', waitMsg);
                        % Update sensors selected in the figure
                        GlobalData.DataSet(iDS).Figure(iFig).SelectedChannels = bst_figures('GetChannelsForFigure', iDS, iFig);
                        % Update TsInfo structure
                        if ~isNoDataFile
                            TsInfo = getappdata(Figure.hFigure, 'TsInfo');
                            if ~isempty(TsInfo) && isfield(TsInfo, 'FileName') && ~isempty(TsInfo.FileName)
                                TsInfo.FileName = GlobalData.DataSet(iDS).DataFile;
                                setappdata(Figure.hFigure, 'TsInfo', TsInfo);
                            end
                        end
                        % Update "isStatic" status
                        setappdata(Figure.hFigure, 'isStatic', (GlobalData.DataSet(iDS).Measures.NumberOfSamples <= 2));
                        % Reload figure
                        bst_figures('ReloadFigures', Figure.hFigure, 0);
                    else
                        isFigureRemoved = 1;
                    end

                % === RESULTS TIME SERIES ===
                case 'ResultsTimeSeries'
                    % Get the results list that is displayed in this figure
                    oldFigRes = getappdata(Figure.hFigure, 'ResultsFiles');
                    % If no or more than one results file : close figure
                    if (length(oldFigRes) ~= 1) || isempty(oldResultsFiles)
                        isFigureRemoved = 1;
                    else
                        % Get new results file for this results file
                        iNewRes = find(file_compare(oldResultsFiles, oldFigRes{1}));
                        newFigRes = newResultsFiles{iNewRes};
                        % If cannot switch
                        if isempty(newFigRes)
                            isFigureRemoved = 1;
                        % Else : update display
                        else
                            % Update figure appdata
                            setappdata(Figure.hFigure, 'ResultsFiles', {newFigRes});
                            setappdata(Figure.hFigure, 'ResultsFile', newFigRes);
                            % Remove figure's unique ID that prevents from writing over this figure
                            GlobalData.DataSet(iDS).Figure(iFig).Id.SubType = '';
                            % Display progress bar
                            bst_progress('start', 'Database navigator', waitMsg);
                            % Call display function again
                            view_scouts({newFigRes}, 'SelectedScouts');
                        end
                    end

                % === RESULTS TIME SERIES ===
                case 'Topography'
                    if isDataFileChanged
                        % Display progress bar
                        bst_progress('start', 'Database navigator', waitMsg);
                        % Call plot function again for new DataFile
                        view_topography(GlobalData.DataSet(iDS).DataFile, Figure.Id.Modality, Figure.Id.SubType);
                    end

                % === 3D FIGURES ===
                case '3DViz'
                    % Get the kind of data represented in this window
                    TessInfo = getappdata(Figure.hFigure, 'Surface');
                    % === PROCESS SURFACES ===
                    for iTess = 1:length(TessInfo)
                        % Get old surface file
                        oldSurfaceFile = TessInfo(iTess).SurfaceFile;
                        % Get new surface file
                        fieldName = ['i' TessInfo(iTess).Name];
                        if (isfield(sNewSubject, fieldName) && ~isempty(sNewSubject.(fieldName)))
                            newSurfaceFile = sNewSubject.Surface(sNewSubject.(fieldName)).FileName;
                        % If old surface type is not available for new subject
                        else
                            % Do not process this surface
                            continue
                        end

                        % === SURFACE CHANGED ===
                        if ~file_compare(oldSurfaceFile, newSurfaceFile)
                            isSurfaceChanged = 1;
                        else
                            isSurfaceChanged = 0;
                        end

                        % === DATA CHANGED ===
                        if ~isempty(TessInfo(iTess).DataSource.Type)
                            % Switch according to type of data on the surface
                            switch (TessInfo(iTess).DataSource.Type)
                                case 'Data'
                                    if isDataFileChanged
                                        % Update Surface description
                                        TessInfo(iTess).DataSource.FileName = sNewStudy.Data(iNewData).FileName;
                                        setappdata(Figure.hFigure, 'Surface', TessInfo);
                                    end
                                case 'Source'
                                    if isResultsFileChanged || isDataFileChanged
                                        % Get new results file for this results file
                                        iNewRes = find(file_compare(oldResultsFiles, TessInfo(iTess).DataSource.FileName));
                                        % If cannot switch
                                        if isempty(iNewRes) || isempty(newResultsFiles{iNewRes(1)})
                                            isFigureRemoved = 1;
                                        % Else : update display
                                        else
                                            % Make sure that only one result is found
                                            iNewRes = iNewRes(1);
                                            newFigRes = newResultsFiles{iNewRes};
                                            % Reset the maximum values for this surface
                                            TessInfo(iTess).DataMinMax = [];
                                            % Update Surface description
                                            TessInfo(iTess).DataSource.FileName = newFigRes;
                                            setappdata(Figure.hFigure, 'Surface', TessInfo);
                                        end
                                    end
                            end
                            if ~isFigureRemoved
                                % Display progress bar
                                bst_progress('start', 'Database navigator', waitMsg);
                                % View new surface / new data on surface
                                view_surface_data(TessInfo(iTess).SurfaceFile, TessInfo(iTess).DataSource.FileName, Figure.Id.Modality);
                            end

                        % === SURFACE CHANGED / NO DATA ===
                        elseif isSurfaceChanged
                            % Display progress bar
                            bst_progress('start', 'Database navigator', waitMsg);
                            % Display new surface
                            view_surface(newSurfaceFile);
                            % Remove previous surface
                            panel_surface('RemoveSurface', Figure.hFigure, iTess);
                        end
                    end
                    % === PROCESS SENSORS ===
                    if isUpdatedChannelFile
                        % Get the elements to be displayed
                        isMarkers = ~isempty(Figure.Handles.hSensorMarkers);
                        isLabels  = ~isempty(Figure.Handles.hSensorLabels);
                        % Update channels display
                        %view_channels(GlobalData.DataSet(iDS).ChannelFile, Figure.Id.Modality, isMarkers, isLabels);
                        figure_3d('ViewSensors', Figure.hFigure, isMarkers, isLabels);
                    end

                % === FREQ DISPLAYS ===
                case {'Timfreq', 'Spectrum', 'Pac', 'Image'}
                    isFigureRemoved = 1;

                % === CONNECTIVITY ===
                case 'Connect'
                    isFigureRemoved = 1;

                % Other types of windows: just close them
                otherwise
                    isFigureRemoved = 1;
            end

            % If close request for this figure
            if isFigureRemoved
                % If it is the last DataSet figure => DataSet will be unloaded
                isDSUnloaded = (length(GlobalData.DataSet(iDS).Figure) <= 1);
                % Close figure
                close(Figure.hFigure);
                % Do not increment the figure => A figure was deleted, so the next figure has the same indice
            else
                % Go to next figure
                iFig = iFig + 1;
            end
        end
    end
    % Unload some datasets
    if ~isempty(iDSclose)
    	bst_memory('UnloadDataSets', iDSclose);
    end
    % Update events lists
    panel_record('UpdatePanel');
    % Hide progress bar
    bst_progress('stop');
    % Release mutex
    mutexNavigator = [];
end

%% ====== CREATE NAVIGATOR MENU ======
function CreateNavigatorMenu(jMenuNavigator) %#ok<DEFNU>
    import java.awt.event.KeyEvent;
    import javax.swing.KeyStroke;
    import org.brainstorm.icon.*;
    % Create items
    jItemPrevSubj = gui_component('MenuItem', jMenuNavigator, [], 'Previous subject',   IconLoader.ICON_PREVIOUS_SUBJECT,   [], @(h,ev)DbNavigation('PreviousSubject'));
    jItemNextSubj = gui_component('MenuItem', jMenuNavigator, [], 'Next subject',       IconLoader.ICON_NEXT_SUBJECT,       [], @(h,ev)DbNavigation('NextSubject'));
    jMenuNavigator.addSeparator();
    jItemPrevCond = gui_component('MenuItem', jMenuNavigator, [], 'Previous condition', IconLoader.ICON_PREVIOUS_CONDITION, [], @(h,ev)DbNavigation('PreviousCondition'));
    jItemNextCond = gui_component('MenuItem', jMenuNavigator, [], 'Next condition',     IconLoader.ICON_NEXT_CONDITION,     [], @(h,ev)DbNavigation('NextCondition'));
    jMenuNavigator.addSeparator();
    jItemPrevData = gui_component('MenuItem', jMenuNavigator, [], 'Previous data file', IconLoader.ICON_PREVIOUS_DATA,      [], @(h,ev)DbNavigation('PreviousData'));
    jItemNextData = gui_component('MenuItem', jMenuNavigator, [], 'Next data file',     IconLoader.ICON_NEXT_DATA,          [], @(h,ev)DbNavigation('NextData'));
    % Set keyboards shortcuts
    jItemNextSubj.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F1), 0)); % F1
    jItemNextCond.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F2), 0)); % F2
    jItemNextData.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F3), 0)); % F3
    jItemPrevSubj.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F1), KeyEvent.SHIFT_MASK)); % SHIFT+F1
    jItemPrevCond.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F2), KeyEvent.SHIFT_MASK)); % SHIFT+F2
    jItemPrevData.setAccelerator(KeyStroke.getKeyStroke(int32(KeyEvent.VK_F3), KeyEvent.SHIFT_MASK)); % SHIFT+F3
end


function ltfatmex(varargin)
%LTFATMEX   Compile Mex/Oct interfaces
%   Usage:  ltfatmex;
%           ltfatmex(...);
%
%   `ltfatmex` compiles the C backend in order to speed up the execution of
%   the toolbox. The C backend is linked to Matlab and Octave through Mex
%   and Octave C++ interfaces.
%   Please see INSTALL-Matlab or INSTALL-Octave for the requirements.
%
%   The action of `ltfatmex` is determined by one of the following flags:
%
%     'compile'  Compile stuff. This is the default.
%
%     'clean'    Removes the compiled functions.
%
%     'test'     Run some small tests that verify that the compiled
%                functions work.
%
%   The target to work on is determined by on of the following flags.
%
%   General LTFAT:
%
%     'lib'      Perform action on the LTFAT C library.
%
%     'mex'      Perform action on the mex / oct interfaces.
%
%     'pbc'      Perform action on the PolyBoolClipper code for use with MULACLAB
%
%     'auto'     Choose automatically which targets to work on from the
%                previous ones based on the operation system etc. This is
%                the default.
%
%   Block-processing framework related:
%
%     'playrec'  Perform action on the playrec code for use with real-time
%                block streaming framework.
%
%     'java'     Perform compilation of JAVA classes into the bytecode.
%                The classes makes the GUI for the blockproc. framework.
%
%   Other:
%
%      'verbose' Print action details.
%
%      'debug'   Build a debug version. This will disable compiler
%                optimizations and include debug symbols.
%

%   AUTHOR : Peter L. Søndergaard.
%   TESTING: NA
%   REFERENCE: NA

% Verify that comp_pgauss is in path
if ~exist('comp_pgauss','file')
  disp(' ');
  disp('--- LTFAT - The Linear Time Frequency Analysis toolbox. ---');
  disp(' ')
  disp('To start the toolbox, call LTFATSTART as the first command.');
  disp(' ');
  return;
end;

bp=mfilename('fullpath');
bp=bp(1:end-length(mfilename));

definput.flags.target={'auto','lib','mex','pbc','playrec','java','blockproc'};
% This has to be also defined
definput.flags.comptarget={'release','debug'};
definput.flags.command={'compile','clean','test'};
definput.flags.verbosity={'noverbose','verbose'};
definput.flags.mexapi={'auto','legacy'};
definput.keyvals.mexfile = [];
definput.keyvals.jobs = [];

s = which('ltfatarghelper');
if strcmpi(mexext,s(end-numel(mexext)+1:end))
   % We must avoid calling corrupt ltfatarghelper.mexext
   % The following must not fail
   try
       ltfatarghelper({},struct(),{});
   catch
       if any(strcmpi('verbose',varargin))
           fprintf('Removing corrupt ltfatarghelper.%s',mexext);
       end
       delete(s);
       % Now there is just a ltfatarghelper.m
   end
end

[flags,kv]=ltfatarghelper({},definput,varargin);

% Remember the current directory.
curdir=pwd;

% Compile backend lib?
do_lib  = flags.do_lib || flags.do_auto;
% Compile MEX/OCT interfaces?
do_mex  = flags.do_mex || flags.do_auto;
% Compile MEX PolyBoolClipper.mex
do_pbc  = flags.do_pbc || flags.do_auto;
% Compile MEX playrec.mex... using Portaudio library.
% (relevant only for the bloc processing framework)
do_playrec  = flags.do_playrec || flags.do_blockproc;
% Compile Java classes containing GUI for the bloc proc. framework.
do_java  = flags.do_java || flags.do_blockproc;

if isoctave
	extname='oct';
    ext='oct';
else
    extname='mex';
    ext=mexext;
end;

fftw_lib_names = {'fftw3', 'fftw3f' };

% Check if we are on Windows
if ispc
    makefilename='Makefile_mingw';
    make_exe = 'mingw32-make';
    sharedExt = 'dll';
    %fftw_lib_names = {'fftw3', 'fftw3f' };
    % The pre-compiled Octave for Windows comes only in 32bit version (3.6.4)
    % We use different Makefiles
    if isoctave
      makefilename='Makefile_mingwoct';
    end
end;

% Check if we are on Unix-type system
if isunix
   makefilename='Makefile_unix';
   make_exe = 'make';
   sharedExt = 'so';
end;

% Check if we are on Mac
if ismac
   makefilename='Makefile_mac';
   make_exe = 'make';
   sharedExt = 'dylib';
end;

clear mex;
% -------------- Handle cleaning --------------------------------
if flags.do_clean

  if do_lib
    disp('========= Cleaning libltfat ===============');
    cd([bp,'lib',filesep,'ltfatcompat']);
    callmake(make_exe,makefilename,'target','clean',flags.verbosity);
    %[status,result]=system([make_exe, ' -f ',makefilename,' clean']);
    %disp('Done.');
  end;

  if do_mex
    fprintf('========= Cleaning %s interfaces =========\n', upper(extname));
     cd([bp,extname]);
     callmake(make_exe,makefilename,'target','clean','ext',ext,flags.verbosity);
     %[status,result]=system([make_exe, ' -f ',makefilename,' clean',...
     %                ' EXT=',ext]);
  end;

  if do_pbc
     % Use the correct makefile
     makefilename_pbc = makefilename;
     if isoctave
       if ~strcmpi(makefilename(end-2:end),ext)
          makefilename_pbc = [makefilename,ext];
       end
    end

    disp('========= Cleaning PolyBoolClipper ====================');
    cd([bp,'thirdparty',filesep,'polyboolclipper']);
    clear mex;
    callmake(make_exe,makefilename_pbc,'target','clean','ext',mexext,flags.verbosity);
    %[status,result]=system([make_exe, ' -f ',makefilename,' clean',' EXT=',mexext]);
  end;

  if do_playrec
     % Use the correct makefile
     if isoctave
       if ~strcmpi(makefilename(end-2:end),ext)
          makefilename = [makefilename,ext];
       end
    end

    disp('========= Cleaning PLAYREC ================');
    cd([bp,'thirdparty',filesep,'Playrec']);
    clear mex;
    %[status,result]=system([make_exe, ' -f ',makefilename,' clean',' EXT=',mexext]);
    callmake(make_exe,makefilename,'target','clean','ext',mexext,flags.verbosity);
  end;

  if do_java
    disp('========= Cleaning JAVA ================');
    cd([bp,'blockproc',filesep,'java']);
    %[status,result]=system([make_exe,' clean']);
    callmake(make_exe,[],'target','clean',flags.verbosity);
  end;

  cd(curdir);
end;

% -------------- Handle compiling  --------------------------------

if flags.do_compile
  if do_lib
    disp('========= Compiling libltfat ==============');
    cd([bp,'lib',filesep,'ltfatcompat']);
    clear mex;

    dfftw = ['-l',fftw_lib_names{1}];
    sfftw = ['-l',fftw_lib_names{2}];
    if ispc && ~isoctave
        fftw_lib_found_names = searchfor(bp,fftw_lib_names,sharedExt);
        if ~isempty(fftw_lib_found_names)
           dfftw = ['-l:',fftw_lib_found_names{1}];
           sfftw = ['-l:',fftw_lib_found_names{2}];
       end
    end
      % DFFTW and SFFTW are not used in the unix_makefile
      [status,result] = callmake(make_exe,makefilename,'matlabroot','arch',...
                       'dfftw',dfftw,'sfftw',sfftw,flags.verbosity,...
                       'comptarget',flags.comptarget,'jobs',kv.jobs);
      if(~status)
        disp('Done.');
      else
        error('Failed to build LTFAT libs:\n %s',result);
      end
    %end;
  end;

  if do_mex
    fprintf('========= Compiling %s interfaces ========\n', upper(extname));
    clear mex;
    cd([bp,extname]);

    dfftw = ['-l',fftw_lib_names{1}];
    sfftw = ['-l',fftw_lib_names{2}];
    if ~isoctave
        fftw_lib_found_names = searchfor(bp,fftw_lib_names,sharedExt);
        if ~isempty(fftw_lib_found_names)
            if ~ismac
               dfftw = ['-l:',fftw_lib_found_names{1}];
               sfftw = ['-l:',fftw_lib_found_names{2}];
            else
                % We need a full path here.
               dfftw = [binDirPath(),filesep,fftw_lib_found_names{1}];
               sfftw = [binDirPath(),filesep,fftw_lib_found_names{2}];
            end
       end
    end

    [status,result] = callmake(make_exe,makefilename,'matlabroot','arch',...
                      'ext',ext,'dfftw',dfftw,'sfftw',sfftw,...
                      flags.verbosity,'target',kv.mexfile,...
                      'comptarget',flags.comptarget,'jobs',kv.jobs,...
                      flags.mexapi);

    if(~status)
      disp('Done.');
    else
      error('Failed to build %s interfaces: %s \n',upper(extname),result);
    end
  end;

  if do_pbc
    makefilename_pbc = makefilename;
    if isoctave
       if ~strcmpi(makefilename(end-2:end),ext)
          makefilename_pbc = [makefilename,ext];
       end
    end

    disp('========= Compiling PolyBoolClipper ===================');
    % Compile PolyBoolClipper mex file for use with mulaclab
    cd([bp,'thirdparty',filesep,'polyboolclipper']);
    clear mex;
    [status,result] = callmake(make_exe,makefilename_pbc,'matlabroot','arch',...
                      'ext',ext,flags.verbosity);

    if(~status)
      disp('Done.');
    else
      error('Failed to build PlyBoolClipper:\n %s',result);
    end
  end;
  if do_playrec
    disp('========= Compiling PLAYREC ===============');
    cd([bp,'thirdparty',filesep,'Playrec']);
    clear mex;
    % Compile the Playrec (interface to portaudio) for the real-time block-
    % stream processing

     portaudioLib = '-lportaudio';

     binArchPath = binDirPath();
       playrecRelPath = ['thirdparty',filesep,'Playrec'];

       foundPAuser = [];
       if ispc
          foundPAuser = dir([bp,playrecRelPath,filesep,'*portaudio*',sharedExt,'*']);
       end

       foundPAmatlab = [];
       if ~isoctave
          % Check if portaudio library is present in the Matlab installation
          foundPAmatlab = dir([binArchPath,filesep,'*portaudio*',sharedExt,'*']);
       end

       if ~isempty(foundPAuser)
          if numel(foundPAuser)>1
             error('Ambiguous portaudio libraries in %s. Please leave just one.',playrecRelPath);
          end
          foundPAuser = foundPAuser(1).name;

       elseif ~isempty(foundPAmatlab)
          if numel(foundPAmatlab)>1
             if ispc
                %This should not happen on Windows
                %Use the first one on Linux
                error('Ambiguous portaudio libraries in %s.',binArchPath);
             end
          end
             foundPAmatlab = foundPAmatlab(1).name;
       else
          if ispc && isoctave || ispc
          error(['Portaudio not found. Please download Portaudio http://www.portaudio.com\n',...
                 'and build it as a shared library and copy it to the\n',...
                 '%s directory. \n'],playrecPath);
          end
       end

    if isoctave
       if ~strcmpi(makefilename(end-2:end),ext)
          makefilename = [makefilename,ext];
       end
    end

    doPAuser = ~isempty(foundPAuser);
    doPAmatlab = ~isempty(foundPAmatlab) && ~doPAuser;

    if doPAmatlab
       if ismac
          % Full path is needed on MAC since
          % clang does not understand -l: prefix.
          portaudioLib = [binArchPath,filesep,foundPAmatlab];
       else
          portaudioLib = ['-l:',foundPAmatlab];
       end
       fprintf('    ...using %s from Matlab installation.\n',foundPAmatlab);
    elseif doPAuser
        portaudioLib = ['-l:',foundPAuser];
        fprintf('   ...using %s from ltfat%s%s.\n',...
                  foundPAuser,filesep,playrecRelPath);
    end

    [status,result] = callmake(make_exe,makefilename,'matlabroot','arch',...
                      'ext',mexext,'portaudio',portaudioLib,'extra','HAVE_PORTAUDIO',...
                      flags.verbosity);
    if(~status)
      disp('Done.');
    else
      error('Failed to build PLAYREC:\n %s',result);
    end
  end;

  if do_java
    disp('========= Compiling JAVA classes ===================');
    % Compile the JAVA classes
    cd([bp,'blockproc',filesep,'java']);
    clear mex;
    [status,result] = callmake(make_exe,'Makefile',flags.verbosity);
    if(~status)
      disp('Done.');
    else
      error('Failed to build JAVA classes:\n %s',result);
    end
  end;
end;

% -------------- Handle testing ---------------------------------------

if flags.do_test

  if do_mex

    fprintf('========= Testing %s interfaces ==========\n', extname);
    fprintf('1.: Test if comp_pgauss.%s was compiled: ',ext);
    fname=['comp_pgauss.',ext];
    if exist(fname,'file')
      disp('SUCCESS.');
    else
      disp('FAILED.');
    end;

    fprintf('2.: Test if pgauss executes:              ');
    pgauss(100);
    % If the execution of the script makes it here, we know that pgauss
    % did not crash the system, so we can just print success. Same story
    % with the following entries.
    disp('SUCCESS.');

    fprintf('3.: Test if fftreal executes:             ');
    fftreal(randn(10,1),10);
    disp('SUCCESS.');

    fprintf('4.: Test if dgt executes:                 ');
    dgt(randn(12,1),randn(12,1),3,4);
    disp('SUCCESS.');

  end;

end;

% Jump back to the original directory.
cd(curdir);

function status = filesExist(filenames)
   if(~iscell(filenames))
      filenames={filenames};
   end
   for ii=1:length(filenames)
      filename = filenames{ii};
      if(~exist(filename,'file'))
         error('%s: File %s not found.',mfilename,filename);
      end
   end

function found_files=searchfor(bp,files,sharedExt)

found_names = {};
      if ispc
         for ii=1:numel(files)
            % Search the ltfat/mex lib
            L = dir([bp,'mex',filesep,'*',files{ii},'*.',sharedExt]);
            if isempty(L)
                error(['%s: %s could not be found in ltfat/mex subdir.',...
                       ' Please download the FFTW dlls and install them.'],...
                      upper(mfilename),files{ii});
            end
            found_files{ii} = L(1).name;
            fprintf('   ...using %s from ltfat/mex.\n',L(1).name);
         end
      elseif isunix
          for ii=1:numel(files)
             L = dir([binDirPath(),filesep,'*',files{ii},'*.',sharedExt,'*']);

             if isempty(L)
                 error('%s: Matlab FFTW libs were not found. Strange.',...
                      upper(mfilename));
             end

             found_files{ii} = L(1).name;

             fprintf('   ...using %s from Matlab installation.\n',...
                     found_files{ii});
          end

      end;

function path=binDirPath()
path = [matlabroot,filesep,'bin',filesep,computer('arch')];

function [status,result]=callmake(make_exe,makefilename,varargin)
%CALLMAKE
%   Usage:  callmake(make_exe,makefilename);
%           callmake(make_exe,makefilename,'matlabroot',matlabroot,...);
%
%   `callmake(make_exe,makefilename)` is a platform independent wrapper for
%   calling the make command `make_exe` on `makefilename` file. When
%   `makefilename` is missing or is empty, the default `Makefile` file is
%   used.
%
%   `callmake(...,'target',target)` used `target` from the makefile.
%
%   Flags:
%
%       matlabroot:   Pass MATLABROOT=matlabroot variable to the makefile.
%
%       arch:         Pass ARCH=computer('arch') variable to the makefile.
%
%   Key-value parameters:
%
%       ext:          Pass EXT variable to the makefile.
%
%       portaudio:    Pass PORTAUDIO variable to the makefile.
%
%       dfftw:        Pass DFFTW variable to the makefile.
%
%       sfftw:        Pass SFFTW variable to the makefile.

  if nargin < 2 || isempty(makefilename)
     systemCommand = make_exe;
  else
     systemCommand = [make_exe, ' -f ',makefilename];
  end
  definput.flags.matlabroot={'none','matlabroot'};
  definput.flags.arch={'none','arch'};
  definput.keyvals.ext=[];
  definput.keyvals.dfftw=[];
  definput.keyvals.sfftw=[];
  definput.keyvals.target=[];
  definput.keyvals.comptarget=[];
  definput.keyvals.portaudio=[];
  definput.keyvals.extra=[];
  definput.keyvals.jobs =[];
  definput.flags.targetapi={'auto','legacy'};
  definput.flags.verbosity={'noverbose','verbose'};
  [flags,kv]=ltfatarghelper({},definput,varargin);

  if flags.do_matlabroot
     systemCommand = [systemCommand, ' MATLABROOT=','"',matlabroot,'"'];
  end

  if flags.do_arch
     systemCommand = [systemCommand, ' ARCH=',computer('arch')];
  end

  if ~isempty(kv.ext)
     systemCommand = [systemCommand, ' EXT=',kv.ext];
  end

  if ~isempty(kv.dfftw)
     systemCommand = [systemCommand, ' DFFTW=',kv.dfftw];
  end

  if ~isempty(kv.sfftw)
     systemCommand = [systemCommand, ' SFFTW=',kv.sfftw];
  end

  if ~isempty(kv.portaudio)
     systemCommand = [systemCommand, ' PORTAUDIO=',kv.portaudio];
  end

  if ~isempty(kv.comptarget) && ~strcmpi(kv.comptarget,'release')
     systemCommand = [systemCommand, ' COMPTARGET=',kv.comptarget];
  end

  if ~isempty(kv.extra)
     systemCommand = [systemCommand, ' ',kv.extra,'=1'];
  end

  if ~isempty(kv.jobs)
      if isnumeric(kv.jobs)
        systemCommand = [systemCommand,sprintf(' -j%d',kv.jobs)];
      elseif ischar(kv.jobs)
        systemCommand = [systemCommand,sprintf(' -j%s',kv.jobs)];
      end
  end

  if ~isoctave && ~flags.do_legacy
      matlabversion = version('-release');
      if str2double(matlabversion(1:4)) >= 2018
          systemCommand = [systemCommand,' POST2018a=1'];
      end
  end

  if flags.do_verbose
      fprintf('Calling:\n    %s\n\n',systemCommand);
  end

  if ~isempty(kv.target)
       if ~iscell(kv.target), kv.target = {kv.target}; end
       for ii = 1:numel(kv.target)
           [status,result]=system([systemCommand,' ',kv.target{ii}]);
       end
  else
        [status,result]=system(systemCommand);
  end

  if flags.do_verbose && ~isoctave
     disp(result);
  end


function sfun_keyboard_input_v1_2(block)
% modified from sfun_keyboard_input_v1_01.m of Marc Compere by Emanuele Ruffaldi
% created : 17 June 2003
% modified: 20 June 2003
% created: 19 May 2009 => Level 2 and terminate
%

% Level-2 M file S-Function with keyboard
%   Copyright 1990-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.1 $

  setup(block);

%endfunction

function setup(block)

  block.NumDialogPrms  = 0;
  block.NumInputPorts  = 0;
  block.NumOutputPorts = 2;

  block.OutputPort(1).Dimensions       = 1;
  block.OutputPort(1).SamplingMode = 'Sample';
  block.OutputPort(1).DatatypeID  = 0;
  block.OutputPort(2).Dimensions       = 1;
  block.OutputPort(2).SamplingMode = 'Sample';
  block.OutputPort(2).DatatypeID  = 0;
  block.SampleTimes = [-1, 0];
%end

  %% Register methods
%  block.RegBlockMethod('CheckParameters', @CheckPrms);
  block.RegBlockMethod('PostPropagationSetup',    @DoPostPropSetup);
  block.RegBlockMethod('InitializeConditions',    @InitConditions);
  block.RegBlockMethod('Outputs',                 @Output);
  block.RegBlockMethod('Update',                  @Update);
  block.RegBlockMethod('Terminate',                 @Terminate);
%endfunction

function DoPostPropSetup(block)

  %% Setup Dwork
  block.NumDworks = 3;
  block.Dwork(1).Name = 'key';
  block.Dwork(1).Dimensions      = 1;
  block.Dwork(1).DatatypeID      = 0;
  block.Dwork(1).Complexity      = 'Real';
  block.Dwork(1).UsedAsDiscState = true;

  block.Dwork(2).Name = 'fig';
  block.Dwork(2).Dimensions      = 1;
  block.Dwork(2).DatatypeID      = 0;
  block.Dwork(2).Complexity      = 'Real';
  block.Dwork(2).UsedAsDiscState = false;

  block.Dwork(3).Name = 'new';
  block.Dwork(3).Dimensions      = 1;
  block.Dwork(3).DatatypeID      = 0;
  block.Dwork(3).Complexity      = 'Real';
  block.Dwork(3).UsedAsDiscState = true;
%endfunction

function InitConditions(block)

  %% Initialize Dwork
  block.Dwork(1).Data = 0;
   handle.figure=findobj('type','figure','Tag','keyboard input figure');

   if isempty(handle.figure)
      % 'position' args -> [left, bottom, width, height]
      handle.figure=figure('position',[100 100 400 200],...
                           'WindowStyle','Modal',...
                           'Name','Keyboard Input',...
                           'Color',get(0,'DefaultUicontrolBackgroundColor')); %,...
                           %'HandleVisibility','callback');
      %handle.figure=figure('position',[800 620 400 300]);
      %handle.figure=figure('position',[800 620 400 300],'WindowButtonDownFcn',@myCallback)
      %handle.figure=figure('position',[800 620 400 300],'WindowButtonMoveFcn',@myCallback_move,'WindowButtonDownFcn',@myCallback_clickdown)
      set(handle.figure,'Tag','keyboard input figure');

      % Make the OFF button (position args->[left bottom width height])
      handle.offbutton = uicontrol(handle.figure,...
          'Style','pushbutton',...
          'Units','characters',...
          'Position',[5 5 46 2],...
          'String','Disable exclusive figure-keyboard input',...
          'Callback',{@turn_modal_off,handle});

      % Make the ON button (position args->[left bottom width height])
      handle.onbutton = uicontrol(handle.figure,...
          'Style','pushbutton',...
          'Units','characters',...
          'Position',[5 1 46 2],...
          'String','Re-enable exclusive figure-keyboard input',...
          'Callback',{@turn_modal_on,handle});

   else, % reset the figure to 'modal' to continue accepting keyboard input
      set(handle.figure,'WindowStyle','Modal')
   end
  block.Dwork(2).Data = handle.figure;

%endfunction

function Output(block)

  block.OutputPort(1).Data = block.Dwork(1).Data;
  block.OutputPort(2).Data = block.Dwork(3).Data;

%endfunction

function Update(block)

   handle.figure = block.Dwork(2).Data;
   %handle = get(handle.figure,'userdata');

   current_char=get(handle.figure,'CurrentCharacter'); % a single character, like 'b'

   % update the grahics object
   %set(handle.point,'Xdata',[x(1) x(1)],'Ydata',[x(2) x(2)],'Zdata',[-1 +1]);

   % conditionally update the (numeric) state
   if ~isempty(current_char)
      block.Dwork(1).Data =double(current_char);
      block.Dwork(3).Data = 1;
	   % reset 'CurrentCharacter' so if user lifts up from key, this is noticed
	   set(handle.figure,'CurrentCharacter',char(0)) % the plus key is the only key that may be
   else,
      block.Dwork(3).Data = 0;
   end

                                   % pressed, but when the user stops, is not noticed

   % notes:
   %    - use sprintf() to convert string -> number contained in a string (or character) variable
   %    - use char() to convert floating point number -> ascii character
   %    - use str2num() to convert the string-number into a (double) floating point number
   %    - use str2num() with char() to convert a number-string into a char-string
   %         char(97) == char(str2num('97')) --> 'a'
   %    - use num2str() to convert a (double) number into the same number but contained
   %      in a string variable
   % For example:
   %    tmp='a';                        % assign a string
   %    tmp_num=sprintf('%i',tmp)       % convert that string into a number contained in a string variable, tmp_num
   %        (tmp_num is the string containing the characters '97')
   %    tmp_char=char(str2num(tmp_num)) % convert that string variable back into a number, then into the original string

%endfunction

% Callback for turning Modal OFF
function turn_modal_off(obj,eventdata,handle)
%disp('turn_modal_off:')
%handle
set(handle.figure,'WindowStyle','Normal')
%end

% Callback for turning Modal ON
function turn_modal_on(obj,eventdata,handle)
%disp('turn_modal_on:')
%handle
set(handle.figure,'WindowStyle','Modal')
%end

% % Callback for 'WindowButtonMoveFcn' in figure
% function myCallback_move(obj,eventdata)
% str=sprintf('\tWindowButtonMoveFcn callback executing');disp(str)
% end
%
% % Callback for 'WindowButtonDownFcn' in figure
% function myCallback_clickdown(obj,eventdata)
% str=sprintf('\t\tWindowButtonDownFcn callback executing');disp(str)
% end

function Terminate(block)
   handle.figure = block.Dwork(2).Data;
   if handle.figure ~= 0
       close (handle.figure);
   end


function pass = test_toFileIVP(pref)
%TEST_TOFILEIVP   Test exporting all IVP demos to an m-file.
%
% This test only checks whether nothing breaks, it does not try to solve the
% problems.

% Find the folders which demos are stored in. The chebguiDemos folder lives in
% the trunk folder, find the path of the Chebfun trunk.
trunkPath = chebfunroot();

% Append directory information
bvppath = fullfile(trunkPath, 'chebguiDemos', 'ivpdemos');

% Set up file to exporting to
tempPath = fullfile(trunkPath, 'tests', 'chebgui');
tempFileName = 'tempExporterTest.m';

% Obtain directory information:
D = dir(bvppath);

% Create a BVP exporter object:
exporter = chebguiExporter.constructor('ivp');

% Wrap the test in a try-catch statement to ensure we don't files hanging around
% in the test folder:
try

    % Loop through the demos and export
    for demoCounter = 3:length(D)

        % The path to the .guifile
        demoPath = fullfile(bvppath, D(demoCounter).name);

        % Load demo
        cg = chebgui.demo2chebgui(demoPath);

        % Export the demo!
        toFile(exporter, cg, tempFileName, tempPath)
    end

    % Got here without crashing == success!
    pass = 1;

catch
    % Something went wrong == fail :(
    pass = 0;
end

% Delete the temporary file we wrote to:
delete(fullfile(tempPath, tempFileName))

end


%% Sachs Model
%
%%