function spm_eeg_prep_ui(callback)
% User interface for spm_eeg_prep function performing several tasks
% for preparation of converted MEEG data for further analysis
% FORMAT spm_eeg_prep_ui(callback)
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_eeg_prep_ui.m 7169 2017-09-19 10:42:27Z vladimir $

spm('Pointer','Watch');

if ~nargin, callback = 'CreateMenu'; end

eval(callback);

spm('Pointer','Arrow');

end
%==========================================================================
% function CreateMenu
%==========================================================================
function CreateMenu

SVNrev = '$Rev: 7169 $';
spm('FnBanner', 'spm_eeg_prep_ui', SVNrev);
Finter = spm('FnUIsetup', 'M/EEG prepare', 0);

%-Draw top level menu
% ====== File ===================================
FileMenu = uimenu(Finter,'Label','File',...
    'Tag','EEGprepUI',...
    'HandleVisibility','on');

FileOpenMenu = uimenu(FileMenu, ...
    'Label','Open',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','O',...
    'Callback', 'spm_eeg_prep_ui(''FileOpenCB'')');

FileHeaderMenu = uimenu(FileMenu, ...
    'Label','Load header',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','H',...
    'Callback', 'spm_eeg_prep_ui(''FileHeaderCB'')');

FileImportMenu = uimenu(FileMenu, ...
    'Label','Import from workspace',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','I',...
    'Callback', 'spm_eeg_prep_ui(''FileImportCB'')');

FileSaveMenu = uimenu(FileMenu, ...
    'Label','Save',...
    'Separator','off',...
    'Tag','EEGprepUI',...
    'Enable','off',...
    'HandleVisibility', 'on',...
    'Accelerator','S',...
    'Callback', 'spm_eeg_prep_ui(''FileSaveCB'')');

FileExitMenu = uimenu(FileMenu, ...
    'Label','Quit',...
    'Separator','on',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Accelerator','Q',...
    'Callback', 'spm_eeg_prep_ui(''FileExitCB'')');

% ====== Batch inputs ======================================

