% POP_STUDY  - create a new STUDY set structure defining a group of related EEG datasets.
%                The STUDY set also contains information about each of the datasets: the
%                subject code, subject group, experimental condition, and session. This can
%                be provided interactively in a pop-up window or be automatically filled
%                in by the function. Defaults: Assume a different subject for each
%                dataset and only one condition; leave subject group and session fields
%                empty. Additional STUDY information about the STUDY name, task and
%                miscellaneous notes can also be saved in the STUDY structure.
% Usage:
%  >> [ STUDY ALLEEG ] = pop_study([],[], 'gui', 'on'); % create new study interactively
%  >> [ STUDY ALLEEG ] = pop_study(STUDY, ALLEEG, 'gui', 'on'); % edit study interactively
%  >> [ STUDY ALLEEG ] = pop_study(STUDY, ALLEEG, 'key', 'val', ...); % edit study
%
% Optional Inputs:
%   STUDY                - existing study structure.
%   ALLEEG               - vector of EEG dataset structures to be included in the STUDY.
%
% Optional Inputs:
%   All "'key', 'val'" inputs of STD_EDITSET may be used.
%
% Outputs:
%   STUDY                - new STUDY set comprising some or all of the datasets in
%                          ALLEEG, plus other information about the experiments.
%   ALLEEG               - an updated ALLEEG structure including the STUDY datasets.
%
% Graphic interface buttons:
%  "STUDY set name"      - [edit box] name for the STUDY structure {default: ''}
%  "STUDY set task name" - [edit box] name for the task performed by the subject {default: ''}
%  "STUDY set notes"     - [edit box] notes about the experiment, the datasets, the STUDY,
%                          or anything else to store with the rest of the STUDY information
%                          {default: ''}
%  "dataset filename/browse" - Load dataset from specified filename. It may be
%                          an EEGLAB dataset or any file supported by File-IO (if
%                          it is installed from the plugin manager).
%  "subject"             - [edit box] subject code associated with the dataset. If no
%                          subject code is provided, each dataset will assumed to be from
%                          a different subject {default: 'S1', 'S2', ..., 'Sn'}
%  "session"             - [edit box] dataset session. If no session information is
%                          provided, all datasets that belong to one subject are assumed to
%                          have been recorded within one session {default: []}
%  "run"                 - [edit box] dataset run. If no run information is
%                          provided, all datasets that belong to one subject are assumed to
%                          have been recorded within one run {default: []}
%  "condition"           - [edit box] dataset condition. If no condition code is provided,
%                          all datasets are assumed to be from the same condition {default:[]}
%  "group"               - [edit box] the subject group the dataset belongs to. If no group
%                          is provided, all subjects and datasets are assumed to belong to
%                          the same group. {default: []}
%  "Save this STUDY set to disk file" - [check box] If checked, save the new STUDY set
%                          structure to disk. If no filename is provided, a window will
%                          pop up to ask for it.
%
% See also: std_editset, POP_LOADSTUDY, POP_PRECLUST, POP_CLUST
%
% Authors: Arnaud Delorme, Hilit Serby, Scott Makeig, SCCN, INC, UCSD, July 22, 2005

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, July 22, 2005, arno@sccn.ucsd.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.

% Coding notes: Useful information on functions and global variables used.

function [STUDY, ALLEEG, com]  = pop_study(STUDY, ALLEEG, varargin)

com = '';

if nargin < 1
    help pop_study;
    return;
end

% type of call (gui, script or internal)
% --------------------------------------
mode = 'internal_command';
if ~ischar(STUDY) %initial settings
    mode = 'script';
    if nargin > 2
        for index = 1:length(varargin)
            if ischar(varargin{index})
                if strcmpi(varargin{index}, 'gui')
                    mode = 'gui';
                end
            end
        end
    end
end

if isempty(STUDY)
    newstudy       = 1;
    STUDY.name     = '';
    STUDY.task     = '';
    STUDY.notes    = '';
    STUDY.filename = '';
    STUDY.cluster  = [];
    STUDY.history = 'STUDY = [];';
else
    newstudy       = 0;
end

if strcmpi(mode, 'script') % script mode
    [STUDY, ALLEEG] = std_editset(STUDY, ALLEEG, varargin{:});
    return;