BatchInputsMenu = uimenu(Finter,'Label','Batch inputs',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

BInputsChannelsMenu = uimenu(BatchInputsMenu, 'Label', 'Channel selection',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ChannelsCB'')');

BInputsTrialsMenu = uimenu(BatchInputsMenu, 'Label', 'Trial definition',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''TrialsCB'')');

BInputsEventsBIDSMenu = uimenu(BatchInputsMenu, ...
    'Label','Load events from BIDS',...
    'Separator','off',...
    'Enable','off',...
    'Tag','EEGprepUI',...
    'HandleVisibility', 'on',...
    'Callback', 'spm_eeg_prep_ui(''EventsBIDSCB'')');

BInputsEventsMenu = uimenu(BatchInputsMenu, 'Label', 'Event list',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EventsCB'')');

BInputsMontageMenu = uimenu(BatchInputsMenu, 'Label', 'Montage',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

BInputsSortCondMenu = uimenu(BatchInputsMenu, 'Label', 'Sort conditions',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''SortCondCB'')');

MontageCustomMenu = uimenu(BInputsMontageMenu, 'Label', 'Custom Montage',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''MontageCB'')');

MontageRereferenceMenu = uimenu(BInputsMontageMenu, 'Label', 'Re-reference',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''RereferenceCB'')');

MontageROIMenu = uimenu(BInputsMontageMenu, 'Label', 'ROI',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''roiCB'')');

% ====== Channel types ===============================

ChanTypeMenu = uimenu(Finter,'Label','Channel types',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

chanTypes = {'EEG', 'EOG', 'ECG', 'EMG', 'LFP', 'PHYS', 'ILAM', 'SRC', 'Other'};

for i = 1:length(chanTypes)
    CTypesMenu(i) = uimenu(ChanTypeMenu, 'Label', chanTypes{i},...
        'Tag','EEGprepUI',...
        'Enable', 'on', ...
        'HandleVisibility','on',...
        'Callback', 'spm_eeg_prep_ui(''ChanTypeCB'')');
end

CTypesRef2MEGMenu = uimenu(ChanTypeMenu, 'Label', 'MEGREF=>MEG',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on',...
    'Separator', 'on',...
    'Callback', 'spm_eeg_prep_ui(''MEGChanTypeCB'')');

CTypesDefaultMenu = uimenu(ChanTypeMenu, 'Label', 'Default',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on',...
    'Callback', 'spm_eeg_prep_ui(''ChanTypeDefaultCB'')');

CTypesBIDSMenu = uimenu(ChanTypeMenu, 'Label', 'From BIDS',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ChanTypeBIDSCB'')');

CTypesReviewMenu = uimenu(ChanTypeMenu, 'Label', 'Review',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ChanTypeCB'')');

% ====== Sensors ===================================

Coor3DMenu = uimenu(Finter,'Label','Sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

LoadMEGSensMenu = uimenu(Coor3DMenu, 'Label', 'Load MEG sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadMEGSensCB'')');

HeadshapeMenu = uimenu(Coor3DMenu, 'Label', 'Load MEG Fiducials/Headshape',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''HeadshapeCB'')');

LoadEEGSensMenu = uimenu(Coor3DMenu, 'Label', 'Load EEG sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'Separator', 'on', ...
    'HandleVisibility','on');

LoadEEGSensTemplateMenu = uimenu(LoadEEGSensMenu, 'Label', 'Assign default',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensTemplateCB'')');

LoadEEGSensMatMenu = uimenu(LoadEEGSensMenu, 'Label', 'From *.mat file',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensCB'')');

LoadEEGSensOtherMenu = uimenu(LoadEEGSensMenu, 'Label', 'Convert locations file',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''LoadEEGSensCB'')');

ReferenceMenu = uimenu(Coor3DMenu, 'Label', 'Define EEG referencing',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on');

ReferenceSelectMenu = uimenu(ReferenceMenu, 'Label', 'Select reference sensors',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ReferenceCB'')');

ReferenceMontageMenu = uimenu(ReferenceMenu, 'Label', 'Specify montage matrix',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''ReferenceCB'')');

CoregisterMenu = uimenu(Coor3DMenu, 'Label', 'Coregister',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''CoregisterCB'')');

% ====== 2D projection ===================================

Coor2DMenu = uimenu(Finter, 'Label','2D projection',...
    'Tag','EEGprepUI',...
    'Enable', 'off', ...
    'HandleVisibility','on');

EditMEGMenu = uimenu(Coor2DMenu, 'Label', 'Edit existing MEG',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EditExistingCoor2DCB'')');

EditEEGMenu = uimenu(Coor2DMenu, 'Label', 'Edit existing EEG',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''EditExistingCoor2DCB'')');

LoadTemplateMenu = uimenu(Coor2DMenu, 'Label', 'Load template',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''LoadTemplateCB'')');

SaveTemplateMenu = uimenu(Coor2DMenu, 'Label', 'Save template',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''SaveTemplateCB'')');

Project3DEEGMenu = uimenu(Coor2DMenu, 'Label', 'Project 3D (EEG)',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''Project3DCB'')');

Project3DMEGMenu = uimenu(Coor2DMenu, 'Label', 'Project 3D (MEG)',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''Project3DCB'')');

AddCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Add sensor',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''AddCoor2DCB'')');

DeleteCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Delete sensor',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''DeleteCoor2DCB'')');

UndoMoveCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Undo move',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''UndoMoveCoor2DCB'')');

ApplyCoor2DMenu = uimenu(Coor2DMenu, 'Label', 'Apply',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Separator', 'on', ...
    'Callback', 'spm_eeg_prep_ui(''ApplyCoor2DCB'')');

Clear2DMenu = uimenu(Coor2DMenu, 'Label', 'Clear',...
    'Tag','EEGprepUI',...
    'Enable', 'on', ...
    'HandleVisibility','on',...
    'Callback', 'spm_eeg_prep_ui(''Clear2DCB'')');
end

%==========================================================================
% function FileOpenCB
%==========================================================================
function FileOpenCB

D = spm_eeg_load;
setD(D);

update_menu;

end

%==========================================================================
% function FileHeaderCB
%==========================================================================
function FileHeaderCB

S = [];
S.mode = 'header';
[S.dataset, sts] = spm_select(1, '.*', 'Select M/EEG data file');
if ~sts, return; end
D = spm_eeg_convert(S);
setD(D);

update_menu;

end

%==========================================================================
% function FileImportCB
%==========================================================================
function FileImportCB

var = evalin('base', 'whos');
var = {var(ismember({var.class}, {'single', 'double'})).name};

[selection, ok]= listdlg('ListString', var, 'SelectionMode', 'single' ,'Name', 'Select variable' , 'ListSize', [400 300]);

if ok
    data = full(double(evalin('base', var{selection})));
    dim = size(data);
    ndim = length(dim);

    if length(dim)>3
        error('Data of up to 3 dimensions are supported.');
    end

    prompt = sprintf('%d|', dim);
    prompt(end) = [];

    chandim = spm_input('Number of channels:', 1, prompt, 1:length(dim));

    nchan = dim(chandim);

    otherdim = setdiff(1:ndim, chandim);

    if ndim == 3
        prompt = sprintf('%d|', dim(otherdim));
        prompt(end) = [];
        trialdim = spm_input('Number of trials:','+1', prompt, otherdim);

        ntrials = dim(trialdim);
    else
        trialdim = [];
        ntrials  = 1;
    end

    sampledim = setdiff(1:ndim, [trialdim, chandim]);
    nsamples  = dim(sampledim);

    data    = permute(data, [chandim, sampledim, trialdim]);

    fs      = spm_input('Sampling rate (Hz):', '+1','r');
    t_onset = spm_input('Time axis onset (ms):', '+1','r');

    fname   = spm_input('Output file name:', '+1','s');

    time    = (0:(nsamples-1))/fs - 1e-3*t_onset;

    for i = 1:ntrials
        ftdata.trial{i} = data(:, :, i);
        ftdata.time{i}  = time;
    end

    for i = 1:nchan
        ftdata.label{i, 1} = sprintf('Ch%d', i);
    end

    D = spm_eeg_ft2spm(ftdata, fname);

    spm_eeg_review(D);
    spm_eeg_review_callbacks('edit', 'prep');
end
end
%==========================================================================
% function FileSaveCB
%==========================================================================
function FileSaveCB

D = getD;
if ~isempty(D)
    D.save;
end

update_menu;

end

%==========================================================================
% function FileExitCB
%==========================================================================
function FileExitCB

spm_figure('Clear','Interactive');
spm('FigName','M/EEG prepare: done');

end

%==========================================================================
% function ChannelsCB
%==========================================================================
function ChannelsCB

D = getD;

[selection, ok]= listdlg('ListString', D.chanlabels, 'SelectionMode', 'multiple' ,'Name', 'Select channels' , 'ListSize', [400 300]);
if ~ok, return; end

[chanfilename, chanpathname] = uiputfile( ...
    {'*.mat', 'MATLAB File (*.mat)'}, 'Save channel selection as');

label = D.chanlabels(selection);

save(fullfile(chanpathname, chanfilename), 'label', spm_get_defaults('mat.format'));

end

%==========================================================================
% function TrialsCB
%==========================================================================
function TrialsCB

D = getD;

S = [];
S.D = D;
S.save = 1;
spm_eeg_definetrial(S);

CreateMenu;

setD(D);

update_menu;

end

%==========================================================================
% function EventsBIDSCB
%==========================================================================
function EventsBIDSCB

D = getD;

S = [];
S.task = 'loadbidsevents';
S.D = D;
[S.filename, sts] = spm_select(1, '.*_events.tsv$', 'Select BIDS tsv file');

if sts
    S.replace = spm_input('Replace existing','1','replace|add',[1 0], 1);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end

%==========================================================================
% function EventsCB
%==========================================================================
function EventsCB

D = getD;

ev = D.events;

if iscell(ev)
    sev = ev{1};
    for i = 2:numel(ev)
        sev = spm_cat_struct(sev, ev{i});
    end
else
    sev = ev;
end

selected = spm_eeg_select_event_ui(sev);

events = [];
for i = 1:size(selected, 1)
    events(i).type  = selected{i, 1};
    events(i).value = selected{i, 2};
end

if ~isempty(events)
    [filename, pathname] = uiputfile('*.mat', 'Save event list as');

    if ~isequal(filename, 0)
        save(fullfile(pathname, filename), 'events', spm_get_defaults('mat.format'));
    end
end
end

%==========================================================================
% function MontageCB
%==========================================================================
function MontageCB

D = getD;

label = D.chanlabels(D.indchantype({'EEG', 'EOG', 'ECG', 'EMG'}));

montage = [];
montage.labelorg = label;
montage.labelnew = label;
montage.tra = eye(numel(label));

spm_eeg_montage_ui(montage);

end

%==========================================================================
% function RereferenceCB
%==========================================================================
function RereferenceCB

D = getD;

% Get indices for just EEG channels and remove any bad channels
%--------------------------------------------------------------------------
eegchan  = D.indchantype('EEG');
goodind  = D.indchantype('EEG', 'GOOD');

if isempty(goodind)
    error('No good EEG channels.')
end

%-Get reference channel indices
%--------------------------------------------------------------------------
[selection, ok]= listdlg('ListString', D.chanlabels(goodind), 'SelectionMode', 'multiple' ,...
    'Name', 'Select reference channels' , 'ListSize', [400 300]);
if ~ok
    return;
end

refind  = goodind(selection);
badind  = D.indchantype('EEG', 'BAD');

goodind = find(ismember(eegchan, goodind));
badind  = find(ismember(eegchan,  badind));
refind  = find(ismember(eegchan,  refind));

tra                 = eye(length(eegchan));
tra(goodind,refind) = tra(goodind,refind) - 1/length(refind);
tra(badind, refind) = tra(badind,refind)  - 1/length(refind);

montage          = [];
montage.labelorg = D.chanlabels(eegchan);
montage.labelnew = D.chanlabels(eegchan);
montage.tra      = tra;

[filename, pathname] = uiputfile('*.mat', 'Save montage as');

if ~isequal(filename, 0)
    save(fullfile(pathname, filename), 'montage', spm_get_defaults('mat.format'));
end

end

%==========================================================================
% function roiCB
%==========================================================================
function roiCB

D = getD;

[modality, chanind]  = spm_eeg_modality_ui(D, 0, 1);

chanind = setdiff(chanind, D.badchannels);

if isempty(chanind)
    error(['No good ' modality ' channels.']);
end

montage = [];
montage.labelorg = D.chanlabels(chanind)';
montage.labelnew = {};
montage.tra      = [];
while 1
    roilabel = spm_input('ROI Label:', '+1', 's');

    [selection, ok]= listdlg('ListString', D.chanlabels(chanind), 'SelectionMode', 'multiple' ,...
        'Name', 'Select reference channels' , 'ListSize', [400 300]);
    if ok
       montage.labelnew{end+1, 1} = roilabel;
       tra = zeros(1, length(chanind));
       tra(selection) = 1;
       tra = tra./sum(tra);
       montage.tra = [montage.tra; tra];
    end

    if spm_input('Add another?','+1','yes|no',[0, 1], 0);
        break;
    end
end

[filename, pathname] = uiputfile('*.mat', 'Save montage as');

if ~isequal(filename, 0)
    save(fullfile(pathname, filename), 'montage', spm_get_defaults('mat.format'));
end

update_menu;

end

%==========================================================================
% function SortCondCB
%==========================================================================
function SortCondCB

D = getD;

S   = [];
S.task = 'sortconditions';
S.D = D;
oldcondlist = D.condlist;
S.condlist = cell(size(oldcondlist));
for i = 1:D.nconditions
    str = sprintf('%s|', oldcondlist{:});
    str = str(1:(end-1));

    ind = spm_input(['Select condition ' num2str(i)], 1, 'm', str, 1:numel(oldcondlist));
    S.condlist(i) = oldcondlist(ind);
    oldcondlist(ind) = [];
end

[filename, pathname] = uiputfile('*.mat', 'Save conditions list as');

if ~isequal(filename, 0)
    condlist = S.condlist;

    save(fullfile(pathname, filename), 'condlist', spm_get_defaults('mat.format'));
end

D = spm_eeg_prep(S);

setD(D);
update_menu;

end

%==========================================================================
% function ChanTypeCB
%==========================================================================
function ChanTypeCB

type = get(gcbo, 'Label');

D = getD;

if ~isempty(D)
    chanlist ={};
    for i = 1:D.nchannels
        if strncmp(D.chantype(i), 'MEG', 3) || strncmp(D.chantype(i), 'REF', 3)
            chanlist{i} = [num2str(i) '    Label:    ' D.chanlabels(i) '    Type:    ' D.chantype(i) , ' (nonmodifiable)'];
        else
            chanlist{i} = [num2str(i) '    Label:    ' D.chanlabels(i) '    Type:    ' D.chantype(i)];
        end

        chanlist{i} = [chanlist{i}{:}];
    end

    if strcmpi(type, 'review')
        listdlg('ListString', chanlist, 'SelectionMode', 'single', 'Name', 'Review channels', 'ListSize', [400 300]);
        return
    else

        [selection,ok]= listdlg('ListString', chanlist, 'SelectionMode', 'multiple',...
            'InitialValue', strmatch(type, D.chantype) ,'Name', ['Set type to ' type], 'ListSize', [400 300]);

        selection(strmatch('MEG', chantype(D, selection))) = [];

        if ok && ~isempty(selection)
            S.task = 'settype';
            S.D = D;
            S.ind = selection;
            S.type = type;
            D = spm_eeg_prep(S);
            setD(D);
        end
    end
end

update_menu;

end
%==========================================================================
% function MEGChanTypeCB
%==========================================================================
function MEGChanTypeCB

S = [];
S.D = getD;
S.task = 'settype';

switch get(gcbo, 'Label')
    case 'MEGREF=>MEG'
        dictionary = {
            'REFMAG',   'MEGMAG';
            'REFGRAD',  'MEGGRAD';
            };

        ind = spm_match_str(S.D.chantype, dictionary(:,1));

        grad = S.D.sensors('meg');
        if ~isempty(grad)
            % Under some montages only subset of the reference sensors are
            % in the grad
            [junk, sel] = intersect(S.D.chanlabels(ind), grad.label);
            ind = ind(sel);
        end

        S.ind = ind;

        [sel1, sel2] = spm_match_str(S.D.chantype(S.ind), dictionary(:, 1));

        S.type = dictionary(sel2, 2);

        D = spm_eeg_prep(S);
end

setD(D);
update_menu;

end
%==========================================================================
% function ChanTypeDefaultCB
%==========================================================================
function ChanTypeDefaultCB

S.D    = getD;
S.task = 'defaulttype';
D      = spm_eeg_prep(S);

setD(D);
update_menu;

end

%==========================================================================
% function ChanTypeBIDSCB
%==========================================================================
function ChanTypeBIDSCB

S = [];
S.D    = getD;
S.task = 'bidschantype';
[S.filename, sts] = spm_select(1, '.*_channels.tsv$', 'Select BIDS tsv file');

if sts
    D      = spm_eeg_prep(S);

    setD(D);
    update_menu;
end

end

%==========================================================================
% function LoadEEGSensTemplateCB
%==========================================================================
function LoadEEGSensTemplateCB

S.D    = getD;
S.task = 'defaulteegsens';

if strcmp(S.D.modality(1, 0), 'Multimodal')
    fid = fiducials(S.D);
    if ~isempty(fid)
        lblfid = fid.fid.label;

        S.regfid = match_fiducials({'nas'; 'lpa'; 'rpa'}, lblfid);
        S.regfid(:, 2) = {'spmnas'; 'spmlpa'; 'spmrpa'};
    else
        warndlg(strvcat('Could not match EEG fiducials for multimodal dataset.', ...
            '           EEG coregistration might fail.'));
    end
end

D = spm_eeg_prep(S);
setD(D);
update_menu;

end
%==========================================================================
% function LoadEEGSensCB
%==========================================================================
function LoadMEGSensCB

D = getD;

S = [];
S.task = 'loadmegsens';
S.D = D;
[S.source,sts] = spm_select(1, '\.*', 'Select a raw MEG data file');
if ~sts, return; end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function LoadEEGSensCB
%==========================================================================
function LoadEEGSensCB

D = getD;

switch get(gcbo, 'Label')
    case 'From *.mat file'
        [S.sensfile, sts] = spm_select(1,'mat','Select EEG sensors file');
        if ~sts, return, end
        S.source = 'mat';
        [S.headshapefile, sts] = spm_select(1,'mat','Select EEG fiducials file');
        if ~sts, return, end
        S.fidlabel = spm_input('Fiducial labels:', '+1', 's', 'nas lpa rpa');
    case 'Convert locations file'
        [S.sensfile, sts] = spm_select(1, '.*', 'Select locations file');
        if ~sts, return, end
        S.source = 'locfile';
end

if strcmp(D.modality(1, 0), 'Multimodal')
    if ~isempty(D.fiducials)
        S.regfid = {};
        if strcmp(S.source, 'mat')
            fidlabel = S.fidlabel;
            lblshape = {};
            fidnum = 0;
            while ~all(isspace(fidlabel))
                fidnum = fidnum+1;
                [lblshape{fidnum},fidlabel] = strtok(fidlabel);
            end
            if (fidnum < 3)
                error('At least 3 labeled fiducials are necessary');
            end
        else
            shape = ft_read_headshape(S.sensfile);
            lblshape = shape.fid.label;
        end

        fid = fiducials(D);
        lblfid = fid.fid.label;

        S.regfid = match_fiducials(lblshape, lblfid);
    else
        warndlg(strvcat('Could not match EEG fiducials for multimodal dataset.', ...
            '           EEG coregistration might fail.'));
    end
end

S.D    = D;
S.task = 'loadeegsens';
D      = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function HeadshapeCB
%==========================================================================
function HeadshapeCB

S = [];
S.D = getD;
S.task = 'headshape';

[S.headshapefile, sts] = spm_select(1, '.*', 'Select fiducials/headshape file');
if ~sts, return, end
S.source = 'convert';

shape = ft_read_headshape(S.headshapefile);
lblshape = shape.fid.label;

fid = fiducials(S.D);

if ~isempty(fid)
    lblfid = fid.fid.label;
    S.regfid = match_fiducials(lblshape, lblfid);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function CoregisterCB
%==========================================================================
function ReferenceCB

D = getD;

S = [];
S.D = D;
S.task = 'sens2chan';
switch get(gcbo, 'Label')
    case 'Select reference sensors'
        elec = D.sensors('EEG');

        [refind, ok]= listdlg('ListString', elec.label, 'SelectionMode', 'multiple' ,'Name', 'Select reference electrodes' , 'ListSize', [400 300]);
        if ~ok
            return;
        end

        S.refelec = elec.label(refind);
    case 'Specify montage matrix'

        sens  = D.sensors('EEG');
        label = D.chanlabels(D.indchantype('EEG'));

        [sel1, sel2] = spm_match_str(label, sens.label);

        montage = [];
        montage.labelorg = sens.label;
        montage.labelnew = label;
        montage.tra = zeros(numel(label), numel(sens.label));
        montage.tra(sub2ind(size(montage.tra), sel1, sel2)) = 1;

        S.montage = spm_eeg_montage_ui(montage);
end

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function CoregisterCB
%==========================================================================
function CoregisterCB

S = [];
S.D = getD;
S.task = 'coregister';

D = spm_eeg_prep(S);

% Bring the menu back
spm_eeg_prep_ui;

setD(D);

update_menu;

end
%==========================================================================
% function EditExistingCoor2DCB
%==========================================================================
function EditExistingCoor2DCB

D = getD;

switch get(gcbo, 'Label')
    case 'Edit existing MEG'
        xy = D.coor2D('MEG');
        label = D.chanlabels(strmatch('MEG', D.chantype));
    case 'Edit existing EEG'
        xy = D.coor2D('EEG');
        label = D.chanlabels(strmatch('EEG', D.chantype, 'exact'));
end

plot_sensors2D(xy, label);

update_menu;

end
%==========================================================================
% function LoadTemplateCB
%==========================================================================
function LoadTemplateCB

[sensorfile, sts] = spm_select(1, 'mat', 'Select sensor template file', ...
    [], fullfile(spm('dir'), 'EEGtemplates'));
if ~sts, return, end

template = load(sensorfile);

if isfield(template, 'Cnames') && isfield(template, 'Cpos')
    plot_sensors2D(template.Cpos, template.Cnames);
end

update_menu;

end
%==========================================================================
% function SaveTemplateCB
%==========================================================================
function SaveTemplateCB

handles=getHandles;

Cnames = handles.label;
Cpos = handles.xy;
Rxy = 1.5;
Nchannels = length(Cnames);

[filename, pathname] = uiputfile('*.mat', 'Save channel template as');

save(fullfile(pathname, filename), 'Cnames', 'Cpos', 'Rxy', 'Nchannels', spm_get_defaults('mat.format'));

end
%==========================================================================
% function Project3DCB
%==========================================================================
function Project3DCB

D = getD;

switch get(gcbo, 'Label')
    case 'Project 3D (EEG)'
        modality = 'EEG';
        chanind  = D.indchantype('EEG');
    case 'Project 3D (MEG)'
        modality = 'MEG';
        chanind  = D.indchantype('MEGANY');
end

if ~isfield(D, 'val')
    D.val = 1;
end

if isfield(D, 'inv') && isfield(D.inv{D.val}, 'datareg')
    datareg = D.inv{D.val}.datareg;
    ind     = strmatch(modality, {datareg(:).modality}, 'exact');
    sens    = datareg(ind).sensors;
else
    sens    = D.sensors(modality);
end

[xy, label] = spm_eeg_project3D(sens, modality);

[sel1, sel2] = spm_match_str(D.chanlabels(chanind), label);

plot_sensors2D(xy(:, sel2), label(sel2));

update_menu;

end
%==========================================================================
% function AddCoor2DCB
%==========================================================================
function AddCoor2DCB

newlabel = spm_input('Label?', '+1', 's');

if isempty(newlabel)
    return;
end

coord = spm_input('Coordinates [x y]', '+1', 'r', '0.5 0.5', 2);

handles = getHandles;

if ~isfield(handles, 'xy')
    handles.xy = [];
end

if ~isfield(handles, 'xy')
    handles.xy = [];
end

if ~isfield(handles, 'label')
    handles.label = {};
end

plot_sensors2D([handles.xy coord(:)], ...
    [handles.label newlabel]);

update_menu;

end
%==========================================================================
% function ApplyCoor2DCB
%==========================================================================
function ApplyCoor2DCB

handles = getHandles;
D = getD;

S = [];
S.task = 'setcoor2d';
S.D = D;
S.xy = handles.xy;
S.label = handles.label;

D = spm_eeg_prep(S);

setD(D);

update_menu;

end
%==========================================================================
% function update_menu
%==========================================================================
function update_menu

Finter = spm_figure('GetWin','Interactive');
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'File'), 'Enable', 'on');

IsEEG = 'off';
IsMEG = 'off';
IsEpochable = 'off';
HasEvents   = 'off';
IsEpoched   = 'off';
HasPlanar   = 'off';
HasSensors  = 'off';
HasSensorsEEG = 'off';
ReferenceSelectable = 'off';
HasSensorsMEG = 'off';
HasChannelsMEGREF = 'off';
HasFiducials = 'off';
HasDefaultLocs = 'off';
HasHistory = 'off';
if isa(get(Finter, 'UserData'), 'meeg')
    Dloaded = 'on';

    D = getD;

    if ~isempty(D.indchantype('EEG'))
        IsEEG = 'on';
    end

    if ~isempty(D.indchantype('MEG'));
        IsMEG = 'on';
    end

    if ~isempty(D.events)
        HasEvents = 'on';
    end

    if isequal(D.type, 'continuous') && ~isempty(D.events);
        IsEpochable = 'on';
    end

    if ~isequal(D.type, 'continuous')
        IsEpoched = 'on';
    end

    [res, list] = modality(D, 1, 1);
    if ismember('MEGPLANAR', list);
        HasPlanar = 'on';
    end

    if ~isempty(D.indchantype('REF'));
        HasChannelsMEGREF = 'on';
    end

    if ~isempty(D.sensors('EEG')) || ~isempty(D.sensors('MEG'))
        HasSensors = 'on';
    end

    if ~isempty(D.sensors('EEG'))
        HasSensorsEEG = 'on';

        elec = D.sensors('EEG');
        if ~isfield(elec, 'tra') &&  (size(elec.elecpos, 1) == numel(elec.label))
            ReferenceSelectable = 'on';
        end
    end

    if  ~isempty(D.sensors('MEG'))
        HasSensorsMEG = 'on';
    end

    if  ~isempty(D.fiducials)
        HasFiducials = 'on';
    end

    template_sfp = dir(fullfile(spm('dir'), 'EEGtemplates', '*.sfp'));
    template_sfp = {template_sfp.name};
    ind = strmatch([ft_senstype(D.chanlabels(D.indchantype('EEG'))) '.sfp'], template_sfp, 'exact');

    if ~isempty(ind) || ft_senstype(D.chanlabels(D.indchantype('EEG')), 'ext1020')
        HasDefaultLocs = 'on';
    end

    if  ~isempty(D.history)
        HasHistory = 'on';
    end

else
    Dloaded = 'off';
end

handles = getHandles;

IsTemplate = 'off';
IsSelected = 'off';
IsMoved = 'off';
if ~isempty(handles)
    if isfield(handles, 'xy') && size(handles.xy, 1)>0
        IsTemplate = 'on';
    end

    if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
        IsSelected = 'on';
    end

    if isfield(handles, 'lastMoved')
        isMoved = 'on';
    end
end

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Save'), 'Enable', 'on');
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load events from BIDS'), 'Enable', Dloaded);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Batch inputs'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Trial definition'), 'Enable', IsEpochable);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Event list'), 'Enable', HasEvents);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Montage'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Re-reference'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'ROI'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Sort conditions'), 'Enable', IsEpoched);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Channel types'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Sensors'), 'Enable', Dloaded);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', '2D projection'), 'Enable', Dloaded);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'MEGREF=>MEG'), 'Enable', HasChannelsMEGREF);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Assign default'), 'Enable', HasDefaultLocs);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load EEG sensors'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load MEG sensors'), 'Enable', IsMEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Load MEG Fiducials/Headshape'), 'Enable', HasSensorsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Headshape'), 'Enable', HasSensorsMEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Define EEG referencing'), 'Enable', HasSensorsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Select reference sensors'), 'Enable', ReferenceSelectable);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Coregister'), 'Enable', HasSensors);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Edit existing EEG'), 'Enable', IsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Edit existing MEG'), 'Enable', IsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Project 3D (EEG)'), 'Enable', HasSensorsEEG);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Project 3D (MEG)'), 'Enable', HasSensorsMEG);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Delete sensor'), 'Enable', IsSelected);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Undo move'), 'Enable', IsMoved);

set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Apply'), 'Enable', IsTemplate);
set(findobj(Finter,'Tag','EEGprepUI', 'Label', 'Clear'), 'Enable', IsTemplate);

delete(setdiff(findobj(Finter), [Finter; findobj(Finter,'Tag','EEGprepUI')]));

if strcmp(Dloaded, 'on') && isfield(D,'PSD') && D.PSD == 1
    try
        hc = get(Finter,'children');
        hc = findobj(hc,'flat','type','uimenu');
        hc = findobj(hc,'flat','label','File');
        delete(hc)
    end
    uicontrol(Finter,...
        'style','pushbutton','string','OK',...
        'callback','spm_eeg_review_callbacks(''get'',''prep'')',...
        'tooltipstring','Send changes to ''SPM Graphics'' window',...
        'BusyAction','cancel',...
        'Interruptible','off',...
        'Tag','EEGprepUI');
end

figure(Finter);

end
%==========================================================================
% function getD
%==========================================================================
function D = getD

Finter = spm_figure('GetWin','Interactive');
D = get(Finter, 'UserData');
if ~isa(D, 'meeg')
    D = [];
end

end
%==========================================================================
% function setD
%==========================================================================
function setD(D)
Finter = spm_figure('GetWin','Interactive');
set(Finter, 'UserData', D);