elseif strcmpi(mode, 'gui') % GUI mode

    % check events
    fieldList = { 'condition' 'group' 'subject' 'session' 'run' };
    if ~isempty(ALLEEG) && isstruct(ALLEEG) && isfield(ALLEEG, 'event')
        for iField = 1:length(fieldList)
            if any(cellfun(@(x)isfield(x, fieldList{iField}), {ALLEEG.event}))
                fprintf(2, 'Rename field "%s" in datasets'' event structure as it conflicts with the STUDY field bearing the same name\n', fieldList{iField});
            end
        end
    end

    % show warning if necessary
    % -------------------------
    if isreal(ALLEEG)
        if ALLEEG == 0
            res = questdlg2( strvcat('Datasets currently loaded will be removed from EEGLAB memory.', ...
                                     'Are you sure you want to continue?'), ...
                                     'Discard loaded EEGLAB datasets?', 'Cancel', 'Yes', 'Yes');
            if strcmpi(res, 'cancel'), return; end
        end
        ALLEEG = [];
        alleegEmpty = 1;
    else
        alleegEmpty = 0;
    end

    % set initial datasetinfo
    % -----------------------
    if isfield(STUDY, 'datasetinfo')
        datasetinfo = STUDY.datasetinfo;
        different = 0;
        for k = 1:length(ALLEEG)
            if ~strcmpi(datasetinfo(k).filename, ALLEEG(k).filename), different = 1; break; end
            if ~strcmpi(datasetinfo(k).subject,   ALLEEG(k).subject),   different = 1; break; end
            if ~strcmpi(datasetinfo(k).condition, ALLEEG(k).condition), different = 1; break; end
            if ~strcmpi(char(datasetinfo(k).group), char(ALLEEG(k).group)),     different = 1; break; end
            if ~isequal(datasetinfo(k).session, ALLEEG(k).session),             different = 1; break; end
            if ~isequal(datasetinfo(k).run, ALLEEG(k).run),                     different = 1; break; end
        end
        if different
            info = 'from_STUDY_different_from_ALLEEG';
        else
            info = 'from_STUDY';
        end
        if ~isfield(datasetinfo, 'comps');
            datasetinfo(1).comps = [];
        end
    else
        info = 'from_ALLEEG';
        if length(ALLEEG) > 0
            datasetinfo(length(ALLEEG)).filename  = [];
            datasetinfo(length(ALLEEG)).filepath  = [];
            datasetinfo(length(ALLEEG)).subject   = [];
            datasetinfo(length(ALLEEG)).session   = [];
            datasetinfo(length(ALLEEG)).run       = [];
            datasetinfo(length(ALLEEG)).condition = [];
            datasetinfo(length(ALLEEG)).group     = [];
            for k = 1:length(ALLEEG)
                datasetinfo(k).filename  = ALLEEG(k).filename;
                datasetinfo(k).filepath  = ALLEEG(k).filepath;
                datasetinfo(k).subject   = ALLEEG(k).subject;
                datasetinfo(k).session   = ALLEEG(k).session;
                datasetinfo(k).run       = ALLEEG(k).run;
                datasetinfo(k).condition = ALLEEG(k).condition;
                datasetinfo(k).group     = ALLEEG(k).group;
            end
            if ~isfield(datasetinfo, 'comps');
                datasetinfo(1).comps = [];
            end
        else
            datasetinfo = [];
        end
    end

    nextpage    = 'pop_study(''nextpage'', gcbf);';
    prevpage    = 'pop_study(''prevpage'', gcbf);';
    delset      = 'pop_study(''clear'',    gcbf, get(gcbo, ''userdata''));';
    loadset     = 'pop_study(''load'',     gcbf, get(guiind, ''userdata''), get(guiind, ''string'')); clear guiind;';
    loadsetedit = [ 'guiind = gcbo;' loadset ];
    subcom      = 'pop_study(''subject''  , gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    sescom      = 'pop_study(''session''  , gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    runcom      = 'pop_study(''run''      , gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    condcom     = 'pop_study(''condition'', gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    grpcom      = 'pop_study(''group''    , gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    compcom     = 'pop_study(''component'', gcbf, get(gcbo, ''userdata''), get(gcbo, ''string''));';
    cb_del      = 'pop_study(''delclust'' , gcbf, ''showwarning'');';
    cb_dipole   = 'pop_study(''dipselect'', gcbf, ''showwarning'');';

	browsestudy = [ '[filename, filepath] = uiputfile2(''*.study'', ''Use existing STUDY set to import dataset information -- pop_study()''); ' ...
                      'set(findobj(''parent'', gcbf, ''tag'', ''usestudy_file''), ''string'', [filepath filename]);' ];
	saveSTUDY = [ 'set(findobj(''parent'', gcbf, ''userdata'', ''save''), ''enable'', fastif(get(gcbo, ''value'')==1, ''on'', ''off''));' ];
	browsesave = [ '[filename, filepath] = uiputfile2(''*.study'', ''Save STUDY with .study extension -- pop_clust()''); ' ...
                   'if filename ~= 0,' ...
                   '   set(findobj(''parent'', gcbf, ''tag'', ''studyfile''), ''string'', [filepath filename]);' ...
                   'end;' ...
                   'clear filename filepath;' ];

    texthead = fastif(newstudy, 'Create a new STUDY set', 'Edit STUDY set information - remember to save changes');
	guispec = { ...
        {'style' 'text' 'string' texthead 'FontWeight' 'Bold' 'HorizontalAlignment' 'center'} ...
        {} {'style' 'text' 'string' 'STUDY set name:' } { 'style' 'edit' 'string' char(STUDY.name) 'tag' 'study_name' } ...
        {} {'style' 'text' 'string' 'STUDY set task name:' } { 'style' 'edit' 'string' char(STUDY.task) 'tag' 'study_task' } ...
        {} {'style' 'text' 'string' 'STUDY set notes:' } { 'style' 'edit' 'string' char(STUDY.notes) 'tag' 'study_notes' } {}...
        {} ...
        {'style' 'text' 'string' 'dataset filename' 'userdata' 'addt'} {'style' 'text' 'string' 'browse' 'userdata' 'addt'} ...
        {'style' 'text' 'string' 'subject'    'userdata' 'addt'} ...
        {'style' 'text' 'string' 'session'    'userdata' 'addt'} ...
        {'style' 'text' 'string' 'run'        'userdata' 'addt'} ...
        {'style' 'text' 'string' 'condition'  'userdata' 'addt'} ...
        {'style' 'text' 'string' 'group'      'userdata' 'addt'} ...
        {'style' 'pushbutton' 'string' 'Select by r.v.' 'userdata' 'addt' 'callback' cb_dipole } ...
        {} };
    geomUnit  = [0.2 1 3.5];
    geomUnit2 = [0.2 1.05 0.35 0.4 0.35 0.25 0.6 0.4 0.6 0.3];
	guigeom = { [1] geomUnit geomUnit geomUnit [1] geomUnit2 };

    % create edit boxes
    % -----------------
    for index = 1:10
        guigeom = { guigeom{:} geomUnit2 };
        select_com = ['[inputname, inputpath] = uigetfile2(''*.set;*.SET'', ''Choose dataset to add to STUDY -- pop_study()'');'...
                      'if inputname ~= 0,' ...
                      '   guiind = findobj(''parent'', gcbf, ''tag'', ''set' int2str(index) ''');' ...
                      '   set( guiind,''string'', [inputpath inputname]);' ...
                          loadset ...
                      'end; clear inputname inputpath;'];
        numstr = int2str(index);
        guispec = { guispec{:}, ...
        {'style' 'text'       'string' numstr  'tag' [ 'num'   int2str(index) ] 'userdata' index }, ...
        {'style' 'edit'       'string' ''      'tag' [ 'set'   int2str(index) ] 'userdata' index 'callback' loadsetedit}, ...
        {'style' 'pushbutton' 'string' '...'   'tag' [ 'brw'   int2str(index) ] 'userdata' index 'Callback' select_com}, ...
        {'style' 'edit'       'string' ''      'tag' [ 'sub'   int2str(index) ] 'userdata' index 'Callback' subcom}, ...
        {'style' 'edit'       'string' ''      'tag' [ 'sess'  int2str(index) ] 'userdata' index 'Callback' sescom}, ...
        {'style' 'edit'       'string' ''      'tag' [ 'run'   int2str(index) ] 'userdata' index 'Callback' runcom}, ...
        {'style' 'edit'       'string' ''      'tag' [ 'cond'  int2str(index) ] 'userdata' index 'Callback' condcom}, ...
        {'style' 'edit'       'string' ''      'tag' [ 'group' int2str(index) ] 'userdata' index 'Callback' grpcom}, ...
        {'style' 'pushbutton' 'string' 'All comp.'   'tag' [ 'comps' int2str(index) ] 'userdata' index 'Callback' compcom}, ...
        {'style' 'pushbutton' 'string' 'CLear' 'tag' [ 'clear' int2str(index) ] 'userdata' index 'callback' delset} };
    end

    guispec = { guispec{:}, ...
                {'style' 'text'       'string'  'Important note: Removed datasets will not be saved before being deleted from EEGLAB memory' }, ...
                {}, ...
                {'style' 'pushbutton' 'string'  '<'      'Callback' prevpage 'userdata' 'addt'}, ...
                {'style' 'text'       'string'  'Page 1' 'tag' 'page' 'horizontalalignment' 'center' }, ...
                {'style' 'pushbutton' 'string'  '>'      'Callback' nextpage 'userdata' 'addt'}, {}, ...
                {}, ...
                {'style' 'checkbox'   'value'   0        'tag' 'delclust' 'callback' cb_del }, ...
                {'style' 'text'       'string'  'Delete cluster information (to allow loading new datasets, set new components for clustering, etc.)' } };
	guigeom = { guigeom{:} [1] [1 0.2 0.3 0.2 1] [1] [0.14 3] };

    fig_arg{1} = ALLEEG;      % datasets
    fig_arg{2} = datasetinfo; % datasetinfo
    fig_arg{3} = 1;           % page
    fig_arg{4} = {};          % all commands
    fig_arg{5} = (length(STUDY.cluster) > 1); % are cluster present
    fig_arg{6} = STUDY;       % are cluster present
    fig_arg{7} = [];          % tags

    % generate GUI
    % ------------
    optiongui = { 'geometry', guigeom, ...
                  'uilist'  , guispec, ...
                  'helpcom' , 'pophelp(''pop_study'')', ...
                  'title'   , 'Create a new STUDY set -- pop_study()', ...
                  'userdata', fig_arg };
	[~, ~, ~, ~, instruct, alltags] = inputgui( 'mode', 'plot', optiongui{:}); % plot only to get tags

    % save tags
    currentfig = gcf;
    fig_arg{7} = alltags;
    set(currentfig, 'userdata', fig_arg);

    % update GUI and wait for user input
    pop_study('delclust', currentfig);
    pop_study('redraw', currentfig);
    [result, userdat2, ~, outstruct] = inputgui( 'mode', currentfig, optiongui{:});
    if isempty(result), return; end

    while test_wrong_parameters(currentfig)
    	[result, userdat2, ~, outstruct] = inputgui( 'mode', currentfig, optiongui{:});
        if isempty(result), return; end
    end
    close(currentfig);

    % convert GUI selection to options
    % --------------------------------
    allcom = simplifycom(userdat2{4});
    options = {};
    if ~strcmpi(result{1}, STUDY.name ), options = { options{:} 'name'        result{1} }; end
    if ~strcmpi(result{2}, STUDY.task ), options = { options{:} 'task'        result{2} }; end
    if ~strcmpi(result{3}, STUDY.notes), options = { options{:} 'notes'       result{3} }; end
    if ~isempty(allcom),                 options = { options{:} 'commands'    allcom    }; end
%     if isnumeric(outstruct(1).studyfile)
%         if outstruct(1).studyfile == 1,  options = { options{:} 'resave'      'on' }; end
%     else
%         if ~isempty(outstruct(1).studyfile), options = { options{:} 'filename' outstruct(1).studyfile }; end
%     end
    options = { options{:} 'updatedat' 'on' };

    if outstruct(1).delclust == 1
        options = { options{:} 'rmclust' 'on' };
    else
        options = { options{:} 'rmclust' 'off' };
    end

    % ---
    if ~isequal(outstruct, instruct) && (outstruct(1).delclust ~= 1) % notice that isequal is sensitive to fields order. isequaln isn't backward compatible
        strfields = fieldnames(outstruct);
        for i = 1:length(strfields)
            strdiff(i) = strcmp(outstruct.(strfields{i}),instruct.(strfields{i}));
        end
        % If the information of the STUDY differ, then remove information from clusters
        % Ignoring ('STUDY set name','STUDY set task','STUDY set notes')
        if any(~strdiff(3:end-2))
            options{find(strcmp(options,'rmclust'))+1} = 'on';
        end
    end
    % ---

    % check channel labels
    % --------------------
    TMPALLEEG   = userdat2{1};
    if isfield(TMPALLEEG, 'chanlocs')
        allchans = { TMPALLEEG.chanlocs };
        if any(cellfun('isempty', allchans))
            txt = strvcat('Some datasets do not have channel labels. Do you wish to generate', ...
                          'channel labels automatically for all datasets ("1" for channel 1,', ...
                          '"2" for channel 2, ...). Datasets will be overwritten on disk.', ...
                          'If you abort, the STUDY will not be created.');
            res = questdlg2(txt, 'Dataset format problem', 'Yes', 'No, abort', 'Yes');
            if strcmpi(res, 'yes'), options = { options{:} 'addchannellabels' 'on' 'savedat' 'on'};
            else return;
            end
        end
    end

    % run command and create history
    % ------------------------------
    if alleegEmpty
        com = sprintf( '[STUDY ALLEEG] = std_editset( STUDY, [], %s );\n[STUDY ALLEEG] = std_checkset(STUDY, ALLEEG);', vararg2str(options) );
    else
        com = sprintf( '[STUDY ALLEEG] = std_editset( STUDY, ALLEEG, %s );\n[STUDY ALLEEG] = std_checkset(STUDY, ALLEEG);', vararg2str(options) );
    end
    [STUDY, ALLEEG] = std_editset(STUDY, ALLEEG, options{:});

    % check sessions
    % --------------
    allSessions = [ STUDY.datasetinfo.session ];
    if ~isempty(allSessions)
        if ~ismember(1, allSessions)
            fprintf(2, 'Sessions are usually numbered starting at 1; Do not use session to store custom information\n');
        end
    end

else % internal command

    com = STUDY;
    hdl = ALLEEG; %figure handle

    % userdata info
    % -------------
    userdat     = get(hdl, 'userdata');
    ALLEEG      = userdat{1};
    datasetinfo = userdat{2};
    page        = userdat{3};
    allcom      = userdat{4};
    clusterpresent = userdat{5};
    STUDY       = userdat{6};
    tags        = userdat{7};

    switch  com
        case 'subject'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;

            datasetinfo(realindex).subject   = varargin{2};
            if get(findobj(hdl, 'tag', 'copy_to_dataset'), 'value')
                ALLEEG(realindex).subject    = varargin{2};
            end
            allcom = { allcom{:}, { 'index' realindex 'subject' varargin{2} } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);

        case 'run'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;
            datasetinfo(realindex).run = str2num(varargin{2});
            if get(findobj(hdl, 'tag', 'copy_to_dataset'), 'value')
                ALLEEG(realindex).run  = str2num(varargin{2});
            end
            allcom = { allcom{:}, { 'index' realindex 'run' str2num(varargin{2}) } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);

        case 'session'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;
            datasetinfo(realindex).session   = str2num(varargin{2});
            if get(findobj(hdl, 'tag', 'copy_to_dataset'), 'value')
                ALLEEG(realindex).session    = str2num(varargin{2});
            end
            allcom = { allcom{:}, { 'index' realindex 'session' str2num(varargin{2}) } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);

        case 'group'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;
            datasetinfo(realindex).group   = varargin{2};
            if get(findobj(hdl, 'tag', 'copy_to_dataset'), 'value')
                ALLEEG(realindex).group    = varargin{2};
            end
            allcom = { allcom{:}, { 'index' realindex 'group' varargin{2} } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);

        case 'condition'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;
            datasetinfo(realindex).condition   = varargin{2};
            if get(findobj(hdl, 'tag', 'copy_to_dataset'), 'value')
                ALLEEG(realindex).condition     = varargin{2};
            end
            allcom = { allcom{:}, { 'index' realindex 'condition' varargin{2} } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);

        case 'dipselect'
            STUDY.datasetinfo = datasetinfo;

            res = inputdlg2_with_checkbox( { strvcat('Enter maximum residual (topo map - dipole proj.) var. (in %)', ...
                                       'NOTE: This will delete any existing component clusters!') }, ...
                                       'pop_study():  Pre-select components', 1, { '15' },'pop_study' );

            if isempty(res), return; end
            if res{2} == 1
                STUDY = std_editset(STUDY, ALLEEG, 'commands', { 'inbrain', 'on', 'dipselect' str2num(res{1})/100 'return' });
                allcom = { allcom{:}, { 'inbrain', 'on', 'dipselect' str2num(res{1})/100 } };
            else
                STUDY = std_editset(STUDY, ALLEEG, 'commands', { 'inbrain', 'off','dipselect' str2num(res{1})/100 'return' });
                allcom = { allcom{:}, { 'inbrain', 'off', 'dipselect' str2num(res{1})/100 } };
            end

            datasetinfo   = STUDY.datasetinfo;

            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);
            set(findobj(hdl, 'tag', 'delclust'), 'value', 1);
            pop_study('delclust', hdl);
            pop_study('redraw', hdl);

        case 'component'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;

            for index = 1:size(ALLEEG(realindex).icaweights,1)
                complist{index} = [ 'IC ' int2str(index) ];
            end
            [tmps,tmpv] = listdlg2('PromptString', 'Select components', 'SelectionMode', ...
                                    'multiple', 'ListString', strvcat(complist), 'initialvalue', datasetinfo(realindex).comps);
            if tmpv ~= 0 % no cancel

                % find other subjects with the same session
                % -----------------------------------------
                for index = 1:length(datasetinfo)
                    if realindex == index || (strcmpi(datasetinfo(index).subject, datasetinfo(realindex).subject) && ...
                                ~isempty(datasetinfo(index).subject) && ...
                                isequal( datasetinfo(index).session, datasetinfo(realindex).session ) && ...
                                isequal( datasetinfo(index).run, datasetinfo(realindex).run ) )
                        datasetinfo(index).comps = tmps;
                        allcom = { allcom{:}, { 'index' index 'comps' tmps } };
                        set(findobj('tag', [ 'comps' int2str(index) ]), ...
                            'string', formatbut(tmps), 'horizontalalignment', 'left');
                    end
                end

            end
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);
            pop_study('redraw', hdl);

        case 'clear'
            guiindex  = varargin{1};
            realindex = guiindex+(page-1)*10;

            datasetinfo(realindex).filename  = '';
            datasetinfo(realindex).filepath  = '';
            datasetinfo(realindex).subject   = '';
            datasetinfo(realindex).session   = [];
            datasetinfo(realindex).run       = [];
            datasetinfo(realindex).condition = '';
            datasetinfo(realindex).group     = '';
            datasetinfo(realindex).comps     = [];

            allcom = { allcom{:}, { 'remove' realindex } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);
            pop_study('redraw', hdl);

        case 'nextpage'
            userdat{3} = page+1;
            set(hdl, 'userdata', userdat);
            pop_study('redraw', hdl);

        case 'prevpage'
            userdat{3} = max(1,page-1);
            set(hdl, 'userdata', userdat);
            pop_study('redraw', hdl);

        case 'load'
            guiindex  = varargin{1};
            filename  = varargin{2};
            realindex = guiindex+(page-1)*10;

            % load dataset
            % ------------
            TMPEEG = pop_loadset('filename', filename, 'loadmode', 'info');
            ALLEEG = eeg_store(ALLEEG, eeg_checkset(TMPEEG), realindex);

            % update datasetinfo structure
            % ----------------------------
            datasetinfo(realindex).filename  = ALLEEG(realindex).filename;
            datasetinfo(realindex).filepath  = ALLEEG(realindex).filepath;
            datasetinfo(realindex).subject   = ALLEEG(realindex).subject;
            datasetinfo(realindex).session   = ALLEEG(realindex).session;
            datasetinfo(realindex).run       = ALLEEG(realindex).run;
            datasetinfo(realindex).condition = ALLEEG(realindex).condition;
            datasetinfo(realindex).group     = ALLEEG(realindex).group;
            datasetinfo(realindex).comps     = [];

            allcom = { allcom{:}, { 'index' realindex 'load' filename } };
            userdat{1} = ALLEEG;
            userdat{2} = datasetinfo;
            userdat{4} = allcom;
            set(hdl, 'userdata', userdat);
            pop_study('redraw', hdl);

         case 'delclust'
            if clusterpresent
                if ~get(findobj(hdl, 'tag', 'delclust'), 'value')
                    for k = 1:10
                        set(tags.(['set'   num2str(k)]), 'style', 'text');
                        set(tags.(['comps' num2str(k)]), 'enable', 'off');
                        set(tags.(['sess'  num2str(k)]), 'enable', 'off');
                        set(tags.(['brw'   num2str(k)]), 'enable', 'off');
                    end
                else
                    for k = 1:10
                        set(tags.(['set'   num2str(k)]), 'style', 'edit');
                        set(tags.(['comps' num2str(k)]), 'enable', 'on');
                        set(tags.(['sess'  num2str(k)]), 'enable', 'on');
                        set(tags.(['brw'   num2str(k)]), 'enable', 'on');
                    end
                end
            else
                set(findobj(hdl, 'tag', 'delclust'), 'value', 0)
                if nargin > 2
                    warndlg2('No cluster present');
                end
            end

        case 'redraw'
            for k = 1:10
                kk = k+(page-1)*10; % real index
                if kk > length(datasetinfo)
                    set(tags.(['num' num2str(k)]), 'string', int2str(kk));
                    set(tags.(['set' num2str(k)]), 'string', '');
                    set(tags.(['sub' num2str(k)]), 'string','');
                    set(tags.(['sess' num2str(k)]), 'string','');
                    set(tags.(['run'  num2str(k)]), 'string','');
                    set(tags.(['cond' num2str(k)]), 'string','');
                    set(tags.(['comps' num2str(k)]), 'string','');
                    set(tags.(['group' num2str(k)]), 'string','');
                else
                    set(tags.(['num' num2str(k)]), 'string', int2str(kk));
                    set(tags.(['set' num2str(k)]), 'string', fullfile(char(datasetinfo(kk).filepath), char(datasetinfo(kk).filename)));
                    set(tags.(['sub' num2str(k)]), 'string', char(datasetinfo(kk).subject));
                    set(tags.(['sess' num2str(k)]), 'string', int2str(datasetinfo(kk).session));
                    set(tags.(['run' num2str(k)]),  'string', int2str(datasetinfo(kk).run));
                    set(tags.(['cond' num2str(k)]), 'string', char(datasetinfo(kk).condition));
                    set(tags.(['comps' num2str(k)]), 'string', formatbut(datasetinfo(kk).comps));
                    set(tags.(['group' num2str(k)]), 'string', char(datasetinfo(kk).group));
                end
            end
            if page<10
                 pagestr =  [ ' Page ' int2str(page) ];
            else pagestr =  [ 'Page ' int2str(page) ];
            end
            set(tags.('page'), 'string', pagestr );
    end
end

% remove empty elements in allcom
% -------------------------------
function allcom = simplifycom(allcom);

    for index = length(allcom)-1:-1:1
        if strcmpi(allcom{index}{1}, 'index') && strcmpi(allcom{index+1}{1}, 'index')
            if allcom{index}{2} == allcom{index+1}{2} % same dataset index
                allcom{index}(end+1:end+length(allcom{index+1})-2) = allcom{index+1}(3:end);
                allcom(index+1) = [];
            end
        end
    end

% test for wrong parameters
% -------------------------
function bool = test_wrong_parameters(hdl)
    userdat     = get(hdl, 'userdata');
    datasetinfo = userdat{2};
    datastrinfo = userdat{1};

    bool = 0;
    for index = 1:length(datasetinfo)
        if ~isempty(datasetinfo(index).filename)
            if isempty(datasetinfo(index).subject) && bool == 0
                bool = 1; warndlg2('All datasets must have a subject name or code', 'Error');
            end
        end
    end

    nonempty     = cellfun('isempty', { datasetinfo.filename });
    anysession   = any(~cellfun('isempty', { datasetinfo(nonempty).session }));
    allsession   = all(~cellfun('isempty', { datasetinfo(nonempty).session }));
    anyrun       = any(~cellfun('isempty', { datastrinfo(nonempty).run}));
    allrun       = all(~cellfun('isempty', { datastrinfo(nonempty).run}));
    anycondition = any(~cellfun('isempty', { datasetinfo(nonempty).condition }));
    allcondition = all(~cellfun('isempty', { datasetinfo(nonempty).condition }));
    anygroup     = any(~cellfun('isempty', { datasetinfo(nonempty).group }));
    allgroup     = all(~cellfun('isempty', { datasetinfo(nonempty).group }));
    anydipfit    = any(~cellfun('isempty', { datastrinfo(nonempty).dipfit}));
    alldipfit    = all(~cellfun('isempty', { datastrinfo(nonempty).dipfit}));

    if anygroup && ~allgroup
         bool = 1; warndlg2('If one dataset has a group label, they must all have one', 'Error');
    end
    if anycondition && ~allcondition
         bool = 1; warndlg2('If one dataset has a condition label, they must all have one', 'Error');
    end
    if anysession && ~allsession
         bool = 1; warndlg2('If one dataset has a session index, they must all have one', 'Error');
    end
    if anyrun && ~allrun
         bool = 1; warndlg2('If one dataset has a session index, they must all have one', 'Error');
    end
    if anydipfit && ~alldipfit
         bool = 1; warndlg2('Dipole''s data across datasets is not uniform');
    end
function strbut = formatbut(complist)
    if isempty(complist)
        strbut = 'All comp.';
    else
        if length(complist) > 3, strbut = [ 'Comp.: ' int2str(complist(1:2)) ' ...' ];
        else                     strbut = [ 'Comp.: ' int2str(complist) ];
        end
    end

%---------------------- helper functions -------------------------------------

function [result] = inputdlg2_with_checkbox(Prompt,Title,LineNo,DefAns,funcname);

if nargin < 4
   help inputdlg2;
   return;
end;
if nargin < 5
	funcname = '';
end

if length(Prompt) ~= length(DefAns)
	error('inputdlg2: prompt and default answer cell array must have the same size');
end

geometry = {};
listgui = {};

% determine if vertical or horizontal
% -----------------------------------
geomvert = [];
for index = 1:length(Prompt)
	geomvert = [geomvert size(Prompt{index},1) 1];  % default is vertical geometry
end
if all(geomvert == 1) && length(Prompt) > 1
	geomvert = []; % horizontal
end

for index = 1:length(Prompt)
	if ~isempty(geomvert) % vertical
		geometry = { geometry{:} [ 1] [1 ]};
	else
		geometry = { geometry{:} [ 1 0.6 ]};
	end
	listgui = { listgui{:} { 'Style', 'text', 'string', Prompt{index}}  ...
				{ 'Style', 'edit', 'string', DefAns{index} } { 'Style', 'checkbox', 'string','Keep only in-brain dipoles (requires Fieldtrip extension).','value',1 }  };
end
geometry = [1 1 1];geomvert = [2 1 1];
result = inputgui(geometry, listgui, ['pophelp(''' funcname ''');'], Title, [], 'normal', geomvert);


function aedat = Reorientate(aedat, transpose, flipX, flipY)

%{
For polarity and frame data, if present, apply transpose, flipX and flipY
operations in that order according to the boolean inputs.
%}

dbstop if error

% ReorientatePolarity function depends on this step being carried out
% first
if transpose
    aedat.info.deviceAddressSpace = aedat.info.deviceAddressSpace([2 1]);
end

aedat = ReorientatePolarity(aedat, transpose, flipX, flipY);

aedat = ReorientateFrames(aedat, transpose, flipX, flipY);


function cx = getgain(X)

cx = X.gain;


function record=PASreadrectxt(filename)
  [fd,syserrmsg]=fopen(filename,'rt');
  if (fd==-1),
    PASmsg=sprintf('Could not open %s for reading',filename);
    PASerrmsg(PASmsg,syserrmsg);
  end;

  matchstrs=initstrings;
  record=PASemptyrecord;
  notEOF=1;
  while (notEOF),
    line=fgetl(fd);
    notEOF=ischar(line);
    if (notEOF),
      matchnum=match(line,matchstrs);
      switch matchnum,
    case 1, [imgname]=strread(line,matchstrs(matchnum).str);
	        record.imgname=char(imgname);
	case 2, [x,y,c]=strread(line,matchstrs(matchnum).str);
	        record.imgsize=[x y c];
	case 3, [database]=strread(line,matchstrs(matchnum).str);
	        record.database=char(database);
	case 4, [obj,lbl,xmin,ymin,xmax,ymax]=strread(line,matchstrs(matchnum).str);
	        record.objects(obj).label=char(lbl);
		record.objects(obj).bbox=[min(xmin,xmax),min(ymin,ymax),max(xmin,xmax),max(ymin,ymax)];
	case 5, tmp=findstr(line,' : ');
	        [obj,lbl]=strread(line(1:tmp),matchstrs(matchnum).str);
	        record.objects(obj).label=char(lbl);
    		record.objects(obj).polygon=sscanf(line(tmp+3:end),'(%d, %d) ')';
	case 6, [obj,lbl,mask]=strread(line,matchstrs(matchnum).str);
	        record.objects(obj).label=char(lbl);
    		record.objects(obj).mask=char(mask);
	case 7, [obj,lbl,orglbl]=strread(line,matchstrs(matchnum).str);
            lbl=char(lbl);
	        record.objects(obj).label=lbl;
    		record.objects(obj).orglabel=char(orglbl);
            if strcmp(lbl(max(end-8,1):end),'Difficult')
                record.objects(obj).difficult=true;
                lbl(end-8:end)=[];
            else
                record.objects(obj).difficult=false;
            end
            if strcmp(lbl(max(end-4,1):end),'Trunc')
                record.objects(obj).truncated=true;
                lbl(end-4:end)=[];
            else
                record.objects(obj).truncated=false;
            end
            t=find(lbl>='A'&lbl<='Z');
            t=t(t>=4);
            if ~isempty(t)
                record.objects(obj).view=lbl(t(1):end);
                lbl(t(1):end)=[];
            else
                record.objects(obj).view='';
            end
            record.objects(obj).class=lbl(4:end);

	otherwise, %fprintf('Skipping: %s\n',line);
      end;
    end;
  end;
  fclose(fd);
return

function matchnum=match(line,matchstrs)
  for i=1:length(matchstrs),
    matched(i)=strncmp(line,matchstrs(i).str,matchstrs(i).matchlen);
  end;
  matchnum=find(matched);
  if isempty(matchnum), matchnum=0; end;
  if (length(matchnum)~=1),
    PASerrmsg('Multiple matches while parsing','');
  end;
return

function s=initstrings
  s(1).matchlen=14;
  s(1).str='Image filename : %q';

  s(2).matchlen=10;
  s(2).str='Image size (X x Y x C) : %d x %d x %d';

  s(3).matchlen=8;
  s(3).str='Database : %q';

  s(4).matchlen=8;
  s(4).str='Bounding box for object %d %q (Xmin, Ymin) - (Xmax, Ymax) : (%d, %d) - (%d, %d)';

  s(5).matchlen=7;
  s(5).str='Polygon for object %d %q (X, Y)';

  s(6).matchlen=5;
  s(6).str='Pixel mask for object %d %q : %q';

  s(7).matchlen=8;
  s(7).str='Original label for object %d %q : %q';

return


function [EEG, postOut] = prepPostProcess(EEG, postIn)
% PREP post-processing: HP filter, remove interpolated, cleanup metadata
%
%
%
%% Setup the options for EEGLab to use double precision
    [backupOptionsFile, currentOptionsFile, warningsState] = setupForEEGLAB();
    finishup = onCleanup(@() cleanup(backupOptionsFile, currentOptionsFile, ...
        warningsState));

%% Check the parameters
postOut = struct();

%% Initial setup
    try
        %% Check the input
        if nargin < 1 || ~isstruct(EEG)
            error('postProcess:NotEnoughArguments', 'first argument must be a structure');
        elseif nargin < 2 || ~exist('postIn', 'var') || isempty(postIn)
            postIn = struct();
        end
        if ~isstruct(postIn)
            error('postProcess:NoData', 'second argument must be a structure')
        end
        if ~isfield(EEG, 'etc') || ~isfield(EEG.etc, 'noiseDetection') || ...
                hasPrepErrors(EEG.etc.noiseDetection)
            error('postProcess:NoPrep', 'Prep has not executed or has errors');
        end

        %% Set up the output structure
        postOut = struct('keepFiltered', [], 'removeInterpolatedChannels', [], ...
            'cleanupReference', []);
        defaults = getPrepDefaults(EEG, 'postprocess');

        [postOut, errors] = checkPrepDefaults(postIn, postOut, defaults);
        if ~isempty(errors)
            error('postProcess:BadParameters', ['|' sprintf('%s|', errors{:})]);
        end
        defaults = getPrepDefaults(EEG, 'general');
        [postOut, errors] = checkPrepDefaults(postOut, postOut, defaults);
        if ~isempty(errors)
            error('postProcess:BadGeneralParameters', ['|' sprintf('%s|', errors{:})]);
        end
        EEG.etc.noiseDetection.postProcess = postOut;

    catch mex
        errorMessages.setup = ...
            ['postProcessing failed due to previous issues: ' ...
            getReport(mex, 'basic', 'hyperlinks', 'off')];
        EEG.etc.noiseDetection.errors.postProcess = errorMessages;
        return;
    end

%% Perform filtering if requested
     try
        if postOut.keepFiltered
          fprintf('Refiltering the data in post processing\n');
          [EEG, detrend] = removeTrend(EEG, postOut);
          EEG.etc.noiseDetection.postProcess.detrend = detrend;
        end
    catch mex
        errorMessages.detrend = ...
            ['postProcessing failed removeTrend: ' ...
            getReport(mex, 'basic', 'hyperlinks', 'off')];
        EEG.etc.noiseDetection.errors.status = 'post-processing error';
        EEG.etc.noiseDetection.errors.postProcess = errorMessages;
        if strcmpi(postOut.errorMsgs, 'verbose')
            warning('[%s]\n%s', mex.identifier, ...
                getReport(mex, 'extended', 'hyperlinks', 'on'));
        end
        return;
     end

%% Remove interpolated channels if requested
     try
        interpolatedChannels = EEG.etc.noiseDetection.reference.interpolatedChannels.all;
        if postOut.removeInterpolatedChannels && ~isempty(interpolatedChannels)
          EEG.etc.noiseDetection.postProcess.removedChannelNumbers = ...
               interpolatedChannels;
          EEG.etc.noiseDetection.postProcess.removedChannelData = ...
               EEG.data(interpolatedChannels, :);
          EEG.etc.noiseDetection.postProcess.removedChanlocs = ...
               EEG.chanlocs(interpolatedChannels);
          EEG.chanlocs(interpolatedChannels) = [];
          EEG.data(interpolatedChannels, :) = [];
          EEG.nbchan = length(EEG.chanlocs);
        else
           EEG.etc.noiseDetection.postProcess.removedChannelNumbers = [];
           EEG.etc.noiseDetection.postProcess.removedChannelLocs = [];
        end
    catch mex
        errorMessages.removeInterpolated = ...
            ['postProcessing failed to remove interpolated channels: ' ...
              getReport(mex, 'basic', 'hyperlinks', 'off')];
        EEG.etc.noiseDetection.errors.status = 'post-processing error';
        EEG.etc.noiseDetection.errors.postProcess = errorMessages;
        if strcmpi(postOut.errorMsgs, 'verbose')
            warning('[%s]\n%s', mex.identifier, ...
                getReport(mex, 'extended', 'hyperlinks', 'on'));
        end
        return;
     end
     postOut = EEG.etc.noiseDetection.postProcess;

 %% Cleanup if requested
     try
        if postOut.cleanupReference
           EEG = cleanupReference(EEG);
        end
    catch mex
        errorMessages.cleanupReference = ...
            ['postProcessing failed to cleanup reference information: ' ...
            getReport(mex, 'basic', 'hyperlinks', 'off')];
        EEG.etc.noiseDetection.errors.status = 'post-processing error';
        EEG.etc.noiseDetection.errors.postProcess = errorMessages;
        if strcmpi(postOut.errorMsgs, 'verbose')
            warning('[%s]\n%s', mex.identifier, ...
                getReport(mex, 'extended', 'hyperlinks', 'on'));
        end
        return;
     end

     %% If we got this far everything was good
     EEG.etc.noiseDetection.errors.postProcess = 0;
end

%% Cleanup callback
function cleanup(backupFile, currentFile, warningsState)
% Restore EEGLAB options file and warning settings
   restoreEEGOptions(backupFile, currentFile);
   warning(warningsState);
end % cleanup


function [url, topdir] = UFlocation
%UFLOCATION URL and top-level directory of the UF Sparse Matrix Collection
%
% Example:
%   [url, topdir] = UFlocation
%
% See also UFget.

% Copyright 2006-2007, Timothy A. Davis

params = UFget_defaults ;
t = find (params.dir == filesep) ;
topdir = regexprep (params.dir (1:t(end-1)), '[\/\\]', filesep) ;

t = find (params.url == '/') ;
url = params.url (1:t(end)) ;


function rename_lowercase()
% renames all files in dir with lower case versions
% if all characters are capitalized.
%
% :Example:
% ::
%
%    d = dir('0*'); for i=1:length(d),cd(d(i).name),rename_lowercase, cd .., end
%
% ..
%    tor wager
% ..

d = dir; for i = 3:length(d),
    nm1 =(d(i).name);nm2=lower(nm1);
    str=['!mv ' nm1 ' ' nm2];

    if strcmp(nm1,'SPM.MAT'),
        str = ['!mv ' nm1 ' SPM.mat'];
        disp(str)
        eval(str)
    elseif strcmp(upper(nm1),nm1)
        disp(str)
        eval(str)
    end
end

return


function vol = rxOrthogonalSlice(rx);
%
% vol = rxOrthogonalSlice(rx);
%
% Compute a slice orthogonal to
% the interpolated slice, for
% viewing the rx placement.
%
% Will rotate the volume along
% the y axis in mrGray conventions
% (as if rotating axials clockwise)
% 90 degrees from the current
% prescription.
%
%
% ras 03/05.
vol = [];

return


%   AUTHORSHIP
%   Primary Developer: Stephen Meehan <swmeehan@stanford.edu>
%   Math Lead & Secondary Developer:  Connor Meehan <connor.gw.meehan@gmail.com>
%   Bioinformatics Lead:  Wayne Moore <wmoore@stanford.edu>
%   Provided by the Herzenberg Lab at Stanford University
%   License: BSD 3 clause
%
classdef Radio
    methods(Static)
        function [jp, bg]=Panel(varargin)
            [~, jsa, ~, defaultIdx]=getMsgTypeAndOptions(...
                javax.swing.JOptionPane.QUESTION_MESSAGE, varargin);
            [jp,bg]=Radio.Panel2(jsa, defaultIdx, 11);
        end

        function [jp, bg]=PanelAndCallback(callback, noDefault, varargin)
            [~, jsa, ~, defaultIdx]=getMsgTypeAndOptions(...
                javax.swing.JOptionPane.QUESTION_MESSAGE, varargin);
            if noDefault
                defaultIdx=0;
            end
            [jp,bg]=Radio.Panel2(jsa, defaultIdx, 11, false, callback);
        end

        function [outerPnl, bg, checkBoxes, innerPnl]=Panel2(javaArray, ...
                dfltIdxs, addScrollAfter, checkBox, callback, ...
                priorCmps, sortIdxs, visibleIdxs)
            if nargin<4 || ~checkBox
                checkBox=false;
                jObj='javax.swing.JRadioButton';
            else
                jObj='javax.swing.JCheckBox';
            end
            N=javaArray.length;
            isJPanels=N>0 && isa(javaArray(1),'javax.swing.JPanel');
            innerPnl=javaObjectEDT('javax.swing.JPanel');
            innerPnl.setLayout(javax.swing.BoxLayout(innerPnl, 1));
            if checkBox
                bg=javaObjectEDT('javax.swing.JPanel');
                if ~isJPanels
                    columns=1;
                    horizontal=true;
                    for i=1:N
                        if javaArray(i).length>2
                            horizontal=false;
                            break;
                        else
                        end
                    end
                else
                    columns=1;
                    if N>1
                        columns=javaArray(1).getComponentCount;
                    end
                    horizontal=false;
                end
                if ~horizontal
                    if ~isJPanels
                        innerPnl.setLayout(javaObjectEDT(...
                            'java.awt.GridLayout', N, 1));
                    else
                        innerPnl.setLayout(javaObjectEDT(...
                            'java.awt.GridBagLayout'));
                        gbc=javaObjectEDT('java.awt.GridBagConstraints');
                        gbc.fill=2;
                    end
                else
                    innerPnl.setLayout(javaObjectEDT(...
                        'java.awt.GridLayout', 1, N));
                end
            else
                bg=javax.swing.ButtonGroup;
            end
            checkBoxes=java.util.ArrayList;
            idx=1;
            scrollToHeight=0;
            first=true;
            if ~isJPanels
                %javaArray is char or strings
                for i=1:N
                    rb=javaObjectEDT(jObj, javaArray(i));
                    rObj=rb;
                    pb=handle(rb, 'CallbackProperties');
                    if nargin>=5
                        set(pb,'ActionPerformedCallback', callback);
                    end
                    checkBoxes.add(rb);
                    bg.add(rb);
                    innerPnl.add(rObj);
                    if ~isempty(find(dfltIdxs==idx, 1))
                        rb.setSelected(true);
                        if first
                            d=rb.getPreferredSize;
                            scrollToHeight=d.height*(i-1);
                            first=false;
                        end
                    end
                    idx=idx+1;
                end
            else
                try
                    if nargin>5
                        priorCnt=length(priorCmps);
                        columns=priorCnt/N;
                    end
                    if nargin>6
                        if nargin<8 || isempty(visibleIdxs)
                            for i=1:N
                                idx2=(2*sortIdxs(i))-1;
                                rb=priorCmps{idx2};
                                checkBoxes.add(rb);
                                gbc.gridy=i-1;
                                gbc.gridx=0;
                                gbc.weightx=.8;
                                innerPnl.add(rb, gbc);
                                for j=2:columns
                                    gbc.gridx=j-1;
                                    gbc.weightx=.2;
                                    jp=priorCmps{(idx2+j)-1};
                                    innerPnl.add(jp, gbc);
                                end
                                idx=idx+1;
                            end
                        else
                            for i=1:N
                                si=sortIdxs(i);
                                if isempty(find(visibleIdxs==si, 1))
                                    continue;
                                end
                                idx2=(2*si)-1;
                                rb=priorCmps{idx2};
                                checkBoxes.add(rb);
                                gbc.gridy=i-1;
                                gbc.gridx=0;
                                gbc.weightx=.8;
                                innerPnl.add(rb, gbc);
                                for j=2:columns
                                    gbc.gridx=j-1;
                                    gbc.weightx=.2;
                                    jp=priorCmps{(idx2+j)-1};
                                    innerPnl.add(jp, gbc);
                                end
                                idx=idx+1;
                            end
                        end
                    else
                    for i=1:N
                            rb=javaObjectEDT(jObj, javaArray(i).getComponent(0).getText);
                            pb=handle(rb, 'CallbackProperties');
                            if nargin>=5
                                set(pb,'ActionPerformedCallback', callback);
                            end
                            checkBoxes.add(rb);
                            gbc.gridy=i-1;
                            gbc.gridx=0;
                            gbc.weightx=.8;
                            innerPnl.add(rb, gbc);
                            for j=2:columns
                                gbc.gridx=j-1;
                                gbc.weightx=.2;
                                jp=javaObjectEDT('javax.swing.JPanel');
                                jp.add(javaArray(i).getComponent(j-1));
                                innerPnl.add(jp, gbc);
                            end
                            if ~isempty(find(dfltIdxs==idx, 1))
                                rb.setSelected(true);
                                if first
                                    d=rb.getPreferredSize;
                                    scrollToHeight=d.height*(i-1);
                                    first=false;
                                end
                            end
                            idx=idx+1;
                        end
                    end
                catch ex
                    disp(['If javaArray contains JPanels then '...
                        'Radio.Panel2' newline 'expects component 0'...
                        ' to be JLabel and any 2nd component!' newline]);
                    ex.getReport
                end
            end
            outerPnl=javaObjectEDT('javax.swing.JPanel');
            outerPnl.setBorder(...
                javax.swing.BorderFactory.createEmptyBorder(1,8,1,4));
            if addScrollAfter<N
                d=innerPnl.getPreferredSize;
                d.width=floor(d.width*1.1);
                d.height=floor( (addScrollAfter/N)*d.height);
                innerPnl.setBorder([]);
                scroll=javaObjectEDT('javax.swing.JScrollPane', innerPnl);
                if scrollToHeight>0
                    scroll.getViewport.setViewPosition(...
                        java.awt.Point(0, scrollToHeight));
                end
                scroll.setPreferredSize(d);
                outerPnl=scroll;
            else
                cb=javax.swing.BorderFactory.createCompoundBorder(...
                    javax.swing.BorderFactory.createBevelBorder(...
                    javax.swing.border.BevelBorder.RAISED), ...
                    javax.swing.BorderFactory.createEmptyBorder(8, 8, 8, 8));
                innerPnl.setBorder(cb);
                outerPnl.add(innerPnl);
            end
        end

        function [outerPnl, bg, btns, sortGui]=Panel3(strs, dfltIdxs, ...
                addScrollAfter, callback, checkBox, advice)
            isRadio=nargin<5||~checkBox;
            bg=[];
            sortGui=[];
            if isRadio
                jObj='javax.swing.JRadioButton';
            else
                jObj='javax.swing.JCheckBox';
            end
            N=length(strs);
            innerPnl=javaObjectEDT('javax.swing.JPanel');
            innerPnl.setLayout(javax.swing.BoxLayout(innerPnl, 1));
            if isRadio
                bg=javax.swing.ButtonGroup;
            end
            btns=java.util.ArrayList;
            scrollToHeight=0;
            for i=1:N
                rb=javaObjectEDT(jObj, strs{i});
                rObj=rb;
                pb=handle(rb, 'CallbackProperties');
                set(pb,'ActionPerformedCallback', @(h,e)innerCallback(h,e,i));
                btns.add(rb);
                if isRadio
                    bg.add(rb);
                end
                innerPnl.add(rObj);
            end
            first=true;
            if ~isempty(dfltIdxs)
                for i=1:N
                    if ~isempty(find(dfltIdxs==i, 1))
                        rb=btns.get(i-1);
                        rb.setSelected(true);
                        if first
                            d=rb.getPreferredSize;
                            scrollToHeight=d.height*(i-1);
                            first=false;
                        end
                    end
                end
            end
            outerPnl=javaObjectEDT('javax.swing.JPanel');
            outerPnl.setBorder(...
                javax.swing.BorderFactory.createEmptyBorder(1,8,1,4));
            if addScrollAfter<N
                d=innerPnl.getPreferredSize;
                d.width=floor(d.width*1.1);
                d.height=floor( (addScrollAfter/N)*d.height);
                innerPnl.setBorder([]);
                scroll=javaObjectEDT('javax.swing.JScrollPane', innerPnl);
                if scrollToHeight>0
                    scroll.getViewport.setViewPosition(...
                        java.awt.Point(0, scrollToHeight));
                end
                scroll.setPreferredSize(d);
                outerPnl=scroll;
            else
                cb=javax.swing.BorderFactory.createCompoundBorder(...
                    javax.swing.BorderFactory.createBevelBorder(...
                    javax.swing.border.BevelBorder.RAISED), ...
                    javax.swing.BorderFactory.createEmptyBorder(8, 8, 8, 8));
                innerPnl.setBorder(cb);
                outerPnl.add(innerPnl);
            end
            if ~isRadio
                bp=Gui.BorderPanel;
                north=Gui.BorderPanel;
                bp.add(north, 'North');
                bp.add(outerPnl, 'Center');
                if nargin==6
                    south=Gui.Panel;
                    bp.add(south, 'South');
                    south.add(Gui.Label(advice));
                end
                allChb=Gui.CheckBox('All', true, [],[], @(h,e)doAll(h,e));
                north.add(allChb, 'West');
                outerPnl=bp;
                sortGui=SortGui([], allChb, 'All', north, strs, btns, innerPnl);
                sortGui.setAllChbText;
            end

            function innerCallback(h,e,i)
                if ~isempty(sortGui)
                    sortGui.setAllChbText;
                end
                feval(callback, h, e, i);
            end

            function doAll(h, e)
                state=h.isSelected;
                if state
                    Gui.ShowBusy(sortGui.dlg, 'Showing all', ...
                        'demoicon.gif', 7);
                else
                    Gui.ShowBusy(sortGui.dlg, 'Hiding all', 'hide.png', 5);
                end
                it=btns.iterator;
                idx=1;
                while it.hasNext
                    b=it.next;
                    if ~isempty(Gui.WindowAncestor(b))
                        if state ~= b.isSelected
                            b.setSelected(state);
                            try
                                priorActionCommand=b.getActionCommand;
                                b.setActionCommand(Plots.DOING_ALL);
                                ok=feval(callback, b, e, idx);
                                b.setActionCommand(priorActionCommand);
                                if ~ok
                                    break;
                                end
                            catch
                            end
                        end
                    end
                    idx=idx+1;
                end
                drawnow;
                Gui.HideBusy(sortGui.dlg)
            end

        end

        function [pnl, checkBoxes]=VerticalCheckBoxes(title, ...
                labels, dfltIdxs)
            jObj='javax.swing.JCheckBox';
            N=length(labels);
            pnl=Gui.SetTitledBorder(title);
            pnl.setLayout(javaObjectEDT('java.awt.GridLayout', N, 1));
            checkBoxes={};
            for i=1:N
                cbx=javaObjectEDT(jObj, labels{i});
                if ~isempty(find(dfltIdxs==i, 1))
                    cbx.setSelected(true);
                end
                checkBoxes{end+1}=cbx;
                pnl.add(cbx);
            end
        end

        function [pnl, bg, radioButtons]=Vertical(title, ...
                labels, dfltIdx)
            jObj='javax.swing.JRadioButton';
            N=length(labels);
            pnl=Gui.SetTitledBorder(title);
            pnl.setLayout(javaObjectEDT('java.awt.GridLayout', N, 1));
            radioButtons={};
            bg=javax.swing.ButtonGroup;
            for i=1:N
                rb=javaObjectEDT(jObj, labels{i});
                if dfltIdx==i
                    rb.setSelected(true);
                end
                radioButtons{end+1}=rb;
                pnl.add(rb);
                bg.add(rb);
            end
        end

        function [pnl, bg, radioButtons]=Horizontal(title, labels,...
                dfltIdx)
            jObj='javax.swing.JRadioButton';
            N=length(labels);
            if ~isempty(title)
                pnl=Gui.SetTitledBorder(title);
            else
                pnl=Gui.Panel;
            end

            bg=javax.swing.ButtonGroup;
            radioButtons={};
            for i=1:N
                rb=javaObjectEDT(jObj, labels{i});
                radioButtons{end+1}=rb;
                bg.add(rb);
                pnl.add(rb);
                if dfltIdx==i
                    rb.setSelected(true);
                end
            end
        end

        function Clear(bg)
            e=bg.getElements;
            while e.hasMoreElements
                rb=e.nextElement;
                if rb.isSelected
                    rb.setSelected(false);
                end
            end
        end

        function idx=Choice(bg)
            idx=1;
            e=bg.getElements;
            while e.hasMoreElements
                rb=e.nextElement;
                if rb.isSelected
                    return;
                end
                idx=idx+1;
            end
            idx=0;
        end

        function Disable(bg, idx)
            idx_=1;
            e=bg.getElements;
            while e.hasMoreElements
                rb=e.nextElement;
                if idx==idx_
                    rb.setEnabled(false);
                    return;
                end
                idx_=idx_+1;
            end
            idx_=0;
        end

        function [outerPnl, bg, checkBoxes, innerPnl]=Panel4(...
                javaArray, dfltIdxs, addScrollAfter, checkBox, gridBag)
            if ~checkBox
                checkBox=false;
                jObj='javax.swing.JRadioButton';
            else
                jObj='javax.swing.JCheckBox';
            end
            usingBag=false;
            N=javaArray.length;
            isJPanels=N>0 && isa(javaArray(1),'javax.swing.JPanel');
            innerPnl=javaObjectEDT('javax.swing.JPanel');
            innerPnl.setLayout(javax.swing.BoxLayout(innerPnl, 1));
            columns=1;
            if checkBox
                bg=javaObjectEDT('javax.swing.JPanel');
                if ~isJPanels
                    horizontal=true;
                    for i=1:N
                        if javaArray(i).length>2
                            horizontal=false;
                            break;
                        else
                        end
                    end
                else
                    columns=javaArray(1).getComponentCount;
                    horizontal=false;
                end
                if ~horizontal
                    if ~gridBag && ~isJPanels
                        innerPnl.setLayout(javaObjectEDT(...
                            'java.awt.GridLayout', N, 1));
                    else
                        innerPnl.setLayout(javaObjectEDT(...
                            'java.awt.GridBagLayout'));
                        gbc=javaObjectEDT('java.awt.GridBagConstraints');
                        %gbc.weighty=1;
                        gbc.anchor=gbc.WEST;
                        gbc.fill=0;
                        usingBag=true;
                    end
                else
                    innerPnl.setLayout(javaObjectEDT(...
                        'java.awt.GridLayout', 1, N));
                end
            else
                bg=javax.swing.ButtonGroup;
            end
            checkBoxes=java.util.ArrayList;
            idx=1;
            scrollToHeight=0;
            first=true;
            if ~checkBox || ~gridBag
                %javaArray is char or strings
                for i=1:N
                    if isJPanels
                        rb=javaObjectEDT(jObj, javaArray(i).getComponent(0).getText);
                    else
                        rb=javaObjectEDT(jObj, javaArray(i));
                    end
                    rObj=rb;
                    checkBoxes.add(rb);
                    bg.add(rb);
                    if ~usingBag
                        innerPnl.add(rObj);
                    else
                        gbc.gridy=i-1;
                        gbc.gridx=0;
                        innerPnl.add(rObj, gbc);
                    end
                    for j=2:columns
                        gbc.gridx=j-1;
                        gbc.weightx=.2;
                        jp=javaObjectEDT('javax.swing.JPanel');
                        jp.add(javaArray(i).getComponent(j-1));
                        innerPnl.add(jp, gbc);
                    end
                    if ~isempty(find(dfltIdxs==idx, 1))
                        rb.setSelected(true);
                        if first
                            d=rb.getPreferredSize;
                            scrollToHeight=d.height*(i-1);
                            first=false;
                        end
                    end
                    idx=idx+1;
                end
            else
                try
                    for i=1:N
                        if isJPanels
                            rb=javaObjectEDT(jObj, javaArray(i).getComponent(0).getText);
                        else
                            rb=javaObjectEDT(jObj, javaArray(i));
                        end
                        checkBoxes.add(rb);
                        gbc.gridy=i-1;
                        gbc.gridx=0;
                        gbc.weightx=.8;
                        fprintf('y=%d x=%d wx=%d wy=%d fill=%d\n', ...
                            gbc.gridy, gbc.gridx, gbc.weighty, ...
                            gbc.weightx, gbc.fill);
                        innerPnl.add(rb, gbc);
                        for j=2:columns
                            gbc.gridx=j-1;
                            gbc.weightx=.2;
                            jp=javaObjectEDT('javax.swing.JPanel');
                            jp.add(javaArray(i).getComponent(j-1));
                            innerPnl.add(jp, gbc);
                        end
                        if ~isempty(find(dfltIdxs==idx, 1))
                            rb.setSelected(true);
                            if first
                                d=rb.getPreferredSize;
                                scrollToHeight=d.height*(i-1);
                                first=false;
                            end
                        end
                        idx=idx+1;
                    end
                catch ex
                    disp(['If javaArray contains JPanels then '...
                        'Radio.Panel2' newline 'expects component 0'...
                        ' to be JLabel and any 2nd component!' newline]);
                    ex.getReport
                end
            end
            outerPnl=javaObjectEDT('javax.swing.JPanel');
            outerPnl.setBorder(...
                javax.swing.BorderFactory.createEmptyBorder(1,8,1,4));
            if addScrollAfter<N
                d=innerPnl.getPreferredSize;
                d.width=floor(d.width*1.1);
                d.height=floor( (addScrollAfter/N)*d.height);
                innerPnl.setBorder([]);
                scroll=javaObjectEDT('javax.swing.JScrollPane', innerPnl);
                if scrollToHeight>0
                    scroll.getViewport.setViewPosition(...
                        java.awt.Point(0, scrollToHeight));
                end
                scroll.setPreferredSize(d);
                outerPnl=scroll;
            else
                cb=javax.swing.BorderFactory.createCompoundBorder(...
                    javax.swing.BorderFactory.createBevelBorder(...
                    javax.swing.border.BevelBorder.RAISED), ...
                    javax.swing.BorderFactory.createEmptyBorder(8, 8, 8, 8));
                innerPnl.setBorder(cb);
                outerPnl.add(innerPnl);
            end
        end

    end
end


% std_checkset() - check STUDY set consistency
%
% Usage: >> [STUDY, ALLEEG] = std_checkset(STUDY, ALLEEG);
%
% Input:
%   STUDY      - EEGLAB STUDY set
%   ALLEEG     - vector of EEG datasets included in the STUDY structure
%
% Output:
%   STUDY      - a new STUDY set containing some or all of the datasets in ALLEEG,
%                plus additional information from the optional inputs above.
%   ALLEEG     - an EEGLAB vector of EEG sets included in the STUDY structure
%
% Authors:  Arnaud Delorme & Hilit Serby, SCCN, INC, UCSD, November 2005

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, arno@sccn.ucsd.edu
%
% This program 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 2 of the License, or
% (at your option) any later version.
%
% This program 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 this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [STUDY, ALLEEG, command] = std_checkset(STUDY, ALLEEG, option);

if nargin < 2
    help std_checkset;
    return;
end;
command  = '';

if isempty(STUDY), return; end;
studywasempty = 0;

modif = 0;
if ~isfield(STUDY, 'name'),  STUDY.name  = ''; modif = 1; end;
if ~isfield(STUDY, 'task'),  STUDY.task  = ''; modif = 1; end;
if ~isfield(STUDY, 'notes'), STUDY.notes = ''; modif = 1; end;
if ~isfield(STUDY, 'filename'),  STUDY.filename  = ''; modif = 1; end;
if ~isfield(STUDY, 'filepath'),  STUDY.filepath  = ''; modif = 1; end;
if ~isfield(STUDY, 'history'),   STUDY.history   = ''; modif = 1; end;
if ~isfield(STUDY, 'subject'),   STUDY.subject   = {}; modif = 1; end;
if ~isfield(STUDY, 'group'),     STUDY.group     = {}; modif = 1; end;
if ~isfield(STUDY, 'session'),   STUDY.session   = {}; modif = 1; end;
if ~isfield(STUDY, 'condition'), STUDY.condition = {}; modif = 1; end;
if ~isfield(STUDY, 'setind'),    STUDY.setind    = {}; modif = 1; end;
if ~isfield(STUDY, 'etc'),       STUDY.etc       = []; modif = 1; end;
if ~isfield(STUDY, 'etc.warnmemory'), STUDY.etc.warnmemory = 1; modif = 1; end;
if ~isfield(STUDY, 'preclust'),  STUDY.preclust  = []; modif = 1; end;
if ~isfield(STUDY, 'datasetinfo'), STUDY.datasetinfo = []; modif = 1; end;
if ~isfield(STUDY.etc, 'version'), STUDY.etc.version = []; modif = 1; end;
if ~isfield(STUDY.preclust, 'erpclusttimes' ),  STUDY.preclust.erpclusttimes = []; modif = 1; end;
if ~isfield(STUDY.preclust, 'specclustfreqs' ), STUDY.preclust.specclustfreqs = []; modif = 1; end;
if ~isfield(STUDY.preclust, 'erspclustfreqs' ), STUDY.preclust.erspclustfreqs = []; modif = 1; end;
if ~isfield(STUDY.preclust, 'erspclusttimes' ), STUDY.preclust.erspclusttimes = []; modif = 1; end;
if ~isfield(STUDY.datasetinfo, 'comps') & ~isempty(STUDY.datasetinfo), STUDY.datasetinfo(1).comps = []; modif = 1; end;
if ~isfield(STUDY.datasetinfo, 'index') & ~isempty(STUDY.datasetinfo), STUDY.datasetinfo(1).index = []; modif = 1; end;

% all summary fields
% ------------------
try, subject = unique_bc({ STUDY.datasetinfo.subject });
catch,
    subject = '';
    disp('Important warning: some datasets do not have subject codes; some functions may crash!');
end;
try, group = unique_bc({ STUDY.datasetinfo.group });
catch,
    group = {};
    % disp('Important warning: some datasets do not have group codes; some functions may crash!');
end;
try, condition = unique_bc({ STUDY.datasetinfo.condition });
catch,
    condition = {};
    disp('Important warning: some datasets do not have condition codes; some functions may crash!');
end;
try, session = unique_bc([STUDY.datasetinfo.session]);
catch,
    session = '';
    % disp('Important warning: some datasets do not have session numbers; some functions may crash!');
end;
if ~isequal(STUDY.subject,   subject  ), STUDY.subject   = subject;   modif = 1; end;
if ~isequal(STUDY.group,     group    ), STUDY.group     = group;     modif = 1; end;
if ~isequal(STUDY.condition, condition), STUDY.condition = condition; modif = 1; end;
if ~isequal(STUDY.session,   session  ), STUDY.session   = session;   modif = 1; end;

% check dataset info consistency
% ------------------------------
for k = 1:length(STUDY.datasetinfo)
    if ~strcmpi(STUDY.datasetinfo(k).filename, ALLEEG(k).filename)
        STUDY.datasetinfo(k).filename = ALLEEG(k).filename; modif = 1;
        fprintf('Warning: file name has changed for dataset %d and the study has been updated\n', k);
        fprintf('         to discard this change in the study, reload it from disk\n');
    end;
end;

% recompute setind array (setind is deprecated but we keep it anyway)
% -------------------------------------------------------------------
setind  = [];
sameica = std_findsameica(ALLEEG);
for index = 1:length(sameica)
    setind(length(sameica{index}):-1:1,index) = sameica{index}';
end;
setind(find(setind == 0)) = NaN;
if any(isnan(setind))
    warndlg('Warning: non-uniform set of dataset, some function might not work');
end
if ~isequal(setind, STUDY.setind), STUDY.setind = setind; modif = 1; end;

% check that dipfit is present in all datasets
% --------------------------------------------
for cc = 1:length(sameica)
    idat = [];
    for tmpi = 1:length(sameica{cc})
        if isfield(ALLEEG(sameica{cc}(tmpi)).dipfit, 'model')
            idat = sameica{cc}(tmpi);
        end;
    end;
    if ~isempty(idat)
        for tmpi = 1:length(sameica{cc})
            if ~isfield(ALLEEG(sameica{cc}(tmpi)).dipfit, 'model')
                ALLEEG(sameica{cc}(tmpi)).dipfit = ALLEEG(idat).dipfit;
                ALLEEG(sameica{cc}(tmpi)).saved  = 'no';
                fprintf('Warning: no ICA dipoles for dataset %d, using dipoles from dataset %d (same ICA)\n', sameica{cc}(tmpi), idat);
            end;
        end;
    end;
end;

% put in fake channels if channel labels are missing
% --------------------------------------------------
chanlabels = { ALLEEG.chanlocs };
if any(cellfun(@isempty, chanlabels))
    if any(~cellfun(@isempty, chanlabels))
        disp('********************************************************************');
        disp(' IMPORTANT WARNING: SOME DATASETS DO NOT HAVE CHANNEL LABELS AND ');
        disp(' SOME OTHERs HAVE CHANNEL LABELS. GENERATING CHANNEL LABELS FOR ');
        disp(' THE FORMER DATASETS (THIS SHOULD PROBABLY BE FIXED BY THE USER).');
        disp('********************************************************************');
    end;
    disp('Generating channel labels for all datasets...');
    for currentind = 1:length(ALLEEG)
        for ind = 1:ALLEEG(currentind).nbchan
            ALLEEG(currentind).chanlocs(ind).labels = int2str(ind);
        end;
    end;
    ALLEEG(currentind).saved = 'no';
end;

if length( unique( [ ALLEEG.srate ] )) > 1
    disp('********************************************************************');
    disp(' IMPORTANT WARNING: SOME DATASETS DO NOT HAVE THE SAME SAMPLING ');
    disp(' RATE AND THIS WILL MAKE MOST OF THE STUDY FUNCTIONS CRASH. THIS');
    disp(' SHOULD PROBABLY BE FIXED BY THE USER.');
    disp('********************************************************************');
end;

% check cluster array
% -------------------
rebuild_design = 0;
if ~isfield(STUDY, 'cluster'), STUDY.cluster = []; modif = 1; end;
if ~isfield(STUDY, 'changrp'), STUDY.changrp = []; modif = 1; end;
if isempty(STUDY.changrp) && isempty(STUDY.cluster)
    rebuild_design = 1;
end;
if isfield(STUDY.cluster, 'sets'),
    if max(STUDY.cluster(1).sets(:)) > length(STUDY.datasetinfo)
        disp('Warning: Some datasets had been removed from the STUDY, clusters have been reinitialized');
        STUDY.cluster = [];
    end;
end;
if ~studywasempty
    if isempty(STUDY.cluster)
        modif = 1;
        [STUDY] = std_createclust(STUDY, ALLEEG, 'parentcluster', 'on');
    end;
    if length(STUDY.cluster(1).child) == length(STUDY.cluster)-1 && length(STUDY.cluster) > 1
        newchild = { STUDY.cluster(2:end).name };
        if ~isequal(STUDY.cluster(1).child, newchild)
            STUDY.cluster(1).child = newchild;
        end;
    end;
end;

% create STUDY design if it is not present
% ----------------------------------------
if ~studywasempty
    if isfield(STUDY.datasetinfo, 'trialinfo')
        alltrialinfo = { STUDY.datasetinfo.trialinfo };
        if any(cellfun(@isempty, alltrialinfo)) && any(~cellfun(@isempty, alltrialinfo))
            disp('Rebuilding trial information structure for STUDY');
            STUDY  = std_maketrialinfo(STUDY, ALLEEG); % some dataset do not have trialinfo and
            % some other have it, remake it for everybody
        end;
    end;
    if ~isfield(STUDY, 'design') || isempty(STUDY.design) || ~isfield(STUDY.design, 'name')
        STUDY  = std_maketrialinfo(STUDY, ALLEEG);
        STUDY  = std_makedesign(STUDY, ALLEEG);
        STUDY  = std_selectdesign(STUDY, ALLEEG,1);
        rebuild_design = 0;
    else
        if isfield(STUDY.design, 'indvar1')
            STUDY  = std_convertdesign(STUDY, ALLEEG);
        end;

        % convert combined independent variable values
        % between dash to cell array of strings
        % -------------------------------------
        for inddes = 1:length(STUDY.design)
            if length(STUDY.design(inddes).variable) == 0
                STUDY.design(inddes).variable(1).label = '';
                STUDY.design(inddes).variable(1).value = [];
            end;
            if length(STUDY.design(inddes).variable) == 1
                STUDY.design(inddes).variable(2).label = '';
                STUDY.design(inddes).variable(2).value = [];
            end;
            if ~isfield(STUDY.design(inddes).variable, 'pairing')
                STUDY.design(inddes).variable(1).pairing = 'on';
                STUDY.design(inddes).variable(2).pairing = 'on';
            end;
            for indvar = 1:length(STUDY.design(inddes).variable)
                for indval = 1:length(STUDY.design(inddes).variable(indvar).value)
                    STUDY.design(inddes).variable(indvar).value{indval} = convertindvarval(STUDY.design(inddes).variable(indvar).value{indval});
                end;
            end;
        end;

       if ~isfield(STUDY.design(1), 'cell') || isempty(STUDY.design(1).cell)
           fprintf('Warning: Importing STUDY from a newer version of EEGLAB - some information will be lost\n');
           STUDY = std_makedesign(STUDY, ALLEEG, 1, STUDY.design(1), 'defaultdesign', 'forceoff');
       end;
       for inddes = 1:length(STUDY.design)
             for indcell = 1:length(STUDY.design(inddes).cell)
                for indval = 1:length(STUDY.design(inddes).cell(indcell).value)
                    STUDY.design(inddes).cell(indcell).value{indval} = convertindvarval(STUDY.design(inddes).cell(indcell).value{indval});
                end;
            end;
            for indinclude = 1:length(STUDY.design(inddes).include)
                if iscell(STUDY.design(inddes).include{indinclude})
                    for indval = 1:length(STUDY.design(inddes).include{indinclude})
                        STUDY.design(inddes).include{indinclude}{indval} = convertindvarval(STUDY.design(inddes).include{indinclude}{indval});
                    end;
                end;
            end;

            % check for duplicate entries in filebase
            % ---------------------------------------
            if length( { STUDY.design(inddes).cell.filebase } ) > length(unique({ STUDY.design(inddes).cell.filebase }))
                if ~isempty(findstr('design_', STUDY.design(inddes).cell(1).filebase))
                    error('There is a problem with your STUDY, contact EEGLAB support');
                else
                    fprintf('Duplicate entry detected in Design %d, reinitializing design\n', inddes);
                    [STUDY com] = std_makedesign(STUDY, ALLEEG, inddes, STUDY.design(inddes), 'defaultdesign', 'forceoff');
                end
            end;
        end;
    end;

    if rebuild_design % in case datasets have been added or removed
        STUDY = std_rebuilddesign(STUDY, ALLEEG);
    end;

    % scan design to fix old paring format
    % ------------------------------------
    for design = 1:length(STUDY.design)
        for var = 1:length(STUDY.design(design).variable)
            if isstr(STUDY.design(design).variable(1).pairing)
                if strcmpi(STUDY.design(design).variable(1).pairing, 'paired')
                    STUDY.design(design).variable(1).pairing = 'on';
                elseif strcmpi(STUDY.design(design).variable(1).pairing, 'unpaired')
                    STUDY.design(design).variable(1).pairing = 'off';
                end;
            end;
            if isstr(STUDY.design(design).variable(2).pairing)
                if strcmpi(STUDY.design(design).variable(2).pairing, 'paired')
                    STUDY.design(design).variable(2).pairing = 'on';
                elseif strcmpi(STUDY.design(design).variable(2).pairing, 'unpaired')
                    STUDY.design(design).variable(2).pairing = 'off';
                end;
            end;
        end;
    end;

    % add filepath field if absent
    for ind = 1:length(STUDY.design)
        if ~isfield(STUDY.design, 'filepath') || (isnumeric(STUDY.design(ind).filepath) && isempty(STUDY.design(ind).filepath))
            STUDY.design(ind).filepath = '';
            STUDY.saved = 'no';
            modif = 1;
        end;
    end;

    % check that ICA is present and if it is update STUDY.datasetinfo
    allcompsSTUDY  = { STUDY.datasetinfo.comps };
    allcompsALLEEG = { ALLEEG.icaweights };
    if all(cellfun(@isempty, allcompsSTUDY)) && ~all(cellfun(@isempty, allcompsALLEEG))
        for index = 1:length(STUDY.datasetinfo)
            STUDY.datasetinfo(index).comps = [1:size(ALLEEG(index).icaweights,1)];
        end;
    end;

    % make channel groups
    % -------------------
    if ~isfield(STUDY, 'changrp') || isempty(STUDY.changrp)
        STUDY = std_changroup(STUDY, ALLEEG);
        modif = 1;
    end;
end;

% determine if there has been any change
% --------------------------------------
if modif;
    STUDY.saved = 'no';
    command = '[STUDY ALLEEG] = std_checkset(STUDY, ALLEEG);';
    addToHistory = true;
    % check duplicate
    if length(STUDY.history) >= length(command) && strcmpi(STUDY.history(end-length(command)+1:end), command)
        addToHistory = false;
    end;
    if addToHistory
        STUDY.history =  sprintf('%s\n%s',  STUDY.history, command);
    end;
end;

% convert combined independent variables
% --------------------------------------
function val = convertindvarval(val);
if isstr(val)
    inddash = findstr(' - ', val);
    if isempty(inddash), return; end;
    inddash = [ -2 inddash length(val)+1];
    for ind = 1:length(inddash)-1
        newval{ind} = val(inddash(ind)+3:inddash(ind+1)-1);
    end;
    val = newval;
end;


function rts_ProcInformationUpdtFunc(~,~, hRtsMainGUI, hTable)
%rts_ProcInformationUpdtFunc Summary of this function goes here
%   Detailed explanation goes here
    timers = getappdata(hRtsMainGUI,'timers');

	tableData = {};
    try
        validtimers = timer.empty(1,0);
        for(i=1:length(timers))
            if(isvalid(timers(i)))
                validtimers(end+1) = timers(i);
            else
                delete(timers(i));
            end
        end

        for(i=1:length(validtimers))
            task = validtimers(i);

            if(isvalid(task))
                tableData{i,1} = get(task,'Name');
                tableData{i,2} = cap1stLetter(get(task,'Running'));
                tableData{i,3} = cap1stLetter(get(task,'ExecutionMode'));
                tableData{i,4} = num2str(get(task,'Period'));
            else
                delete(task);
            end
        end

        setTableData(hTable, tableData)
    catch ME

    end
end


function X = real(X)
%REAL (overloaded)

X.basis = real(X.basis);
X = clean(X);
if isa(X,'sdpvar')
   X.conicinfo = [0 0];
end


function [ files ] = s1manifest_files( domnode )
%S1MANIFEST_FILES Extract relevant measurement and metadata files from a
%Sentinel manifest.safe file and group them together appropriately.
%
% Written by: Wade Schwartzkopf, NGA/IDT
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

%% Setup
% Overall wrapping node is in a namespace that makes things messier
% (than they already are) in a XPath call, so we go down one level.
domnode = domnode.getFirstChild;
xp=javax.xml.xpath.XPathFactory.newInstance.newXPath();

%% Find associated measurement and metadata files in this package
% First find all of the "Measurement Data Units".  This should provide each
% data object (measurements), together with its metadata and noise and
% calibration files.
mdu_xp_str = ['informationPackageMap/' ...
    '*[local-name()=''contentUnit'']/' ...
    '*[local-name()=''contentUnit''][@repID="s1Level1MeasurementSchema"]'];
num_measurment_data_units = str2double(xp.evaluate(...
    ['count(' mdu_xp_str ')'],...
    domnode));
files(num_measurment_data_units) = struct('data','','product','','noise','','calibration','');
for i = 1:num_measurment_data_units
    % Find data ("measurement") file itself
    data_object_ID = char(xp.evaluate(...
        [mdu_xp_str '[' num2str(i) ']/dataObjectPointer/@dataObjectID'],domnode));
    files(i).data = char(xp.evaluate([...
        'dataObjectSection/' ...
        'dataObject[@ID="' data_object_ID '"]/' ...
        'byteStream/' ...
        'fileLocation/' ...
        '@href'],domnode));
    % Find references to associated metadata files
    associated_DMDs = strsplit(strtrim(char(xp.evaluate(...
        [mdu_xp_str '[' num2str(i) ']/@dmdID'],domnode))));
    % The references are indirect.  They are equivalently pointers to
    % pointers. Not clear why it was done this way, but here we get the
    % real IDs for those files (used in the dataObjectSection).
    associated_IDs=cell(numel(associated_DMDs),1);
    for j = 1:numel(associated_DMDs)
        associated_IDs{j} = char(xp.evaluate([
            'metadataSection/' ...
            'metadataObject[@ID="' associated_DMDs{j} '"]/' ...
            'dataObjectPointer/' ...
            '@dataObjectID'],domnode));
    end
    % Construct a string that will look for any of these IDs
    ID_test_string = ['[@ID="' associated_IDs{1} '"'];
    for j = 2:numel(associated_IDs)
        ID_test_string = [ID_test_string ' or @ID="' associated_IDs{j} '"'];
    end
    ID_test_string = [ID_test_string ']'];
    files(i).product = char(xp.evaluate([...
        'dataObjectSection/' ...
        'dataObject[@repID="s1Level1ProductSchema"]' ... % Must be product type
        ID_test_string '/' ... % and an ID that matches one associated with the current measurement
        'byteStream/' ...
        'fileLocation/' ...
        '@href'],domnode));
    files(i).noise = char(xp.evaluate([...
        'dataObjectSection/' ...
        'dataObject[@repID="s1Level1NoiseSchema"]' ... % Must be noise type
        ID_test_string '/' ... % and an ID that matches one associated with the current measurement
        'byteStream/' ...
        'fileLocation/' ...
        '@href'],domnode));
    files(i).calibration = char(xp.evaluate([...
        'dataObjectSection/' ...
        'dataObject[@repID="s1Level1CalibrationSchema"]' ... % Must be calibration type
        ID_test_string '/' ... % and an ID that matches one associated with the current measurement
        'byteStream/' ...
        'fileLocation/' ...
        '@href'],domnode));
end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


function bool = QIBPresent
%"QIBPresent"
%   Returns true only if the matrix files necessary to run QIB are present
%   and in the proper location for this copy of CERR.  Else returns false.
%
%   JRA 10/15/04
%
%Usage:
%   function bool = QIBPresent
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

bool = 0;
IMRTPdir = fileparts(which('IMRTP'));
if exist(fullfile(IMRTPdir, 'QIBData', 'aahn_6b.dat'), 'file') == 2 && ...
   exist(fullfile(IMRTPdir, 'QIBData', 'aahn_18b.dat'), 'file') == 2 && ...
   exist(fullfile(IMRTPdir, 'QIBData', 'QIB_lin_0pt125.mat'), 'file') == 2 && ...
   exist(fullfile(IMRTPdir, 'QIBData', 'QIBGauss_lin_0pt125.mat'), 'file') == 2
   bool = 1;
end


function h = SideAxes(a,location,s,varargin)

%SideAxes - Add side axes to existing axes.
%
%  USAGE
%
%    h = SideAxes(a,location,s,<options>)
%
%    Using cell arrays will overlay variable pairs.
%
%    a              optional target axes (default = gca)
%    s              size, as proportion of target axes
%    location       'top', 'bottom', 'left' or 'right'
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'gap'         gap between target and side axes, as proportion of total
%                   width or height (default = 0.1)
%    =========================================================================
%
%  OUTPUT
%
%    h              handle to the new axes

% Copyright (C) 2013 by Michaël Zugaro
%
% This program 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.

% Defaults
gap = 0.1;

% Check number of parameters
if nargin < 2,
	error('Incorrect number of parameters (type ''help SideAxes'' for details).');
end

% Optional axes
if nargin == 2,
	s = location;
	location = a;
	a = gca;
elseif isstring_FMAT(a),
	varargin = {s,varargin{:}};
	s = location;
	location = a;
	a = gca;
end

% Check number of options
if mod(length(varargin),2) ~= 0,
	error('Incorrect number of parameters (type ''help SideAxes'' for details).');
end

% Check parameters
if ~ishandle(a),
	error('Incorrect axes (type ''help SideAxes'' for details).');
end
if ~isdscalar(s,'>0','<1'),
	error('Incorrect size (type ''help SideAxes'' for details).');
end
location = lower(location);
if ~isstring_FMAT(location,'top','bottom','left','right'),
	error('Incorrect location (type ''help SideAxes'' for details).');
end

% Parse parameter list
for i = 1:2:length(varargin),
	if ~ischar(varargin{i}),
		error(['Parameter ' num2str(i+2) ' is not a property (type ''help SideAxes'' for details).']);
	end
	switch(lower(varargin{i})),
		case 'gap',
			gap = varargin{i+1};
			if ~isdscalar(gap,'>=0','<1'),
				error('Incorrect value for property ''gap'' (type ''help SideAxes'' for details).');
			end
		otherwise,
			error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help SideAxes'' for details).']);
	end
end

% Resize target axes to make room for side axes

main = a;
position = get(main,'position');
x = position(1);
y = position(2);
currentWidth = position(3); % current width
currentHeight = position(4); % current height

% The following variables will be used only when adding left or right side axes
horizontalGap = currentWidth * gap;
newWidth = currentWidth - horizontalGap;
newMainWidth = newWidth * (1-s);
newSideWidth = newWidth * s;

% The following variables will be used only when adding left or right side axes
verticalGap = currentHeight * gap;
newHeight = currentHeight - verticalGap;
newMainHeight = newHeight * (1-s);
newSideHeight = newHeight * s;

xLims = get(main,'xlim');
yLims = get(main,'ylim');

switch(location),
	case 'top',
		set(main,'position',[x y currentWidth newMainHeight]);
		h = axes('position',[x y+newMainHeight+verticalGap currentWidth newSideHeight]);
		xlim(h,xLims);
	case 'bottom',
		set(main,'position',[x y+newSideHeight+verticalGap currentWidth newMainHeight]);
		h = axes('position',[x y currentWidth newSideHeight]);
		xlim(h,xLims);
	case 'right',
		set(main,'position',[x y newMainWidth currentHeight]);
		h = axes('position',[x+newMainWidth+horizontalGap y newSideWidth currentHeight]);
		ylim(h,yLims);
	case 'left',
		set(main,'position',[x+newSideWidth+horizontalGap y newMainWidth currentHeight]);
		h = axes('position',[x y newSideWidth currentHeight]);
		ylim(h,yLims);
end


function make
% detect platform

compstr = computer;
is64bit = strcmp(compstr(end-1:end),'64');

% compilation parameters

compile_params = cell(0);
if (is64bit)
  compile_params{1} = '-largeArrayDims';
end

% Compile files %

sources = {'mexutils.c'};

cd mex
disp('Compiling sampleTau...');
mex('sampleTau.cpp', sources{:},compile_params{:});
cd ..


function [files, status] = urldir(page, tag)
%
% Lists the files within a URL
%
% Collecion of folders:
% files = urldir(page, 'DIR')
% Collection of files:
% files = urldir(page, 'TXT')

% Created: A. Torralba, 2006
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% LabelMe, the open annotation tool
% Contribute to the database by labeling objects using the annotation tool.
%
% CSAIL, MIT
% 2006
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin == 1
    tag = '[dir]';
    tag = '/'
else
    tag = lower(tag);
    if strcmp(tag, 'dir')
        tag = '/';
    end
    if strcmp(tag, 'txt')
        tag = 'xml';
    end
    if strcmp(tag, 'img')
        tag = 'jpg';
    end
end

nl = length(tag);
nfiles = 0;
files = [];

% Read page
page = strrep(page, '\', '/');
status =0;
trials = 0;
while status == 0 && trials<10
    [webpage, status] = urlread(page, 'Timeout', 5);
    if status == 0
        disp(sprintf('Warning: Failure urldir %s. Trying again...',page))
        drawnow
        trials = trials+1;
    end
end

if trials == 10
    disp('warning: skipping this folder...')
    status = 1;
    return
end

if status
    % Parse page
    j1 = findstr(lower(webpage), '<a href="');
    j2 = findstr(lower(webpage), '');
    Nelements = length(j1);
    if Nelements>0
        for f = 1:Nelements
            % get HREF element
            chain = webpage(j1(f):j2(f));
            jc = findstr(lower(chain), '">');
            if ~isempty(jc)
                chain = deblank(chain(10:jc(1)-1));

                % check if it is the right type
                if length(chain)>length(tag)-1
                    if strcmp(chain(end-nl+1:end), tag)
                        nfiles = nfiles+1;
                        chain = strrep(chain, '%20', ' '); % replace space character
                        files(nfiles).name = chain;
                        files(nfiles).bytes = 1;
                    end
                end
            end
        end
    end
end


function dirText = dimFlip(dirText);
% flip direction text 'a <--> b' to read 'b <--> a'.
%
% Usage:
% dirText = dimFlip(dirText);
%
% ras, 11/05
I = findstr(dirText, ' <--> ');
if isempty(I), return; end
lhs = dirText(1:I);
rhs = dirText(I+5:end);
dirText = [rhs '<-->' lhs];
return


function colors = prtPlotUtilDarkenColors(colors)
% Internal function,
% xxx Need Help xxx

colors = colors - 0.1;
colors(colors < 0) = 0;


function n = numel(varargin)
% returns the number of tensors

n = 1;


function sClusters = in_clusters(ClusterFile, FileFormat)
% IN_CLUSTERS: Read clusters of channels from a file

% @=============================================================================
% 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, 2023

% Read file
switch (FileFormat)
    case 'BST'
        ClusterMat = load(ClusterFile);
        if ~isfield(ClusterMat, 'Clusters')
            error('Invalid Brainstorm clusters file: missing field "Clusters".');
        end
    case 'MNE'
        sMontage = in_montage_mne(FileName);
        for i = 1:length(sMontage)
            ClusterMat.Clusters(i).Label   = sMontage(i).Name;
            ClusterMat.Clusters(i).Sensors = sMontage(i).ChanNames;
        end
    otherwise
        error('Invalid file format.');
end
if isempty(ClusterMat.Clusters)
    error(['No clusters available in file: ' FileName]);
end

% Create standardized Brainstorm structure
sClusters = repmat(db_template('cluster'), 1, length(ClusterMat.Clusters));
% Loop on all the new clusters
for i = 1:length(ClusterMat.Clusters)
    sClusters(i).Sensors = ClusterMat.Clusters(i).Sensors;
    if isfield(ClusterMat.Clusters(i), 'Label') && ~isempty(ClusterMat.Clusters(i).Label)
        sClusters(i).Label = ClusterMat.Clusters(i).Label;
    end
    if isfield(ClusterMat.Clusters(i), 'Color') && ~isempty(ClusterMat.Clusters(i).Color)
        sClusters(i).Color = ClusterMat.Clusters(i).Color;
    end
    if isfield(ClusterMat.Clusters(i), 'Function') && ~isempty(ClusterMat.Clusters(i).Function)
        sClusters(i).Function = ClusterMat.Clusters(i).Function;
    end
end


function [fv, memo]= xvalutil_proc(fv, proc, memo)
% XVALUTIL_PROC - Function used in crossvalidation

% 2014-02 Benjamin Blankertz

if nargin<3,
  memo= cell(2, 0);
end

for k= 1:length(proc),
  procline= proc{k};
  isvar= cellfun(@ischar, procline);
  nVars= min(find(~isvar))-1;
  vars= procline(1:nVars);
  vals= cell(1, nVars);
  cmd= replace_vars(procline(nVars+1:end), memo);
  [fv, vals{:}]= cmd{1}(fv, cmd{2:end});
  memo= cat(2, memo, cat(1, vars, vals));
end

function cmd= replace_vars(cmd, memo)

for n= 1:length(cmd),
  if ischar(cmd{n}) && cmd{n}(1)=='$',
    varname= cmd{n}(2:end);
    idx= strmatch(varname, memo(1,:), 'exact');
    if isempty(idx),
      error('variable %s not defined in opt.Proc', varname);
    end
    cmd{n}= memo{2,idx};
  end
end