end
%==========================================================================
% function getHandles
%==========================================================================
function handles = getHandles
Fgraph = spm_figure('GetWin','Graphics');
handles = get(Fgraph, 'UserData');

end
%==========================================================================
% function setHandles
%==========================================================================
function setHandles(handles)
Fgraph = spm_figure('GetWin','Graphics');
set(Fgraph, 'UserData', handles);

end
%==========================================================================
% function plot_sensors2D
%==========================================================================
function plot_sensors2D(xy, label)

Fgraph = spm_figure('GetWin','Graphics');

spm_clf(Fgraph);

handles = [];

if ~isempty(xy)
    if size(xy, 1) ~= 2
        xy = xy';
    end

    xy(xy < 0.05) = 0.05;
    xy(xy > 0.95) = 0.95;

    handles.h_lbl=text(xy(1,:), xy(2, :),strvcat(label),...
        'FontSize', 9,...
        'Color','r',...
        'FontWeight','bold');

    set(handles.h_lbl, 'ButtonDownFcn', 'spm_eeg_prep_ui(''LabelClickCB'')');

    hold on

    handles.h_el = [];
    for i=1:size(xy, 2)
        handles.h_el(i) = plot(xy(1,i), xy(2,i), 'or');
    end

    set(handles.h_el,'MarkerFaceColor','r','MarkerSize', 2,'MarkerEdgeColor','k');

    handles.TemplateFrame = ...
        plot([0.05 0.05 0.95 0.95 0.05], [0.05 0.95 0.95 0.05 0.05], 'k-');

    axis off;

end

handles.xy = xy;
handles.label = label(:)';

setHandles(handles);

update_menu;

end
%==========================================================================
% function DeleteCoor2DCB
%==========================================================================
function DeleteCoor2DCB

handles = getHandles;
graph = spm_figure('GetWin','Graphics');

if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
    set(graph, 'WindowButtonDownFcn', '');

    label=get(handles.labelSelected, 'String');
    ind=strmatch(label, handles.label, 'exact');

    delete([handles.labelSelected handles.pointSelected]);

    handles.xy(:, ind)=[];
    handles.label(ind) = [];

    plot_sensors2D(handles.xy, handles.label)
end

end
%==========================================================================
% function UndoMoveCoor2DCB
%==========================================================================
function UndoMoveCoor2DCB

handles = getHandles;

if isfield(handles, 'lastMoved')
    label = get(handles.lastMoved(end).label, 'String');
    ind = strmatch(label, handles.label, 'exact');
    handles.xy(:, ind) = handles.lastMoved(end).coords(:);

    set(handles.lastMoved(end).point, 'XData', handles.lastMoved(end).coords(1));
    set(handles.lastMoved(end).point, 'YData', handles.lastMoved(end).coords(2));
    set(handles.lastMoved(end).label, 'Position', handles.lastMoved(end).coords);

    if length(handles.lastMoved)>1
        handles.lastMoved = handles.lastMoved(1:(end-1));
    else
        handles = rmfield(handles, 'lastMoved');
    end

    setHandles(handles);
    update_menu;
end

end
%==========================================================================
% function LabelClickCB
%==========================================================================
function LabelClickCB

handles=getHandles;
Fgraph = spm_figure('GetWin','Graphics');

if isfield(handles, 'labelSelected') && ~isempty(handles.labelSelected)
    if handles.labelSelected == gcbo
        set(handles.labelSelected, 'Color', 'r');
        set(handles.pointSelected,'MarkerFaceColor', 'r');
        set(Fgraph, 'WindowButtonDownFcn', '');
    else
        handles.pointSelected=[];
        handles.labelSelected=[];
    end
else
    set(Fgraph, 'WindowButtonDownFcn', 'spm_eeg_prep_ui(''LabelMoveCB'')');

    coords = get(gcbo, 'Position');

    handles.labelSelected=gcbo;
    handles.pointSelected=findobj(gca, 'Type', 'line',...
        'XData', coords(1), 'YData', coords(2));

    set(handles.labelSelected, 'Color', 'g');
    set(handles.pointSelected,'MarkerFaceColor', 'g');
end

setHandles(handles);

update_menu;

end
%==========================================================================
% function LabelMoveCB
%==========================================================================
function LabelMoveCB

handles = getHandles;
Fgraph = spm_figure('GetWin','Graphics');

coords=mean(get(gca, 'CurrentPoint'));

coords(coords < 0.05) = 0.05;
coords(coords > 0.95) = 0.95;

set(handles.pointSelected, 'XData', coords(1));
set(handles.pointSelected, 'YData', coords(2));

set(handles.labelSelected, 'Position', coords);
set(handles.labelSelected, 'Color', 'r');
set(handles.pointSelected,'MarkerFaceColor','r','MarkerSize',2,'MarkerEdgeColor','k');
set(Fgraph, 'WindowButtonDownFcn', '');
set(Fgraph, 'WindowButtonMotionFcn', 'spm_eeg_prep_ui(''CancelMoveCB'')');

labelind=strmatch(get(handles.labelSelected, 'String'), handles.label);

if isfield(handles, 'lastMoved')
    handles.lastMoved(end+1).point = handles.pointSelected;
    handles.lastMoved(end).label = handles.labelSelected;
    handles.lastMoved(end).coords = handles.xy(:, labelind);
else
    handles.lastMoved.point = handles.pointSelected;
    handles.lastMoved.label = handles.labelSelected;
    handles.lastMoved.coords = handles.xy(:, labelind);
end

handles.xy(:, labelind) = coords(1:2)';

setHandles(handles);

update_menu;

end
%==========================================================================
% function CancelMoveCB
%==========================================================================
function CancelMoveCB

Fgraph = spm_figure('GetWin','Graphics');
handles = getHandles;

handles.pointSelected=[];
handles.labelSelected=[];
set(Fgraph, 'WindowButtonMotionFcn', '');

setHandles(handles);

update_menu;

end
%==========================================================================
% function Clear2DCB
%==========================================================================
function Clear2DCB

plot_sensors2D([], {});

update_menu;

end
%==========================================================================
% function match_fiducials
%==========================================================================
function regfid = match_fiducials(lblshape, lblfid)

if numel(intersect(upper(lblshape), upper(lblfid))) < 3
    if numel(lblshape)<3 || numel(lblfid)<3
        warndlg('3 fiducials are required');
        return;
    else
        regfid = {};
        for i = 1:length(lblfid)
            [selection,ok]= listdlg('ListString',lblshape, 'SelectionMode', 'single',...
                'InitialValue', strmatch(upper(lblfid{i}), upper(lblshape)), ...
                'Name', ['Select matching fiducial for ' lblfid{i}], 'ListSize', [400 300]);

            if ~ok
                continue
            end

            regfid = [regfid; [lblfid(i) lblshape(selection)]];
        end

        if size(regfid, 1) < 3
            warndlg('3 fiducials are required to load headshape');
            return;
        end
    end
else
    [sel1, sel2] = spm_match_str(upper(lblfid), upper(lblshape));
    lblfid = lblfid(sel1);
    lblshape = lblshape(sel2);
    regfid = [lblfid(:) lblshape(:)];
end
end


function cvx_setspath

%CVX_SETSPATH   Sets the cvx solver path.
%   CVX_SETSPATH adds the internal cvx solver directories to Matlab's path
%   so that the CVX system can find the functions that they contain. There
%   is no reason to call this function during normal use of CVX; it is done
%   automatically as needed. However, if you are debugging CVX, calling
%   this function can help to insure that breakpoints stay valid.

global cvx___
osolv = cvx___.solvers.active;
if isempty( cvx___.problems ),
    nsolv = cvx___.solvers.selected;
else
    nsolv = cvx___.problems(end).solver;
end
if osolv ~= nsolv,
    opath = [];
    npath = [];
    needupd = false;
    if osolv,
        tstr = cvx___.solvers.list(osolv).path;
        if ~isempty( tstr ),
            opath = path;
            npath = strrep(opath,tstr,'');
            needupd = true;
        end
        cvx___.solvers.active = 0;
    end
    if nsolv,
        tstr = cvx___.solvers.list(nsolv).path;
        if ~isempty( tstr ),
            if isempty(opath), opath = path; end
            if isempty(npath), npath = opath; end
            npath = [ tstr, npath ];
            needupd = true;
        end
        cvx___.solvers.active = nsolv;
    end
    if needupd,
        path(npath);
    end
end

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


%% string get_file_extension(string)
% filePath - string
% ext - string
%
function [ext] = get_file_extension(fileName)
  %ext = fileName(length(fileName)-2:length(fileName));
  [~,~,ext] = fileparts(fileName);
  ext = ext(2:end);
end


function yn = strcontains(C1, C2)
%STRCONTAINS Does a cell array contain a particular string?
%
% YN = STRCONTAINS(C1, C2).  C1 and C2 should be a cell array of strings
% and a single string, in either order.  If the single string is one of
% the strings in the cell array, the function returns true, otherwise
% false.
%
% $Author: rcotton $  $Date: 2010/10/01 13:58:36 $	$Revision: 1.1 $
% $Copyright: Health and Safety Laboratory 2010 $

% Basic input checking
if (~iscellstr(C1) || ~ischar(C2)) && (~iscellstr(C2) || ~ischar(C1))
   warning('strcontains:BadInputs', ...
      'Inputs should be a cell array of strings and a single string');
   yn = false;
   return;
end

yn = any(strcmp(C1, C2));
end


function w = set(w, varargin)
   %SET Set properties for waveform object(s)
   %   w = Set(w,prop_name, val, [prop_name, val])
   %   SET is one of the two gateway functions of an object, such as waveform.
   %   Properties that are changed through SET are typechecked and otherwise
   %   scrutinized before being stored within the waveform object.  This
   %   ensures that the other waveform methods are all retrieving valid data,
   %   thereby increasing the reliability of the code.
   %
   %   Another strong advantage to using SET and GET to change  and retrieve
   %   properties, rather than just assigning them to the waveform directly,
   %   is that the underlying data structure can change and grow without
   %   harming the code that is written based on the waveform object.
   %
   %   Valid property names:
   %
   %       STATION, CHANNEL, FREQ, DATA, UNITS: self explanatory
   %       START :         Set the start-time using matlab format or string
   %       START_EPOCH:    Set the start-time using epoch (antelope) time
   %       SAMPLE_LENGTH : adjusts the length of the data to length VAL.
   %                       If this number is shorter than existing data, SET
   %                       truncates after sample VAL.  If it is longer, then
   %                       SET will pad the end with zeroes to get length VAL.
   %
   %       If user-defined fields were added to the waveform (ie, through
   %       addField), these fieldnames are also available through set.
   %
   %       for example
   %           % create a waveform object, and add a field called DUMMY with
   %           % the value 12
   %           w = addfield(waveform,'DUMMY',12);
   %
   %           % change the value of the DUMMY field
   %           w = set(w,'DUMMY',32);
   %
   %           % change the station and channel at once
   %           w = set(w,'Station','YCK', 'Channel', 'BHZ');
   %
   %   Batch changes can be made if input w is a matrix (use with care!)
   %
   %   NOTE: There is no set support for "version".  This is handled entirely
   %         within the waveform constructor and the loadobj function.
   %
   % NOTE: To avoid logging the history, append the argument 'nohist'
   %
   %  See also WAVEFORM/GET

   % VERSION: 1.1 of waveform objects
   % AUTHOR: Celso Reyes (celso@gi.alaska.edu)
   % LASTUPDATE: March 15, 2009

   %4/22/08 added synonym for sample_length of data_length

   %global dep2mep

   % check to see if history is bypassed

   idx = numel(varargin);
   while idx > 1
      prop_name = upper(varargin{idx-1});
      val = varargin{idx};
      idx = idx - 2;
      %{
Vidx = 1 : numel(varargin);

while numel(Vidx) >= 2
  prop_name = upper(varargin{Vidx(1)});
  val = varargin{Vidx(2)};
      %}

      %for n = 1 : numel(w);
      %out = w(n);

      switch prop_name
         case 'SCNLOBJECT'
            switch class(val)
               case 'scnlobject'
                  [w.cha_tag] = deal(get(val,'channeltag'));
               case 'ChannelTag'
                  [w.cha_tag] = deal(val);
               case 'char'
                  [w.cha_tag] = deal(ChannelTag.parse(val));
               otherwise
                  % try it!
                  [w.cha_tag] = deal(ChannelTag(val));
                  % error('Waveform:set:propertyTypeMismatch','Expected a SCNLOBJECT or CHANNELTAG');
            end
            % whine while we refractor
            warning('Use set(ChannelTag) instead') % TODO: Decide if better generic name exists

         case {'CHANNELINFO', 'CHANNELTAG'}
            switch class(val)
               case 'scnlobject'
                  [w.cha_tag] = deal(ChannelTag(char(val)));
               case 'ChannelTag'
                  [w.cha_tag] = deal(val);
               case 'char'
                  [w.cha_tag] = deal(ChannelTag(val));
               otherwise
                  % try it!
                  [w.cha_tag] = deal(ChannelTag(val));
                  % error('Waveform:set:propertyTypeMismatch','Expected a SCNLOBJECT or CHANNELTAG');
            end
         case {'STATION','NETWORK', 'CHANNEL','LOCATION'}
            if ~ischar(val)
               error('Waveform:set:propertyTypeMismatch',...
                  '%s should be a string not a %s', prop_name,class(val));
            end
            all_chantags = set([w.cha_tag],prop_name,val);
            for n=1:numel(w)
               w(n).cha_tag = all_chantags(n);
            end

         case {'FS', 'FREQ'}
            if ~isnumeric(val)
               error('Waveform:set:propertyTypeMismatch',...
                  'Frequency should be numeric, not %s', class(val));
            end
            if numel(w)==1
               w.Fs = val(1);
            else
               [w.Fs] = deal(val(1));
            end

         case {'START', 'START_MATLAB'}
            %AUTOMATICALLY figures out whether date is antelope or matlab
            %format;
            if numel(val) == 1
               %expecting a number
               if numel(w) == 1
                  w.start = val;
               else
                  [w.start] = deal(val);
               end
            else
               %might be char, might be vector. give it a shot.
               if numel(w) == 1
                  w.start = datenum(val);
               else
                  [w.start] = deal(datenum(val));
               end
               % else
               %   error('Waveform:set:propertyTypeMismatch',...
               %    'Start time not assigned... Unknown value type: %s', class(val));
            end

         case {'START_ANTELOPE', 'START_EPOCH'}
            if ~isnumeric(val),
               error('Waveform:set:propertyTypeMismatch',...
                  'Epoch time should be numeric, not %s', class(val));
            end
            [w.start] = deal(datenum(dep2mep(val)));

         case 'DATA'
            if ~isnumeric(val)
               error('Waveform:set:propertyTypeMismatch',...
                  'DATA should be numeric, not %s', class(val));
            end
            if numel(w) == 1
               w.data = val(:);
            else
               [w.data] = deal(val(:)); %always a column
            end

         case 'UNITS'
            if ~ischar(val)
               error('Waveform:set:propertyTypeMismatch',...
                  'UNITS should be a string, not %s',class(val));
            end
            [w.units] = deal(val);

         case {'SAMPLE_LENGTH','DATA_LENGTH'} % chop data or zero pad to specific length
            if ~isnumeric(val),
               error('Waveform:set:propertyTypeMismatch',...
                  'SAMPLE_LENGTH should be numeric, not %s',class(val));
            end
            for n=1:numel(w);
               if numel(w(n).data) >= val
                  w(n).data = w(n).data(1:val,1);  % shrink the data
               else
                  w(n).data(numel(w(n).data)+1 :val , 1) = 0; %zero pad
               end
            end

         case 'HISTORY'
            w = addhistory(w, val);

         otherwise
            for n=1:numel(w)
               switch prop_name
                  case w(n).misc_fields
                     %mask = ismember(w(n).misc_fields, prop_name);
                     mask = strcmp(prop_name,w(n).misc_fields);
                     w(n).misc_values(mask) = {val};
                  otherwise
                     error('Waveform:set:unknownProperty',...
                        'can''t understand property name : %s', prop_name);
               end %switch
            end %n
      end %switch

      % Vidx(1:2) = []; %done with those parameters, move to the next ones...

   end
end


function varargout = spm_rmpath(d)
% Recursively removes SPM paths from the MATLAB path
%   SPM_RMPATH checks if the file spm.m is found and removes the
%   path to that file and any subdirectories below it from the MATLAB
%   path.
%
%   P = SPM_RMPATH performs the same function as above and returns the
%   cleaned path string in P.
%
%   SPM_RMPATH(D) strips the path string D from the MATLAB path.
%
%   P = SPM_RMPATH(D) strips the path string D from the MATLAB path and
%   returns the cleaned path string in P.
%
%   See also PATH, ADDPATH, RMPATH, GENPATH, PATHTOOL, SAVEPATH.
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Darren Gitelman & Guillaume Flandin
% $Id: spm_rmpath.m 4025 2010-07-29 11:10:15Z guillaume $

varargout = {};
if ~nargin
    % Get the actual SPM directory
    try, d = spm('dir');
    catch, return; end
end

% Recursively remove directories in the MATLAB path
p = textscan(path,'%s','delimiter',pathsep); p = p{1};
i = strncmp(d,p,length(d)); P = p(i); p(i) = [];
if ~nargin && ~isempty(P)
    fprintf('Removed %s paths starting from base path: "%s"\n',spm('ver','',1),d);
elseif ~isempty(P)
    fprintf('Removed paths starting from base path: "%s" from:\n',d);
else
    fprintf('No matching path strings found to remove\n')
end
if numel(P), fprintf('\t%s\n',P{:}); end

% Set the new MATLAB path
p = strcat(p,pathsep);
path(strcat(p{:}));

% Return the cleaned path if requested
if nargout
    varargout{1} = p;
end


function obj = initializeObj(obj)
	% DESCRIPTION
	% Biafra Ahanonu
	% started: 2014.07.31 - branch from calciumImagingAnalysis 2020.05.07 [15:47:29]
	% inputs
		%
	% outputs
		%

	% changelog
		% 2020.06.05 [11:29:24] - Improved toolbox check.
		% 2021.08.10 [09:57:36] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
		% 2021.11.16 [11:13:07] - Wrap calls to diverging_map to avoid potential ciatah initialization errors on macOS.
		% 2022.06.27 [12:48:22] - Ensure all save paths (pics, data, log, settings, video, etc.) use absolute CIAtah private directory to match CIAtah default properties initialization and to avoid instances where users have non-CIAtah root path working directory.
	% TODO
		%

	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	% load dependencies.
	% loadBatchFxns();
	cnmfVersionDirLoad('none','displayOutput',0);
	% [success] = cnmfVersionDirLoad('cnmfe');

	% try
	% 	% Attempt to load the VERSION from file
	% 	verN = importdata('ciapkg\VERSION');
	% 	obj.classVersion = verN{1};
	% catch

	% end

	% Load colormaps
	obj.colormap = customColormap({[0 0 1],[1 1 1],[0.5 0 0],[1 0 0]});
	obj.colormapAlt = customColormap({[0 0 0.7],[1 1 1],[0.7 0 0]});
	% obj.colormapAlt2 = diverging_map(linspace(0,1,100),[0 0 0.7],[0.7 0 0]);

	% try-catch to handle cases in which diverging_map is not in the path.
	try
		obj.colormapAlt2 = diverging_map(linspace(0,1,100),[0 0 0.7],[0.7 0 0]);
	catch
		obj.colormapAlt2 = customColormap({[0 0.1 0.7],[1 1 1],[0.7 0.1 0]});
	end

	% Check required toolboxes are available, warn if not
	disp(repmat('*',1,42))
	ciapkg.io.dependencyCheck();
	disp(repmat('*',1,42))

	% Ensure date paths are up to date
	obj.picsSavePath = [ciapkg.getDir() filesep 'private' filesep 'pics' filesep datestr(now,'yyyymmdd','local') filesep];
	obj.dataSavePath = [ciapkg.getDir() filesep 'private' filesep 'data' filesep datestr(now,'yyyymmdd','local') filesep];
	obj.logSavePath = [ciapkg.getDir() filesep 'private' filesep 'logs' filesep datestr(now,'yyyymmdd','local') filesep];
	obj.settingsSavePath = [ciapkg.getDir() filesep 'private' filesep 'settings'];
	obj.videoSaveDir = [ciapkg.getDir() filesep 'private' filesep 'vids' filesep datestr(now,'yyyymmdd','local') filesep];

	% ensure private folders are set
	if ~exist(obj.picsSavePath,'dir');mkdir(obj.picsSavePath);fprintf('Creating directory: %s\n',obj.picsSavePath);end
	if ~exist(obj.dataSavePath,'dir');mkdir(obj.dataSavePath);fprintf('Creating directory: %s\n',obj.dataSavePath);end
	if ~exist(obj.logSavePath,'dir');mkdir(obj.logSavePath);fprintf('Creating directory: %s\n',obj.logSavePath);end
	if ~exist(obj.settingsSavePath,'dir');mkdir(obj.settingsSavePath);fprintf('Creating directory: %s\n',obj.settingsSavePath);end
	if ~exist(obj.videoSaveDir,'dir');mkdir(obj.videoSaveDir);fprintf('Creating directory: %s\n',obj.videoSaveDir);end

	% load user specific settings
	loadUserSettings = [obj.settingsSavePath filesep 'calciumImagingAnalysisInitialize.m'];
	if exist(loadUserSettings,'file')~=0
		run(loadUserSettings);
	else
		% create privateLoadBatchFxns.m
	end

	% if use puts in a single folder or a path to a txt file with folders
	if ~isempty(obj.rawSignals)&ischar(obj.rawSignals)
		if isempty(regexp(obj.rawSignals,'.txt'))&exist(obj.rawSignals,'dir')==7
			% user just inputs a single directory
			obj.rawSignals = {obj.rawSignals};
		else
			% user input a file linking to directories
			fid = fopen(obj.rawSignals, 'r');
			tmpData = textscan(fid,'%s','Delimiter','\n');
			obj.rawSignals = tmpData{1,1};
			fclose(fid);
		end
		obj.inputFolders = obj.rawSignals;
		obj.dataPath = obj.rawSignals;
	end
	% add subject information to object given datapath
	if ~isempty(obj.dataPath)
		obj.modelGetFileInfo();
	else
		disp('No folder paths input, run modelAddNewFolders method to add new folders.');
		% warning('Input data paths for all files!!! option: dataPath')
	end
	if ~isempty(obj.discreteStimulusTable)&~strcmp(class(obj.discreteStimulusTable),'table')
		obj.modelReadTable('table','discreteStimulusTable');
		obj.modelTableToStimArray('table','discreteStimulusTable','tableArray','discreteStimulusArray','nameArray','stimulusNameArray','idArray','stimulusIdArray','valueName',obj.stimulusTableValueName,'frameName',obj.stimulusTableFrameName);
	end
	if ~isempty(obj.continuousStimulusTable)&~strcmp(class(obj.continuousStimulusTable),'table')
		obj.delimiter = ',';
		obj.modelReadTable('table','continuousStimulusTable','addFileInfoToTable',1);
		obj.delimiter = ',';
		obj.modelTableToStimArray('table','continuousStimulusTable','tableArray','continuousStimulusArray','nameArray','continuousStimulusNameArray','idArray','continuousStimulusIdArray','valueName',obj.stimulusTableValueName,'frameName',obj.stimulusTableFrameName,'grabStimulusColumnFromTable',1);
	end
	% load behavior tables
	if ~isempty(obj.behaviorMetricTable)&~strcmp(class(obj.behaviorMetricTable),'table')
		obj.modelReadTable('table','behaviorMetricTable');
		obj.modelTableToStimArray('table','behaviorMetricTable','tableArray','behaviorMetricArray','nameArray','behaviorMetricNameArray','idArray','behaviorMetricIdArray','valueName','value');
	end
	% modify stimulus naming scheme
	if ~isempty(obj.stimulusNameArray)
		obj.stimulusSaveNameArray = obj.stimulusNameArray;
		obj.stimulusNameArray = strrep(obj.stimulusNameArray,'_',' ');
	end
	% load all the data
	if ~isempty(obj.rawSignals)&ischar(obj.rawSignals{1})
		disp('paths input, going to load files')
		obj.guiEnabled = 0;
		obj = modelVarsFromFiles(obj);
		obj.guiEnabled = 1;
	end
	% check if signal peaks have already been calculated
	if isempty(obj.signalPeaks)&~isempty(obj.rawSignals)
		% obj.computeSignalPeaksFxn();
	else
		disp('No folder data specified, load data with modelVarsFromFiles method.');
		% warning('no signal data input!!!')
	end
	% load stimulus tables

	disp(repmat('*',1,42))
	% Check java heap size
	try
		javaHeapSpaceSizeGb = java.lang.Runtime.getRuntime.maxMemory*1e-9;
		javaHeapPrefGb = 2;
		javaHelpUrl = "https://www.mathworks.com/help/matlab/matlab_external/java-heap-memory-preferences.html";
		javaTmpStr = sprintf('\n\nGo to Preferences->General->"Java Heap Memory" and increase to >%d MB. See more on how to change Java heap memory online at:Java Heap Memory Preferences (%s).',javaHeapPrefGb*1024,javaHelpUrl,javaHelpUrl,javaHelpUrl);
		if javaHeapSpaceSizeGb<javaHeapPrefGb
			javaErrorStr = @(x) sprintf('Java max heap memory is %0.3f GB. This might cause Miji errors when loading videos due to insufficient memory.%s\n\nOtherwise put "java.opts" (located in "ciapkg" sub-folder) file in the MATLAB start-up path or change MATALB start-up folder to the calciumImagingAnalysis root folder then restart MATLB before continuing.\n',javaHeapSpaceSizeGb,x);
			warning(javaErrorStr(javaTmpStr));
			ciapkg.overloaded.msgbox(javaErrorStr(sprintf('\n\nSee command line for URL to change Java heap memory.')),'Note to user','modal')
		else
			fprintf('Java max heap memory is %0.3f Gb, this should be sufficient to run Miji without problems. Please change "java.opts" file to increase heap space if run into Miji memory errors.\n',javaHeapSpaceSizeGb);
		end
	catch err
		disp(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		disp(repmat('@',1,7))
	end
end


function coppelia_wait(coppelia, n)
    for i=1:n
        coppelia.sim.simxSynchronousTrigger(coppelia.clientID);
    end
end


classdef ScalarNodalVariablePrinter < ResultsPrinter

    properties (Access = protected)
        simulationStr
        hasGaussData = false;
    end

    properties (Access = private)
        fieldName
    end

    methods (Access = public)

        function obj = ScalarNodalVariablePrinter(d)
            obj.init(d);
            obj.fieldName = d.fieldName;
            obj.simulationStr = d.fieldName;
        end

        function printResults(obj,iter,fileID)
            f = obj.fields;
            dS = obj.createScalarDataBase(iter,fileID,f,obj.fieldName,'OnNodes');
            ScalarNodalPrinter(dS);
        end

    end

    methods (Access = protected)

        function storeFieldsToPrint(obj,d)
            obj.fields = d.fields;
        end

    end

end


function display(pot)

disp('utility potential object');
disp(struct(pot));


function varargout = CamSeqGUI(varargin)
% CAMSEQGUI M-file for CamSeqGUI.fig
%      CAMSEQGUI, by itself, creates a new CAMSEQGUI or raises the existing
%      singleton*.
%
%      H = CAMSEQGUI returns the handle to a new CAMSEQGUI or the handle to
%      the existing singleton*.
%
%      CAMSEQGUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in CAMSEQGUI.M with the given input arguments.
%
%      CAMSEQGUI('Property','Value',...) creates a new CAMSEQGUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before CamSeqGUI_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to CamSeqGUI_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Copyright 2002-2003 The MathWorks, Inc.

% Edit the above text to modify the response to help CamSeqGUI

% Last Modified by GUIDE v2.5 19-Sep-2004 12:05:37

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @CamSeqGUI_OpeningFcn, ...
                   'gui_OutputFcn',  @CamSeqGUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before CamSeqGUI is made visible.
function CamSeqGUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to CamSeqGUI (see VARARGIN)

% Choose default command line output for CamSeqGUI
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes CamSeqGUI wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = CamSeqGUI_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.figure1,'Visible','off');
period = input(' Period in seconde between this view and the previous? > ');
set(handles.figure1,'Visible','on');
figure(1)
if isempty(period),
    period = 2;
end
handles.cam = CamSeqUpdate(handles.cam,period);
guidata(hObject,handles);

% --- Executes on button press in pushbutton2.
function pushbutton2_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.figure1,'Visible','off');
handles.fps = input(' Number of Frame per second? > ');
set(handles.figure1,'Visible','on');
figure(1)
if isempty(handles.fps),
    handles.fps = 20;
end
handles.seq = CamSeqGen(handles.cam,handles.fps);
guidata(hObject,handles);

% --- Executes on button press in pushbutton3.
function pushbutton3_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton3 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.figure1,'Visible','off');
handles.filename = input(' File name for the avi? > ','s');
set(handles.figure1,'Visible','on');
figure(1)
CamSeqPlay(handles.seq,handles.fps,handles.filename);
guidata(hObject,handles);

% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton4 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
figure(1)
handles.cam = CamSeqUpdate([]);
guidata(hObject,handles);


function jsubs = cfg2jobsubs(item, subs)

% function jsubs = cfg2jobsubs(item, subs)
% Return the subscript into the job tree for a given subscript vector into
% the val part of the cfg tree.
%
% This code is part of a batch job configuration system for MATLAB. See
%      help matlabbatch
% for a general overview.
%_______________________________________________________________________
% Copyright (C) 2007 Freiburg Brain Imaging

% Volkmar Glauche
% $Id: cfg2jobsubs.m 1862 2008-06-30 14:12:49Z volkmar $

rev = '$Rev: 1862 $'; %#ok

% Only de-reference subscripts into .val{x} field
if isequal(subs(1), substruct('.','val')) && strcmp(subs(2).type, '{}')
    % return possible indices into .val{1} field
    citem = subsref(item, subs(1:2));
    if numel(subs) > 2
        jsubs1 = cfg2jobsubs(citem, subs(3:end));
    else
        jsubs1 = [];
    end;
    if numel(item.values)==1 && isa(item.values{1},'cfg_branch') && ~item.forcestruct,
        cjsubs = substruct('.', gettag(citem), '()',subs(2).subs);
    else
        if numel(item.values)==1 && ~item.forcestruct,
            cjsubs = substruct('{}', subs(2).subs);
        else
            cjsubs = substruct('{}', subs(2).subs, '.', gettag(citem));
        end;
    end;
    jsubs = [cjsubs jsubs1];
else
    cfg_message('matlabbatch:cfg2jobsubs:wrongsubs', 'Inappropriate subscript reference in item ''%s''.', item.tag);
    jsubs = struct('type',{},'subs',{});
end;


%% (Internal) Wave definitions for ECG delineators used in the toolbox
%
%   Fields used in the payload variables generated by ECGtask_ECG_delineation
%
%
% See also ECGtask_ECG_delineation
%
% Author: Mariano Llamedo Soria llamedom@electron.frba.utn.edu.ar
% Version: 0.1 beta
% Last update: 09/05/2016
% Birthdate  : 09/05/2016
% Copyright 2008-2016
%

% ecg-kit struct field format definition
cAnnotationOutputFields = { 'Pon' 'P' 'Poff' 'Ptipo' 'QRSon' 'qrs' 'Q' 'R' 'S' 'QRSoff' 'Ton' 'T' 'Tprima' 'Toff' 'Ttipo' };
cAnnotationSLRfields =    { 'Pon' 'P' 'Poff'         'QRSon' 'qrs'             'QRSoff' 'Ton' 'T' 'Tprima' 'Toff' };

% conversion from HL7-aECG to ecg-kit format
cHL7aECG_translation = ...
    { 'MDC_ECG_WAVC_PWAVE',   'P', 'Pon', 'Poff'; ...
      'MDC_ECG_WAVC_QWAVE',   'Q', '', ''; ...
      'MDC_ECG_WAVC_RWAVE',   'R', '', ''; ...
      'MDC_ECG_WAVC_SWAVE',   'S', '', ''; ...
      'MDC_ECG_WAVC_QRSWAVE', 'qrs', 'QRSon', 'QRSoff'; ...
      'MDC_ECG_WAVC_QSWAVE',  'qrs', 'QRSon', 'QRSoff'; ...
      'MDC_ECG_WAVC_TWAVE',   'T', 'Ton', 'Toff' };

% conversion from ecg-kit format to Physionet's ecgcodes.h (wfdb_ECGCODES_H)
cMIT_wave_translation = { ...
                    'Pon'    39; ...
                    'P'      24; ...
                    'Poff'   40; ...
                    'Ptipo'  nan; ...
                    'QRSon'  39; ...
                    'qrs'    1; ...
                    'Q'      nan; ...
                    'R'      nan; ...
                    'S'      nan; ...
                    'QRSoff' 40; ...
                    'Ton'    39; ...
                    'T'      27; ...
                    'Tprima' 27; ...
                    'Toff'   40; ...
                    'Ttipo'  nan; ...
                    };


function f = addtheme(f, theme)

    %-------------------------------------------------------------------------%

    %-validate theme name-%

    themePath = 'plotly/themes';
    S = dir(themePath);

    if isempty(S)
        paths = split(path, ':');

        for p = 1:length(paths)
            if ~isempty(strfind(paths{p}, themePath))
                themePath = paths{p};
                break;
            end
        end

        S = dir(themePath);
    end

    N = {S.name};

    if ~any(strcmp(N,strcat(theme, '.json'))) == 1
        ME = MException('MyComponent:noSuchVariable',...
            [strcat('\n<strong>', theme,...
            '</strong> is not a supported themes.'),...
            ' Please choose one of these theme names:\n\n',...
            strrep(strrep([S.name], '...', ''), '.json', ' | ')]);
        throw(ME)
    end

    %-------------------------------------------------------------------------%

    %-add theme to figure-%

    fname = sprintf('%s/%s.json', themePath, theme);
    % fname = strcat('plotly/themes/', theme, '.json');
    fid = fopen(fname);
    raw = fread(fid,inf);
    str = char(raw');
    fclose(fid);
    theme_template = jsondecode(str);

    f.layout.template = theme_template;

    if isfield(f.layout.template.layout, 'paper_bgcolor')
        disp(strcat('layout.bg_paper:::',...
            f.layout.template.layout.paper_bgcolor))
    end

    if isfield(f.layout.template.layout, 'plot_bgcolor')
        disp(strcat('layout.plot_bgcolor:::',...
            f.layout.template.layout.plot_bgcolor))
    end

    %-------------------------------------------------------------------------%
end


function n=countTestNodes(obj)
% Return the number of test nodes
%
% c=countTestCases(obj)
%
% Inputs:
%   obj             MoxUnitTestSuite instance
%
% Output:
%   c               Number of test nodes in obj
%
% NNO 2015

    n=obj.test_count;


function FEBioPath=getFEBioPath

% function FEBioPath=getFEBioPath
% ------------------------------------------------------------------------
% This function reads a user specified FEBio path name from a config text
% file. The text file may contain multiple paths (e.g. for multiple
% operational systems), however the first valid patch is always used.
%
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2015/10/05 Updated for GIBBON
% 2015/10/05 Enabled searching valid paths in multi-line config. file.
% 2017/06/10 changed empty output to an empty character array.
%------------------------------------------------------------------------

%%

filePath=mfilename('fullpath');
toolboxPath=fileparts(fileparts(filePath));
configPath=fullfile(toolboxPath,'config');

fileName=fullfile(configPath,'FEBioPath.txt');

%Import text file containing paths
try
    [T]=txtfile2cell(fileName);
catch
    T={};
end

%Get first valid path
FEBioPath='';
if ~isempty(T)
    for q=1:1:numel(T)
        pathName=T{q};
        if exist(pathName,'file')==2
            FEBioPath=pathName;
            return
        end
    end
end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% 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.
%
% 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, see <http://www.gnu.org/licenses/>.


function [s] = find_first_path(guesses,quiet)
  % [s] = find_first_path(guesses,quiet)
  si = find(cellfun(@(guess) exist(guess,'file'),guesses),1,'first');
  if isempty(si)
    if nargin>1 && quiet
      s = [];
      return;
    else
      error('Could not find path');
    end
  end
  s = guesses{si};
end


function varargout = inputindexGUI(varargin)

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @inputindexGUI_OpeningFcn, ...
                   'gui_OutputFcn',  @inputindexGUI_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before inputindexGUI is made visible.
function inputindexGUI_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = [];

try
      prompt    = char(varargin{1});
      num_lines = varargin{2};
      def       = varargin{3};
catch
      prompt    = 'Enter value';
      num_lines = 1;
      def       = 1;
end

set(handles.uipanel_prompt,'Title', prompt)
set(handles.popupmenu_value,'String', cellstr(num2str(([1:num_lines]'))))
set(handles.popupmenu_value,'Value', def)

%
% Color GUI
%
handles = painterplab(handles);

%
% Set font size
%
handles = setfonterplab(handles);

% Update handles structure
guidata(hObject, handles);

% help
% helpbutton

% UIWAIT makes inputindexGUI wait for user response (see UIRESUME)
uiwait(handles.gui_chassis);

% -----------------------------------------------------------------------
function varargout = inputindexGUI_OutputFcn(hObject, eventdata, handles)

varargout{1} = handles.output;
% The figure can be deleted now
delete(handles.gui_chassis);
pause(0.1)

% -----------------------------------------------------------------------
function pushbutton_CANCEL_Callback(hObject, eventdata, handles)
handles.output = [];
% Update handles structure
guidata(hObject, handles);
uiresume(handles.gui_chassis);

% -----------------------------------------------------------------------
function pushbutton_OK_Callback(hObject, eventdata, handles)

value = get(handles.popupmenu_value,'Value'); % JLC, 30 Aug 2012
handles.output = {value};

% Update handles structure
guidata(hObject, handles);
uiresume(handles.gui_chassis);
% -----------------------------------------------------------------------
function popupmenu_value_Callback(hObject, eventdata, handles)

% -----------------------------------------------------------------------
function popupmenu_value_CreateFcn(hObject, eventdata, handles)

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% -----------------------------------------------------------------------
function uipanel_prompt_CreateFcn(hObject, eventdata, handles)

% -----------------------------------------------------------------------
function gui_chassis_CloseRequestFcn(hObject, eventdata, handles)

if isequal(get(handles.gui_chassis, 'waitstatus'), 'waiting')
        %The GUI is still in UIWAIT, us UIRESUME
        handles.output = '';
        %Update handles structure
        guidata(hObject, handles);
        uiresume(handles.gui_chassis);
else
        % The GUI is no longer waiting, just close it
        delete(handles.gui_chassis);
end


function dtiMrMeshScreenSave(handles,fname)
%
%  dtiMrMeshScreenSave(handles,fname)
%
%Author: Wandell, Dougherty
%Purpose:
%   Save whatever is in the dti mrMesh window to a file
%

rgb = mrmGet(handles.mrMesh,'screenshot')/255;
imwrite(rgb, fname);
disp(['Screenshot saved to ' fname '.']);

return;


function rates
    % NO DESCRIPTION
    % 1: without declustering, 2: with declustering
    report_this_filefun()

    cd ~/zmap/src/thomas/seismicrates

    N=1
    for i=1:N
        params=sr_startZ(1);
    end
end


function out=subsasgn(x,varargin)

if isempty(x)
 x=mp(x);
end
out=builtin('subsasgn',x,varargin{:});

% This has to be fixed to allow the incoming doubles (or other mp's)
% to be promoted to the right precision
%'ssssssss',kb


classdef MimCombinedImageDatabase < CoreBaseClass
    % MimCombinedImageDatabase. Part of the internal framework of the TD MIM Toolkit.
    %
    %     You should not use this class within your own code. It is intended to
    %     be used internally within the framework of the TD MIM Toolkit.
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (Access = private)
        ImageDatabase
        MatNatDatabase
    end

    events
        DatabaseHasChanged
    end

    methods
        function obj = MimCombinedImageDatabase(image_database, matnat_database)
            obj.ImageDatabase = image_database;
            obj.MatNatDatabase = matnat_database;

            obj.AddEventListener(image_database, 'DatabaseHasChanged', @obj.ImageDatabaseChanged);
        end

        function [project_names, project_ids] = GetListOfProjects(obj)
            [project_names_1, project_ids_1] = obj.ImageDatabase.GetListOfProjects;
            [project_names_2, project_ids_2] = obj.MatNatDatabase.GetListOfProjects;
            project_names = [project_names_1, project_names_2];
            project_ids = [project_ids_1, project_ids_2];
        end

        function datasets = GetAllSeriesForThisPatient(obj, project_id, patient_id, group_patients)
            datasets_1 = obj.ImageDatabase.GetAllSeriesForThisPatient(project_id, patient_id, group_patients);
            datasets_2 = obj.MatNatDatabase.GetAllSeriesForThisPatient(project_id, patient_id);
            datasets = horzcat(datasets_1, datasets_2);
        end

        function [names, ids, short_visible_names, patient_id_map] = GetListOfPatientNames(obj, project_id, group_patients)

            [names1, ids1, short_visible_names1, patient_id_map1] = obj.ImageDatabase.GetListOfPatientNames(project_id, group_patients);
            [names2, ids2, short_visible_names2, patient_id_map2] = obj.MatNatDatabase.GetListOfPatientNames(project_id);

            names = horzcat(names1, names2);
            ids = horzcat(ids1, ids2);
            short_visible_names = horzcat(short_visible_names1, short_visible_names2);

            patient_id_map = patient_id_map1;

            for key = patient_id_map2.keys
                patient_id_map(key{1}) = patient_id_map2(key{1});
            end
        end

        function [names, ids, short_visible_names, num_series, num_patients_combined, patient_id_map] = GetListOfPatientNamesAndSeriesCount(obj, project_id, group_patients)

            [names1, ids1, short_visible_names1, num_series_1, num_patients_combined_1, patient_id_map1] = obj.ImageDatabase.GetListOfPatientNamesAndSeriesCount(project_id, group_patients);
            [names2, ids2, short_visible_names2, num_series_2, num_patients_combined_2, patient_id_map2] = obj.MatNatDatabase.GetListOfPatientNamesAndSeriesCount(project_id);

            names = horzcat(names1, names2);
            ids = horzcat(ids1, ids2);
            short_visible_names = horzcat(short_visible_names1, short_visible_names2);
            num_series = num_series_1 + num_series_2;
            num_patients_combined = num_patients_combined_1 + num_patients_combined_2;

            patient_id_map = patient_id_map1;

            for key = patient_id_map2.keys
                patient_id_map(key) = patient_id_map2(key);
            end
        end
    end

    methods (Access = private)
        function ImageDatabaseChanged(obj, ~, ~)
            notify(obj, 'DatabaseHasChanged');
        end
    end
end


% MACRO_METHODCALL: Script to insert at the beginning of all the brainstorm class functions
%
% DEPRECATED:  Use "eval(macro_method);" instead.

% @=============================================================================
% 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, The Mathworks, 2010-2016

% Get current stack trace
dbStack = dbstack();
% Get the call just one level above
if (length(dbStack) > 1)
    strScript = sprintf('%s.m at line %d', dbStack(2).name, dbStack(2).line);
else
    strScript = 'Unknown';
end

% Display warning when this function is called
disp([10 '*******************************************************************************' ...
      10 '*** WARNING: In function: ' strScript ...
      10 '***          The process API changed, due to modifications in Matlab 2016b.' ...
      10 '***          Replace "macro_methodcall;" with "eval(macro_method);"' ...
      10 '***          at the beginning of all your Brainstorm functions.' ...
      10 '*******************************************************************************']);

% Matlab versions: 2006b-2016a
if bst_verlessthan(901)
    % No parameters: nothing to do
    if (nargin == 0)
    % Else : execute appropriate local function
    elseif ischar(varargin{1})
        if (nargout)
            [varargout{1:nargout}] = feval(str2func(varargin{1}), varargin{2:end});
        else
            feval(str2func(varargin{1}), varargin{2:end});
        end
    end

% Matlab versions: >= 2016b
% FiS Incompatibility fix (developed by The Mathworks, 2016)
else
    % Catch stupid warnings after 2016b (macro_methodcall.m not working anymore)
    warning('off', 'MATLAB:lang:GetCallerInfoWarning');
    % Builtin to capture the caller info, the structure contains nargin, nargout and local function handles in the caller.
    callerinfo = builtin('_GetCallerInfo');
    fcnNames = cellfun(@func2str,callerinfo.localFunctions,'UniformOutput',false);

    if(length(varargin) >= 1)
        ind1 = strcmp(fcnNames, varargin{1});
    end
    % FiS Incompatibility fix
    if (callerinfo.nargin == 0)
    % Else : execute appropriate local function
    elseif ischar(varargin{1})
        % FiS Incompatibility fix
        if (callerinfo.nargout)
            % FiS Incompatibility fix
            [varargout{1:callerinfo.nargout}] = feval(callerinfo.localFunctions{ind1}, varargin{2:end});
        else
            % FiS Incompatibility fix
            feval(callerinfo.localFunctions{ind1}, varargin{2:end});
        end
    end
end


function ui = mrViewViewMenu(ui);
%
% ui = mrViewViewMenu(ui);
%
% Attaches a view menu to a mrViewer UI.
%
% ras 07/05.
if ~exist('ui', 'var') | isempty(ui),  ui = get(gcf, 'UserData'); end

ui.menus.view = uimenu(ui.fig, 'Label', 'View');

chkVals = {'off' 'on'}; % will be used to set some menus,  below

% navigation panel toggle callback:
cb = 'ui = mrViewGet; ';
cb = [cb 'mrvPanelToggle(ui.panels.nav, gcbo); clear ui; '];
ui.menus.navToggle = uimenu(ui.menus.view, 'Label', 'Navigation Panel', ...
                            'Checked', 'on', 'Separator', 'off', ...
                            'Accelerator', '1', 'Callback', cb);

% ROI panel toggle callback:
cb = 'ui = mrViewGet; ';
cb = [cb 'mrvPanelToggle(ui.panels.roi, gcbo); clear ui; '];
ui.menus.infoToggle = uimenu(ui.menus.view, 'Label', 'ROI Panel', ...
                            'Checked', 'off', 'Separator', 'off', ...
                            'Accelerator', '2', 'Callback', cb);

% grayscale panel toggle callback:
cb = 'ui = mrViewGet; ';
cb = [cb 'mrvPanelToggle(ui.panels.grayscale, gcbo); clear ui; '];
ui.menus.grayscaleToggle = uimenu(ui.menus.view, 'Label', 'Grayscale Panel', ...
                            'Checked', 'off', 'Separator', 'off', ...
                            'Accelerator', '3', 'Callback', cb);

% info panel toggle callback:
cb = 'ui = mrViewGet; ';
cb = [cb 'mrvPanelToggle(ui.panels.info, gcbo); clear ui; '];
ui.menus.infoToggle = uimenu(ui.menus.view, 'Label', 'Info Panel', ...
                            'Checked', 'off', 'Separator', 'off', ...
                            'Accelerator', '4', 'Callback', cb);

% mesh panel toggle callback:
cb = 'ui = mrViewGet; ';
cb = [cb 'mrvPanelToggle(ui.panels.mesh, gcbo); clear ui; '];
ui.menus.infoToggle = uimenu(ui.menus.view, 'Label', 'Mesh Panel', ...
                            'Checked', 'off', 'Separator', 'off', ...
                            'Accelerator', '5', 'Callback', cb);

%%%%% Dock submenu
ui.menus.dock = uimenu(ui.menus.view, 'Label', 'Dock Panel to Display', ...
                       'Separator', 'on');
% for these submenus, we need to know which panels are currently
% attached to the main figure, and which are not. This subroutine does
% that...
chk = findDockedPanels(ui);

% dock Navigation Panel
cb = 'mrViewDock(gcf, ''nav'', gcbo); ';
ui.menus.navDock = uimenu(ui.menus.dock, 'Label', 'Navigation Panel', ...
                            'Checked', chk{1}, 'Callback', cb);

% dock ROI Panel
cb = 'mrViewDock(gcf, ''roi'', gcbo); ';
ui.menus.roiDock = uimenu(ui.menus.dock, 'Label', 'ROI Panel', ...
                            'Checked', chk{2}, 'Callback', cb);

% dock Grayscale Panel
cb = 'mrViewDock(gcf, ''grayscale'', gcbo); ';
ui.menus.navDock = uimenu(ui.menus.dock, 'Label', 'Grayscale Panel', ...
                            'Checked', chk{3}, 'Callback', cb);

% dock Info Panel
cb = 'mrViewDock(gcf, ''info'', gcbo); ';
ui.menus.infoDock = uimenu(ui.menus.dock, 'Label', 'Info Panel', ...
                            'Checked', chk{4}, 'Callback', cb);

% dock Mesh Panel
cb = 'mrViewDock(gcf, ''mesh'', gcbo); ';
ui.menus.meshDock = uimenu(ui.menus.dock, 'Label', 'Mesh Panel', ...
                            'Checked', chk{5}, 'Callback', cb);

% dock Overlay Panels
cb = 'mrViewDock(gcf, ''overlay'', gcbo); ';
ui.menus.overlayDock = uimenu(ui.menus.dock, 'Label', 'Overlay Panels', ...
                            'Checked', 'on', 'Callback', cb);

% dock Colorbar Panel
cb = 'mrViewDock(gcf, ''colorbar'', gcbo); ';
ui.menus.colorbarDock = uimenu(ui.menus.dock, 'Label', 'Colorbar Panel', ...
                            'Checked', chk{6}, 'Callback', cb);

%%%%% overlays submenu
ui.menus.overlays = uimenu(ui.menus.view, 'Label', 'Overlays', ...
                           'Separator', 'on');

% new overlay
uimenu(ui.menus.overlays, 'Label', 'New Overlay', 'Separator', 'on', ...
       'Accelerator', 'O', 'Callback', 'mrViewAddOverlay;');

% close overlay
uimenu(ui.menus.overlays, 'Label', 'Close Overlay', 'Separator', 'on', ...
       'Accelerator', '9', 'Callback', 'mrViewRemoveOverlay;');

% text for showing current overlays:
uimenu(ui.menus.overlays, 'Label', 'Show Overlay Panels:', ...
                         'ForegroundColor', 'w', 'Separator', 'on');

%%%%% label submenu
ui.menus.label = uimenu(ui.menus.view, 'Label', 'Label', 'Separator', 'on');

% label directions
cb = 'val=umtoggle(gcbo); mrViewSet([], ''labelDirs'', val); mrViewRefresh;';
chk = chkVals{ui.settings.labelDirs+1};
uimenu(ui.menus.label, 'Label', 'Label Directions', ...
       'Checked', chk, 'Separator', 'on', 'Callback', cb);

% label units menu:
cb = 'val=umtoggle(gcbo); mrViewSet([], ''labelAxes'', val); mrViewRefresh;';
chk = chkVals{ui.settings.labelAxes+1};
uimenu(ui.menus.label, 'Label', 'Label Units', ...
       'Checked', chk, 'Separator', 'on', 'Callback', cb);

% preserve aspect menu:
cb = 'val=umtoggle(gcbo); mrViewSet([], ''eqAspect'', val); mrViewRefresh;';
ui.menus.preserveAspect = uimenu(ui.menus.view, 'Label', 'Preserve Aspect', ...
                               'Checked', 'on', 'Separator', 'on', ...
                               'Callback', cb);

% All slices option
cb = ['nslices = mrViewGet(gcf, ''NumSlices''); ' ...
      'nrows = ceil(sqrt(nslices)); ncols = ceil(nslices/nrows); ' ...
      'mrViewSet(gcf, ''MontageRows'', nrows, ''MontageCols'', ncols); ' ...
      'mrViewSet(gcf, ''Slice'', 1, ''DisplayFormat'', 1); ' ...
	  'mrViewRefresh(gcf); ' ...
      'clear nrows ncols nslices; '];
uimenu(ui.menus.view, 'Label', 'All Slices', ...
       'Separator', 'off', 'Callback', cb);

% center of current ROI option
cb = 'mrViewRecenter(get(gcf, ''UserData''), ''roi''); ';
uimenu(ui.menus.view, 'Label', 'Center of Current ROI', ...
       'Separator', 'off', 'Callback', cb);

% toggle for standard MATLAB figure menus
addFigMenuToggle(ui.menus.view);

% refresh options
uimenu(ui.menus.view,  'Label', 'Clean Up Display Panels',  ...
        'Separator',  'on',  'Callback', 'mrViewFixDisplay(gcf);');

uimenu(ui.menus.view,  'Label', 'Refresh',  'Separator',  'off',  ...
       'Accelerator', 'Y',  'Callback', 'mrViewRefresh;');

return
% /--------------------------------------------------------------/ %

% /--------------------------------------------------------------/ %
function chk = findDockedPanels(ui);
% Checkes whether each panel in a mrViewer UI is attached ("docked")
% to the main figure. Returns a cell array of 'on' for attached
% and 'off' for not attached (these are useful for setting the
% 'Checked' property of the dock submenus).
panels = [ui.panels.nav ui.panels.roi ui.panels.grayscale ...
		  ui.panels.info ui.panels.mesh ui.panels.colorbar];
for i = 1:6
	if get(panels(i), 'Parent')==ui.fig
		chk{i} = 'on';
	else
		chck{i} = 'off';
	end
end

return