function varargout = sv_result(varargin)

if (nargin == 1) & ~ischar(varargin{1})  % LAUNCH GUI

  fig = openfig(mfilename,'reuse');

  % Generate a structure of handles to pass to callbacks, and store it.
  handles = guihandles(fig);
  % Create the figure-window
  handles.hPlotFigure = figure_w_normalized_uicontrolunits('Name', 'Result plot', 'NumberTitle', 'off');
  handles.hMainAxes = newplot;
  % Store the datasets internally
  handles.vResult = varargin{1}(1);
  handles.vResults = varargin{1};
  % Set up the listbox with descriptions of the stored datasets
  sList = [];
  for nCnt = 1:length(handles.vResults)
    result = handles.vResults(nCnt);
    try
      sList = [sList; cellstr(result.sComment)];
    catch
      sList = [sList; cellstr(num2str(nCnt))];
    end
  end
  set(handles.lstSets, 'String', sList);
  set(handles.lstSets, 'Value', 1);

  guidata(fig, handles);
  % Plot the very first dataset and map
  plotit(handles)

  try
    if length(varargin{1}) > 1
      set(handles.lstMaps, 'String', varargin{1}(1).vcsGridNames);
    else
      set(handles.lstMaps, 'String', varargin{1}.vcsGridNames);
    end
  catch
  end

  if nargout > 0
    varargout{1} = fig;
  end

elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK

  try
    [varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
  catch
    disp(lasterr);
  end

end

% --------------------------------------------------------------------
function varargout = btnClose_Callback(h, eventdata, handles, varargin)

% Closes plot-figure and this figure
try
  delete(handles.hPlotFigure);
catch
end
delete(handles.dlgResult);

% --------------------------------------------------------------------
function plotit(handles)

try
  % Get selected dataset
  handles.vResult = handles.vResults(get(handles.lstSets, 'Value'));

  % Create new values for plotting
  nIdx = get(handles.lstMaps, 'Value');
  vTmp = ones(length(handles.vResult.vX) * length(handles.vResult.vY), 1) * nan;
  vTmp(handles.vResult.vUsedNodes) = handles.vResult.mValueGrid(:,nIdx);
  handles.mPlotValues = reshape(vTmp, length(handles.vResult.vY), length(handles.vResult.vX));

  % Renew plot
  figure_w_normalized_uicontrolunits(handles.hPlotFigure);
  delete(handles.hMainAxes);
  handles.hMainAxes = newplot;
  set(handles.hPlotFigure, 'renderer', 'zbuffer');

  % Plot data
  handles.hSurface = surface(handles.vResult.vX, handles.vResult.vY, zeros(size(handles.mPlotValues)), handles.mPlotValues, 'Parent', handles.hMainAxes);
  axis(handles.hMainAxes, 'image');
  if get(handles.chkInterpolated, 'Value') == 1
    shading(handles.hMainAxes, 'interp');
  else
      set(handles.hSurface,'Edgecolor','none');
  end
  set(handles.hMainAxes, 'box', 'on');
  hold on;

  % Plotting coastline & faults
  if handles.vResult.bMap
    if get(handles.chkCoastlines, 'Value') == 1
      try
        handles.hCoastline = plot(handles.vResult.vCoastline(:,1), handles.vResult.vCoastline(:,2),'LineWidth',1.0,'Color',[0  0  0 ]);
      catch
      end
    end
    if get(handles.chkFaults, 'Value') == 1
      try
        handles.hFaults = plot(handles.vResult.vFaults(:,1), handles.vResult.vFaults(:,2),'k','LineWidth',0.2);
      catch
      end
    end
  end

  if get(handles.chkSeismicity, 'Value') == 1
    if ~isempty(handles.vResult.mCatalog)
      if handles.vResult.bMap
        handles.hEQPlot = plot(handles.vResult.mCatalog(:,1),handles.vResult.mCatalog(:,2),'.k','MarkerSize',6,'Marker','.','Color','k','Visible','on');
      else
        handles.hEQPlot = plot(handles.vResult.mCatalog(:,length(handles.vResult.mCatalog(1,:))),-handles.vResult.mCatalog(:,7),'.k','MarkerSize',6,'Marker','.','Color','k','Visible','on');
      end
    end
  end
  % Set information values
  set(handles.lblMin, 'String', num2str(min(min(handles.mPlotValues)), 3));
  set(handles.lblMax, 'String', num2str(max(max(handles.mPlotValues)), 3));
  set(handles.lblMean, 'String', num2str(mean(handles.vResult.mValueGrid(:,nIdx),'omitnan'), 3));
  if get(handles.chkKeepColormap, 'Value') == 0
    set(handles.txtMin, 'String', num2str(min(min(handles.mPlotValues)), 3));
    set(handles.txtMax, 'String', num2str(max(max(handles.mPlotValues)), 3));
  end

  % Set limits
  fLimMin = str2double(get(handles.txtMin, 'String'));
  if (isnan(fLimMin) | isinf(fLimMin))
    fLimMin = min(min(handles.mPlotValues));
    if isnan(fLimMin)
      fLimMin = 0;
    elseif isinf(fLimMin) % Set to the lowest non-inf value
      mSelection = ~isinf(handles.mPlotValues);
      mNoInfValues = handles.mPlotValues(mSelection);
      fLimMin = min(min(mNoInfValues));
    end
    set(handles.txtMin, 'String', num2str(fLimMin, 3));
  end
  fLimMax = str2double(get(handles.txtMax, 'String'));
  if (isnan(fLimMax) | isinf(fLimMax))
    fLimMax = max(max(handles.mPlotValues));
    if isnan(fLimMax)
      fLimMax = 1;
    elseif isinf(fLimMax) % Set to the highest non-inf value
      mSelection = ~isinf(handles.mPlotValues);
      mNoInfValues = handles.mPlotValues(mSelection);
      fLimMax = max(max(mNoInfValues));
    end
    set(handles.txtMax, 'String', num2str(fLimMax, 3));
  end
  % If both of the values have the same value, colorbar is causing trouble
  if fLimMax == fLimMin
    fLimMax = fLimMax + 0.001;
  end
  set(handles.hMainAxes, 'CLim', [fLimMin fLimMax]);

  % Create a new colorbar
  nColormap = get(handles.lstColormap, 'Value');
  switch nColormap
  case  1, vColormap = autumn(256);
  case  2, vColormap = bone(256);
  case  3, vColormap = colorcube(256);
  case  4, vColormap = cool(256);
  case  5, vColormap = copper(256);
  case  6, vColormap = flag(256);
  case  7, vColormap = gray(256);
  case  8, vColormap = hot(256);
  case  9, vColormap = hsv(256);
  case 10, vColormap = jet(256);
  case 11, vColormap = lines(256);
  case 12, vColormap = pink(256);
  case 13, vColormap = prism(256);
  case 14, vColormap = spring(256);
  case 15, vColormap = summer(256);
  case 16, vColormap = white(256);
  case 17, vColormap = winter(256);
  case 18, vColormap = gui_Colormap_HotCut(256);
  case 19, vColormap = gui_Colormap_HSVCut(256);
  case 20, vColormap = gui_Colormap_RedGrayGreen(256);
  case 21, vColormap = gui_Colormap_RedGrayBlue(256);
  case 22, vColormap = gui_Colormap_BlueYellowRed(256);
  case 23, vColormap = gui_Colormap_Rastafari(256);
  case 24, vColormap = gui_Colormap_4Colors(256);
  case 25, vColormap = gui_Colormap_Skyline(256);
  case 26, vColormap = gui_Colormap_6Colors(256);
  otherwise, vColormap = jet(256);
  end
  % Flip colormap
  if get(handles.chkFlipColormap, 'Value') == 1
    vColormap = flipud(vColormap);
  end
  if get(handles.chkColorscale, 'Value') == 0
    nLimit = str2double(get(handles.txtScaleLimit, 'String'));
    if (isnan(nLimit)) | (nLimit ~= round(nLimit))
      nLimit = 20;
      set(handles.txtScaleLimit, 'String', num2str(nLimit));
    end
    vColormap = ex_dcolor(vColormap, nLimit);
  end
  colormap(handles.hMainAxes, vColormap);
  handles.hColorBar = colorbar('horiz', 'peer', handles.hMainAxes);

  % Save changes
  guidata(handles.dlgResult, handles);
catch
end

% --------------------------------------------------------------------
function varargout = mnuFileLoad_Callback(h, eventdata, handles, varargin)

% Get the filename of new result file
[bOK, sFilePath] = ex_GetFile('*.mat', 'Select a result file');
if bOK
  % Load the new result records
  vTmpResults = load(sFilePath, 'vResults');
  % Add the new result records
  for nCnt = 1:length(vTmpResults.vResults)
    result=vTmpResults.vResults(nCnt);
    handles.vResults = [handles.vResults; result];
  end
  % Create a new list of result records
  sList = [];
  for nCnt = 1:length(handles.vResults)
    result = handles.vResults(nCnt);
    try
      sList = [sList; cellstr(result.sComment)];
    catch
      sList = [sList; cellstr(num2str(nCnt))];
    end
  end
  set(handles.lstSets, 'String', sList);
  set(handles.lstSets, 'Value', 1);
  % Store the new result records
  guidata(handles.dlgResult, handles);
  % Plot the very first dataset and map
  plotit(handles)
  try
    set(handles.lstMaps, 'String', result.vcsGridNames);
  catch
  end
end

% --------------------------------------------------------------------
function varargout = mnuFileSave_Callback(h, eventdata, handles, varargin)

% Save the data
[newfile, newpath] = uiputfile('result.mat', 'Save resutls');
newfile = [newpath newfile];
if length(newfile) > 1
  vResults = handles.vResults;
  save(newfile);
end

% --------------------------------------------------------------------
function varargout = mnuFMD_Callback(h, eventdata, handles, varargin)

aux_McEMR(handles.vResult, handles.dlgResult);

% --------------------------------------------------------------------
function varargout = update_Callback(h, eventdata, handles, varargin)

plotit(handles);

% Buttons
% --------------------------------------------------------------------
function varargout = btnAnalyze_Callback(h, eventdata, handles, varargin)

sAuxiliaryFunction = get(handles.txtAuxiliaryFunction, 'String');
if ~isempty(sAuxiliaryFunction)
  eval([sAuxiliaryFunction '(handles.vResult, handles.dlgResult)']);
end

% --------------------------------------------------------------------
function varargout = btnPlotCDF_Callback(h, eventdata, handles, varargin)

try
  nIdx = get(handles.lstMaps, 'Value');
  vDistribution = handles.vResult.mValueGrid(:,nIdx);
  plot_CDF(vDistribution);
  assignin('base', 'vDistribution', vDistribution);
catch
end

% --------------------------------------------------------------------
function varargout = btnPlotPDF_Callback(h, eventdata, handles, varargin)

try
  nIdx = get(handles.lstMaps, 'Value');
  vDistribution = handles.vResult.mValueGrid(:,nIdx);
  vSel = ~isnan(vDistribution);
  vDistribution = vDistribution(vSel,:);
  nNumberBins = str2double(get(handles.txtNumberBins, 'String'));
  figure;
  histogram(vDistribution, nNumberBins);
catch
end

%% Getting handles
% --------------------------------------------------------------------
function varargout = GetAxesHandle(h, eventdata, handles, varargin)

varargout{1} = handles.hMainAxes;

% --------------------------------------------------------------------
function varargout = GetColorbarHandle(h, eventdata, handles, varargin)

varargout{1} = handles.hColorBar;

% --------------------------------------------------------------------
function varargout = GetFigureHandle(h, eventdata, handles, varargin)

varargout{1} = handles.hPlotFigure;

% --------------------------------------------------------------------
function varargout = GetSurfaceHandle(h, eventdata, handles, varargin)

varargout{1} = handles.hSurface;

% --------------------------------------------------------------------
function varargout = GetEQPlotHandle(h, eventdata, handles, varargin)

varargout{1} = handles.hEQPlot;


%   AUTHORSHIP
%   Primary Developer: Stephen Meehan <swmeehan@stanford.edu>
%   Math Lead & Secondary Developer:  Connor Meehan <connor.gw.meehan@gmail.com>
%   Bioinformatics Lead:  Wayne Moore <wmoore@stanford.edu>
%   Provided by the Herzenberg Lab at Stanford University
%   License: BSD 3 clause
%
classdef SortGui < handle
    properties(Constant)
        PROP_ORDER='sortGui.sortOrder';
        PROP_ASCENDING='sortGui.sortAscending';
        PROP_SEARCH='sortGui.search';
        PROP_RESTRICT='sortGui.restrict';
    end

    properties
        fncRefresh;
        dlg;
    end

    properties(SetAccess=private)
        givenTip=false;
        path;
        strings;
        allChb;
        allMsg;
        sortIdxs;
        visibleIdxs;
        sortTypes;
        sortKeys;
        btnUp;
        btnDown;
        btnSortDir;
        btnSearch;
        options;
        sortAscending=true;
        nOptions;
        checkBoxes
        innerPnl;
        cb;
        map;
        propPrefix;
        allChbPnl;
        searchPnl;
        searchJtf;
        searching=false;
        found;
        foundLabel;
        foundIdx;
        btnRestrict;
        originalSize;
        originalHeight;
        firstCb;
        sortLabel;
    end

    methods
        function this=SortGui(dlg, allChb, allMsg, allChbPnl, options, ...
                checkBoxes, innerPnl)
            pnl=Gui.Panel;
            this.dlg=dlg;
            this.allChb=allChb;
            this.allMsg=allMsg;
            this.options=options;
            this.nOptions=length(options);
            this.sortIdxs=1:this.nOptions;
            this.checkBoxes=checkBoxes;
            this.innerPnl=innerPnl;
            [this.sortKeys, this.sortTypes]=Html.DecodeSort(options{1});
            this.sortLabel=Gui.Label('Sort:');
            if ~isempty(this.sortKeys)
                pnl.add(this.sortLabel);
                fl=javaObjectEDT('java.awt.FlowLayout', 0,0,0);
                pnl.setLayout(fl);
                labels=['sort by' this.sortKeys];
                cb=Gui.Combo(labels, 1,[],[],@(h,e)sortItems(this, h), ...
                    'Change order of list');
                pnl.add(cb);
                this.cb=cb;
                allChbPnl.add(Gui.Label('  '), 'Center');
            end
            this.path=BasicMap.Path;
            this.btnSortDir=Gui.ImageButton(...
                fullfile(this.path, 'sortDown.png'), ...
                'List order is ascending, click for DESCENDING?', ...
                @(h,e)toggleAscending(this));
            pnl.add(this.btnSortDir);
            this.btnSearch=Gui.ImageButton(...
                fullfile(this.path, 'find16.gif'), ...
                'Search items in this list', ...
                @(h,e)toggleSearch(this));
            pnl.add(this.btnSearch);
            this.initSearch;
            allChbPnl.add(pnl, 'East');
            this.allChbPnl=allChbPnl;
            this.seeSortCues;
        end

        function setSortDir(this)
            if this.sortAscending
                this.btnSortDir.setIcon(Gui.Icon('sortDown.png'));
                this.btnSortDir.setToolTipText(...
                    'Order is ascending, click for DESCENDING.');
            else
                this.btnSortDir.setIcon(Gui.Icon('sortUp.png'));
                this.btnSortDir.setToolTipText(...
                    'Order is descending, click for ASCENDING.');
            end
        end

        function toggleAscending(this)
            this.sortIdxs=flip(this.sortIdxs);
            this.refreshSearchAndSort(true);
            this.seeSortCues;
        end

        function seeSortCues(this)
            sorting=~isempty(this.cb) && this.cb.getSelectedIndex>0;
            this.btnSortDir.setVisible(sorting);
            this.sortLabel.setVisible(sorting);
        end

        function refreshSearchAndSort(this, sorting)
            if ~isempty(this.fncRefresh)
                if this.btnRestrict.isSelected
                    pnl=feval(this.fncRefresh);
                else
                    was=this.visibleIdxs;
                    this.visibleIdxs=[];
                    pnl=feval(this.fncRefresh);
                    this.visibleIdxs=was;
                end
                if ~isempty(this.dlg)
                    if isempty(this.originalSize)
                        sz=this.dlg.getSize;
                        if sz.height>0 && sz.width>0
                            this.originalSize=sz;
                            this.originalHeight=this.originalSize.height*.8;
                        end
                    end
                    this.dlg.pack;
                    if ~isempty(this.originalSize)
                        sz=this.dlg.getSize;
                        if ~sz.equals(this.originalSize)
                            this.dlg.setSize(this.originalSize);
                        end
                    end
                end
            else
                this.innerPnl.removeAll;
                if isempty(this.visibleIdxs) || ~this.btnRestrict.isSelected
                    for jj=1:this.nOptions
                        idx=this.sortIdxs(jj);
                        this.innerPnl.add(this.checkBoxes.get(idx-1));
                    end
                else
                    for jj=1:this.nOptions
                        idx=this.sortIdxs(jj);
                        if isempty(find(this.visibleIdxs==idx,1))
                            continue;
                        end
                        this.innerPnl.add(this.checkBoxes.get(idx-1));
                    end
                end
                if nargin>1 && sorting && ~isempty(this.dlg)
                    this.dlg.pack;
                else
                    this.innerPnl.repaint;
                end
            end
            this.sortAscending=~this.sortAscending;
            this.setAscending;
            this.setSortDir;
            this.handleFound;
            this.seeFirstCb;
            drawnow;
        end

        function setAscending(this)
            if ~isempty(this.map)
                if this.sortAscending
                    this.map.set(this.propAscending, 'true');
                else
                    this.map.set(this.propAscending, 'false');
                end
            end
        end

        function prop=propOrder(this)
            prop=[this.propPrefix SortGui.PROP_ORDER];
        end

        function prop=propAscending(this)
            prop=[this.propPrefix SortGui.PROP_ASCENDING];
        end

        function prop=propSearch(this)
            prop=[this.propPrefix SortGui.PROP_SEARCH];
        end

        function prop=propRestrict(this)
            prop=[this.propPrefix SortGui.PROP_RESTRICT];
        end

        function setProperties(this, map, propPrefix, searchDflt)
            if nargin<4
                searchDflt=false;
            end
            this.map=map;
            this.propPrefix=propPrefix;
            if map.has(this.propOrder)
                idx=map.getNumeric(this.propOrder, 0);
                if isnumeric(idx) && idx>0
                    this.cb.setSelectedIndex(idx-1);
                    %this.sort(idx);
                end
            end
            if ~map.is(this.propAscending, true)
                this.toggleAscending;
                this.setSortDir;
            end
            if map.is(this.propSearch, searchDflt)
                MatBasics.RunLater(@(h,e)toggleSearch(this), .12);
            end
            if map.is(this.propRestrict, true)
                this.btnRestrict.setSelected(true);
                this.restrict;
            end
        end

        function sortItems(this, h)
            idx=h.getSelectedIndex+1;
            if ~isempty(this.map)
                this.map.set(this.propOrder, idx);
            end
            this.sort(idx);
        end

        function sort(this, idx)
            if idx<=1
                this.sortIdxs=1:this.nOptions;
            else
                isNum=isequal('N', this.sortTypes{idx-1});
                if isNum
                    rm=realmin;
                    values=zeros(1, this.nOptions);
                else
                    values=cell(1, this.nOptions);
                end
                key=this.sortKeys{idx-1};
                for jj=1:this.nOptions
                    v=Html.DecodeSortValues(this.options{jj}, key);
                    if isNum
                        if isempty(v)
                            values(jj)=rm;
                        else
                            values(jj)=str2double(v{1});
                        end
                    else
                        if isempty(v)
                            values{jj}='';
                        else
                            values{jj}=v{1};
                        end
                    end
                end
                [~, this.sortIdxs]=sort(values);
            end
            if this.sortAscending
                this.sortIdxs=flip(this.sortIdxs);
            end
            this.sortAscending=~this.sortAscending;
            this.toggleAscending;
            this.setAscending;
        end

        function handleFound(this)
            if ~isempty(this.foundLabel)
                if isempty(this.found)
                    this.btnDown.setVisible(false);
                    this.btnUp.setVisible(false);
                    if this.searchJtf.getText.length==0
                        this.foundLabel.setText('');
                    else
                        this.foundLabel.setText(...
                            ['0/' ...
                            num2str(this.nOptions) '']);
                    end
                else
                    N=length(this.found);
                    if ~this.btnRestrict.isSelected
                        s=[num2str(this.foundIdx) '/' num2str(N)];
                        this.btnDown.setVisible(true);
                        this.btnUp.setVisible(true);
                    else
                        s=[num2str(N) '/' num2str(this.nOptions)];
                        this.btnDown.setVisible(false);
                        this.btnUp.setVisible(false);
                    end
                    this.foundLabel.setText(s);
                    if ispc
                        this.dlg.pack;
                    end
                end
            end
        end

        function initSearch(this)
            jtf=javaObjectEDT('javax.swing.JTextField');
            jtf.setColumns(15)
            %jtf.setHorizontalAlignment(jtf.RIGHT);
            jj=handle(jtf, 'CallbackProperties');
            set(jj, 'KeyTypedCallback', @(h,e)search(this, h));
            this.searchPnl=Gui.BorderPanel;
            pnl=Gui.Panel;
            fl=javaObjectEDT('java.awt.FlowLayout', 0,0,0);
            pnl.setLayout(fl);
            emptyBorder=javax.swing.BorderFactory.createEmptyBorder(0,0,0,0);
            pnl.setBorder(emptyBorder);
            pnl.add(javaObjectEDT('javax.swing.JLabel', ' '));
            this.btnRestrict=Gui.CheckBox(Html.Wrap(Html.Img(...
                'hide.png')), false, [],...
                [], @(h,e)restrict(this), ...
                Html.Wrap(['Select ' Html.Img('magnify.png') ...
                ' to restrict list to found items']));
            pnl.add(this.btnRestrict);

            this.btnUp=Gui.ImageButton(...
                fullfile(this.path, 'upArrow.png'), ...
                'Find next', ...
                @(h,e)nextSearch(this, -1));
            pnl.add(this.btnUp);
            this.btnDown=Gui.ImageButton(...
                fullfile(this.path, 'downArrow.png'), ...
                'Find previous', ...
                @(h,e)nextSearch(this, 1));
            pnl.add(this.btnDown);
            this.foundLabel=javaObjectEDT('javax.swing.JLabel');
            pnl.add(this.foundLabel);
            pnl.add(jtf);
            btnClear=Gui.ImageButton(...
                fullfile(BasicMap.Path, 'cancel.gif'), ...
                'Clear/close search', @(h,e)clearSearch(this));
            pnl.add(btnClear);
            pnl.add(javaObjectEDT('javax.swing.JLabel', '     '));
            this.searchPnl.add(pnl, 'East');
            this.searchJtf=jtf;
            this.btnDown.setPreferredSize(java.awt.Dimension(12, 16))
            this.btnUp.setPreferredSize(java.awt.Dimension(12, 16))
        end

        function restrict(this)
            if this.btnRestrict.isSelected
                this.btnUp.setVisible(false);
                this.btnDown.setVisible(false);
                if ~isempty(this.map)
                    this.map.set(this.propRestrict, 'true');
                end
            else
                this.btnUp.setVisible(true);
                this.btnDown.setVisible(true);
                if ~isempty(this.map)
                    this.map.set(this.propRestrict, 'false');
                end
            end
            this.sortAscending=~this.sortAscending;
            this.refreshSearchAndSort;
        end

        function toggleSearch(this)
            if this.searching
                this.allChbPnl.remove(this.searchPnl);
                if ~isempty(this.map)
                    this.map.set(this.propSearch, 'false');
                end
            else
                sz=this.innerPnl.getSize;
                if sz.width==0
                    sz=this.checkBoxes.get(0).getParent.getSize;
                end
                if sz.width>0 && sz.width<500
                    where='North';
                    if sz.width<200
                        this.searchJtf.setColumns(9);
                    end
                else
                    where='Center';
                end

                this.allChbPnl.add(this.searchPnl, where);
                this.searchJtf.requestFocus;
                if ~isempty(this.map)
                    this.map.set(this.propSearch, 'true');
                end

            end
            this.allChbPnl.repaint;
            this.searching=~this.searching;
            this.dlg.pack;
        end

        function nextSearch(this, i)
            N=length(this.found);
            this.foundIdx=this.foundIdx+i;
            if this.foundIdx>N
                this.foundIdx=1;
            elseif this.foundIdx<1
                this.foundIdx=N;
            end
            cb_=this.found{this.foundIdx};
            b=cb_.getBounds;
            b.height=b.height*2.5;
            cb_.getParent.scrollRectToVisible(b);
            this.handleFound;
        end

        function clearSearch(this)
            if this.searchJtf.getText.length==0
                this.toggleSearch;
            else
                this.searchJtf.setText('');
                this.found={};
                this.search(this.searchJtf);
                this.handleFound;
            end
        end

        function search(this, h)
            this.found={};
            this.visibleIdxs=[];
            N=this.nOptions-1;
            s=lower(char(h.getText));
            N=this.nOptions-1;
            if isempty(this.strings)
                this.strings={};
                for i=0:N
                    this.strings{end+1}=string(lower(...
                        char(edu.stanford.facs.swing.Basics.RemoveXml(...
                        this.checkBoxes.get(i).getText))));
                end
            end
            this.firstCb=this.checkBoxes.get(0);
            if isempty(s)
                for i=0:N
                    cb_=this.checkBoxes.get(i);
                    cb_.setEnabled(true);
                end
            else
                first=false;
                N=this.nOptions;
                for i=1:N
                    idx=this.sortIdxs(i);
                    cb_=this.checkBoxes.get(idx-1);
                    if this.strings{idx}.contains(s)
                        cb_.setEnabled(true);
                        if ~first
                            this.firstCb=cb_;
                            first=true;
                        end
                        this.found{end+1}=cb_;
                        this.visibleIdxs(end+1)=idx;
                    else
                        cb_.setEnabled(false);
                    end
                end
            end
            this.foundIdx=1;
            if this.btnRestrict.isSelected
                this.sortAscending=~this.sortAscending;
                this.refreshSearchAndSort;
            else
                this.handleFound;
                this.seeFirstCb;
                if ~this.givenTip && h.getText.length==1
                    this.givenTip=true;
                    MatBasics.RunLater(@(h,e)tip, .51);
                end
            end
            %this.dlg.pack;
            function tip
                try
                    BasicMap.Global.showToolTip(this.btnRestrict,...
                        [], 2, -50);
                catch ex
                    disp(ex);
                end
            end
        end

        function seeFirstCb(this)
            cb_=[];
            if ~isempty(this.firstCb)
                cb_=this.firstCb;
            else
                for jj=0:this.nOptions-1
                    if this.checkBoxes.get(jj).isSelected
                        cb_=this.checkBoxes.get(jj);
                    end
                end
            end
            if ~isempty(cb_)
                b=cb_.getBounds;
                b.height=b.height*2.5;
                cb_.getParent.scrollRectToVisible(b);
            end
        end

        function [idxs_, N]=setAllChbText(this)
            [idxs_, N]=Gui.SetAllChb(this.allChb, this.allMsg, this.checkBoxes);
        end

        function idxs=reorder(this, idxs)
            idxs=MatBasics.Sort1D(idxs, this.sortIdxs);
        end
    end

    methods(Static)
        function items=EncodeLabelsAndList(labels, list, gtp)
            if iscell(list)
                N=length(list);
            else
                N=list.size;
            end
            app=BasicMap.Global;
            items=cell(1, N);
            for i=1:N
                if iscell(list)
                    item=list{i};
                else
                    item=list.get(i-1);
                end
                frequency=sum(labels==i);
                cue1=Html.EncodeSort('frequency', frequency);
                cue2=Html.EncodeSort('name', item);
                if nargin>2
                    frequency=gtp.encodeCount(frequency) ;
                else
                    frequency=String.encodeCount(frequency);
                end

                items{i}=['' item app.smallStart...
                        ''...
                        ' <' frequency ...
                        ' events>'  app.smallEnd ...
                        cue1 cue2 ''];
            end
        end
    end
end


function [filename,xform] = ctrSave(ctr,filename,xform)
% Save Contrack (ctr) structure to file.
%
%  [filename,xform] = ctrSave(ctr,filename)
%
% Saves the ConTrack parameters (in the ctr structure) to a file in ASCII
% format. This file is read by ConTrack on execution.  This text file is
% normally saved relative to the dt6 directory in the parallel directory
% of fibers, ..\fibers\contrack\XXX.
%
% contrack itself is called from the dt6 file directory (p.dt6Dir)
%
% See also ctrCreate, ctrGet, ctrSet
%

% Check input parameters
if notDefined('ctr'), error(' ConTrack structure required.'); end
if (notDefined('filename'))
    filename = mrvSelectFile('w','txt',[],'ConTrack parameters filename');
    if isempty(filename), disp('User canceled'); return; end
end
if notDefined('xform'), xform = eye(4); end

% Open up the file, checking that the user sent in a good file path
fileDir = fileparts(filename);
if isempty(fileDir), fileDir=pwd; end

if ~isdir(fileDir), error('No directory %s',fileDir);
else fid = fopen(filename,'wt');
    if(fid == -1)
        error('Can not write ConTrack parameters file.');
    end
end

% Start the writing
fprintf(fid,'Params: %.0f\n',ctrGet(ctr,'version'));  % Should this be a version parameter
fprintf(fid,'Image Directory: %s/ \n',ctrGet(ctr,'image_directory'));
fprintf(fid,'WM/GM Mask Filename: %s\n',ctrGet(ctr,'fa_filename'));
fprintf(fid,'PDF Filename: %s\n',ctrGet(ctr,'pdf_filename'));
fprintf(fid,'ROI MASK Filename: %s\n',ctrGet(ctr,'mask_filename'));
fprintf(fid,'Desired Samples: %g\n',ctrGet(ctr,'desired_samples'));
fprintf(fid,'Max Pathway Nodes: %g\n',ctrGet(ctr,'max_nodes'));
fprintf(fid,'Min Pathway Nodes: %g\n',ctrGet(ctr,'min_nodes'));
fprintf(fid,'Step Size (mm): %g\n',ctrGet(ctr,'step_size'));
fprintf(fid,'Start Is Seed VOI: %s\n',ctrGet(ctr,'roi',1,'seed_region'));
fprintf(fid,'End Is Seed VOI: %s\n',ctrGet(ctr,'roi',2,'seed_region'));
fprintf(fid,'Save Out Spacing: %g\n',ctrGet(ctr,'save_out_spacing'));
fprintf(fid,'Threshold for WM/GM specification: %g\n',ctrGet(ctr,'fa_absorb'));
fprintf(fid,'Absorption Rate WM: %g\n',ctrGet(ctr,'abs_normal'));
fprintf(fid,'Absorption Rate NotWM: %g\n',ctrGet(ctr,'abs_penalty'));
fprintf(fid,'Local Path Segment Smoothness Standard Deviation: %g\n',ctrGet(ctr,'smooth_std'));
fprintf(fid,'Local Path Segment Angle Cutoff: %g\n',ctrGet(ctr,'angle_cutoff'));

% Can't we use num2str here?  Is it an issue for Tony's read?
fprintf(fid,'ShapeFunc Params (LinMidCl,LinWidthCl,UniformS): [ %s ]\n',getStrFromVec(ctrGet(ctr,'shape_params_vec')));

% Close up
fclose(fid);

return;

%------------------------------------------------
function str = getStrFromVec(vec)
% Shouldn't this just be num2str?
str = [];
for ii = 1:length(vec)
    str = [str num2str(vec(ii)) ', ']; %#ok<AGROW>
end
str = str(1:end-2);
return;


function visMissingRecall_spatial(first_idxs,annolist_gt,xaxis,saveTo)

if (~exist(saveTo,'dir'))
    mkdir(saveTo);
end

for j = 1:length(first_idxs)
    clf;
    img = imread(annolist_gt(j).image.name);
    imagesc(img); axis equal; hold on;
    rects = annolist_gt(j).annorect(first_idxs{j});

    points = [];
    for ridx = 1:length(rects)
        p1 = util_get_annopoint_by_id(rects(ridx).annopoints.point,xaxis(2));
        p2 = util_get_annopoint_by_id(rects(ridx).annopoints.point,xaxis(1));
        points = [points; [p1.x p1.y p2.x p2.y]];
%         if (isfield(rects(ridx), 'annopoints') && ~isempty(rects(ridx).annopoints) && ...
%             isfield(rects(ridx).annopoints, 'point') && ~isempty(rects(ridx).annopoints.point))
%             point = util_get_annopoint_by_id(rects(ridx).annopoints.point,jidx);
%             if (~isempty(point))
%                 points = [points; [point.x point.y]];
%             end
%         end
    end
%     plot(points(:,1),points(:,2),'ro','MarkerFaceColor','y','MarkerEdgeColor','k','MarkerSize',5);
    for i = 1:size(points,1)
        plot([points(i,1),points(i,3)],[points(i,2),points(i,4)],'y-','lineWidth',3);
    end
%     lname = sprintf('detection, score: %1.2f, dist: %1.2f\n',first_scores(j),first_dist(j));
%     legend({lname,'GT'},'fontSize',12);
    axis off;
%     fprintf('imgidx: %d\n',first_idxs(j));
    print(gcf, '-dpng', [saveTo '/imgidx_' padZeros(num2str(j),5) '.png']);
end

end


function CPD = set_params(CPD, varargin)
% SET_PARAMS Set the parameters (fields) for a tabular_decision_node object
% CPD = set_params(CPD, name/value pairs)
%
% The following optional arguments can be specified in the form of name/value pairs:
%
% policy - the table containing the policy
%
% e.g., CPD = set_params(CPD, 'policy', T)

args = varargin;
nargs = length(args);
for i=1:2:nargs
  switch args{i},
   case 'policy',   CPD.CPT = args{i+1};
   otherwise,
    error(['invalid argument name ' args{i}]);
  end
end


function varargout = printsize(varargin)
% VL_PRINTSIZE  Set the printing size of a figure
%   VL_PRINTSIZE(R) adjusts the PaperPosition property of the current
%   figure to make the figure paper width equal to R times the width
%   of a 'uslsetter' page, while preserving the aspect ratio of the
%   figure. Then, it sets the PaperSize property to match tightly the
%   size of the figure.
%
%   VL_PRINTSIZE(FIG,R) operates on the specified figure FIG. The
%   function accepts the following optional arguments:
%
%   AspectRatio:: []
%     Change the figure aspect ratio (width/height) to the specified
%     value.
%
%   Reference:: 'horizontal'
%     If set to 'horizontal', VL_PRINTSIZE(R) makes the width of the
%     figure equal to a fraction R of the width of the page. If set to
%     'vertical', the height is used instead.
%
%   PaperType:: 'usletter'
%     Set the type of the reference page to the specified type. Any of
%     the paper types supported by MATLAB can be used (see
%     PRINT()).
%
%   Margin:: 0
%     If greater than zero, VL_PRINTSIZE(R) leaves the specified
%     margin around the figure rather than fitting the paper size
%     tightly around it. The margin is expressed as a fraction of the
%     figure paper width (or height if Reference is set to
%     'vertical').
%
%   PRINTING FIGURES IN MATLAB
%
%   The standard procedure to include a MATLAB figure in a publication
%   consists in printing the figure to an EPS or PDF file (by using
%   the PRINT() function) and then rescaling it to the desired size in
%   the publication. PDF printing has the additional inconvenience
%   that large white margins are generated, and requires cropping the
%   figure too.
%
%   Unfortunately, rescalign a figure in this manner has the usually
%   unwanted effect of rescaling elements of the graphics such as font
%   sizes and line thicknesses. For example, a font which is 12pt in
%   the original MATLAB figure may become too large or too small after
%   rescaling.
%
%   The `proper' way of printing a figure is to specify its desired
%   size on paper before calling the PRINT() function. VL_PRINTSIZE()
%   helps doing so.
%
%   Margins are an additional nuances. When printing to certain
%   formats such as PDF, MATLAB prints in fact entire page (e.g. a
%   whole US Letter sheet), resulting in wide white margin all around
%   the figure. VL_PRINTSIZE() alleviates this problem by setting the
%   paper size to match the figure size tightly. This does not
%   eliminate margins completely, especially if the figure has
%   multiple subplots, but it is certainly a large improvement.
%
%   See also: VL_HELP().
[varargout{1:nargout}] = vl_printsize(varargin{:});


function TS_LoadFeatures(whatDataFile,TS_DataMatToLoad,TS_CalcTimeToLoad,TS_QualityToLoad,INP_opsToLoad,INP_tsToLoad)
% TS_LoadFeatures   Load some pre-computed features into an hctsa data file
%                     if using externally computed values, e.g., from Catch22.
%
%---INPUTS:
% whatDataFile, the input/output hctsa file (already created with TS_Init)
% TS_DataMatToLoad, the feature matrix to load, as a time series-by-features
%               matrix
% TS_CalcTimeToLoad, the calculation time for each operation, same size as
%                       TS_DataMatToLoad
% TS_QualityToLoad, the quality for each operation (zero means no issues), same
%                       size as TS_DataMatToLoad
% INP_opsToLoad, the names of the hctsa operations, calculation and quality
%                   matrices to be loaded (see INP_ops.txt)
% INP_tsToLoad, the names of the time series that are in whatDataFile that
%                   we're loading
%
% whatDataFile should contain all operations specified in TS_opsToLoad

% ------------------------------------------------------------------------------
% If you use this code for your research, please cite the following two papers:
%
% (1) B.D. Fulcher and N.S. Jones, "hctsa: A Computational Framework for Automated
% Time-Series Phenotyping Using Massive Feature Extraction, Cell Systems 5: 527 (2017).
% DOI: 10.1016/j.cels.2017.10.001
%
% (2) B.D. Fulcher, M.A. Little, N.S. Jones, "Highly comparative time-series
% analysis: the empirical structure of time series and their methods",
% J. Roy. Soc. Interface 10(83) 20130048 (2013).
% DOI: 10.1098/rsif.2013.0048
%
% This work is licensed under the Creative Commons
% Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of
% this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send
% a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View,
% California, 94041, USA.
% ------------------------------------------------------------------------------

% whatDataFile must be a filename

% Load in the data:
TS_DataMat = TS_GetFromData(whatDataFile,'TS_DataMat');
TS_CalcTime = TS_GetFromData(whatDataFile,'TS_CalcTime');
TS_Quality = TS_GetFromData(whatDataFile,'TS_Quality');

% Which operations are we loading?
if isempty(INP_opsToLoad)
    myOpIDs = 1:size(TS_DataMat,2);
else
    myOpIDs = TS_GetIDs(INP_opsToLoad,whatDataFile,'ops','Name');
end

% Which time series are we loading?
if isempty(INP_tsToLoad)
    myTSIDs = 1:size(TS_DataMat,1);
else
    myTSIDs = TS_GetIDs(INP_tsToLoad,whatDataFile,'ts','Name');
end

% Fill the data:
TS_DataMat(myTSIDs,myOpIDs) = TS_DataMatToLoad;
TS_CalcTime(myTSIDs,myOpIDs) = TS_CalcTimeToLoad;
TS_Quality(myTSIDs,myOpIDs) = TS_QualityToLoad;

% Save to the data file
save(whatDataFile,'TS_DataMat','TS_CalcTime','TS_Quality','-append');

end


% VL_IHASHFIND
%  SEL = VL_IHASHFIND(ID, NEXT, K, X)


function [studylist, dosetype]=dicomrt_DICOMdirscan(path2scan)
% dicomrt_DICOMdirscan(path2scan)
%
% Scan a directory for DICOM studies.
%
% path2scan is the directory path that will be scanned. Scan is not recursive.
%
% Returns a cell array "studylist" containing a list of the studies found in directory "path2scan".
%
% This is the structure of studylist
%
%    Patients         RTPLAN             RTDOSE            CT               RTSTRUCTURE      MR
%      Name           series             series            series           series           studies
%  ----------------------------------------------------------------------------------------------------------|
%  | [Name 1] |[L 1] [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] |
%  |          |                      |                 |                 |                 |                 |
%  |          |[L j] [D j] [Flist j] | [D k] [Flist k] | [D l] [Flist l] | [D m] [Flist m] | [D n] [Flist n] |
%  |          |                      |                 |                 |                 |                 |
%  ----------------------------------------------------------------------------------------------------------|
%  |               ...                                    ...                                                |
%  ----------------------------------------------------------------------------------------------------------|
%  | [Name p] |[L 1] [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] | [D 1] [Flist 1] |
%  |          |                      |                 |                 |                 |                 |
%  |          |[L q] [D q] [Flist q] | [D r] [Flist r] | [D s] [Flist s] | [D t] [Flist t] | [D v] [Flist v] |
%  |          |                      |                 |                 |                 |                 |
%  ----------------------------------------------------------------------------------------------------------|
%
% where L=Label, D=Date, Flist=Filename list, j-v are indices
%
% See also: dicomrt_DICOMscan, dicomrt_DICOMimport
%
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)
% LM DK 11/16/2005 if the files do not have extension it checks if the
% files are philips DICOM files if the answer is true it renames them with
% DICOM extension .dcm
%
% LM  DK changed the data structure of studylist to incorporate multiple
% rtdose and rtplan file import.
global dosetype

if nargin == 0
    [path2scan] = uigetdir('*.*','Select the directory that you want to scan');
elseif ischar(path2scan)~=1
    error('dicomrt_DICOMdirscan: The input path is not a character string. Exit now!');
end

% Allowed extensions
oklist=strvcat('dcm','DCM','ct','CT','rtp','RTP','rtstruct','RTSTRUCT','ima','IMA');

% List directory contents
list=dir(path2scan);

%
PatientName=[];        % initialize character array with patients name
studyUID=[];            % initialize character array with study UID
studylist=cell(1,6);    % initialize character array with study UID
nrtplans=0;             % initialize number of different RTPLANS
nrtdoses=0;             % initialize number of different RTDOSES series
nrtcts=0;               % initialize number of different CT series
nrtstructs=0;           % initialize number of different RTSTRUCTURES
nrtmrs=0;               % initialize number of different MR series
nrtpts = 0;             % initialize number of different PET series
nseries=0;              % initialize number of series whitin current study

% Set progress bar
list=dir(path2scan);
h = waitbar(0,'Scanning progress');
set(h,'Name',['dicomrt_DICOMimport: ',path2scan]);

for k=1:size(list,1)
    if list(k).isdir==0
        try
            dictFlg = checkDictUse;
            if dictFlg
                if isdeployed
                    temp = dicominfo(fullfile(path2scan,list(k).name), 'dictionary',...
                        fullfile(getCERRPath,'bin','ES - IPT4.1CompatibleDictionary.mat'));
                else
                    temp = dicominfo(fullfile(path2scan,list(k).name), 'dictionary',...
                        'ES - IPT4.1CompatibleDictionary.mat');
                end
            else
                temp=dicominfo(fullfile(path2scan,list(k).name));
            end
        catch
            warning(['file ' list(k).name ' not DICOM']);
            continue;
        end
        if strcmpi(temp.Format,'DICOM')

            % check if the loaded DICOM file is valid (based on Modality field)
            if ~isfield(temp,'Modality')
                try
                    temp=dicominfo(fullfile(path2scan,list(k).name));
                    if ~isfield(temp,'Modality')
                        warning(['file ' list(k).name ' is not a valid DICOM. ignoring...']);
                    end
                end
                continue;
            end

            % check if a patient name has been already loaded
            if isempty(PatientName)==0
                % yes, a patient name has been already loaded
                % check if this patient's name has been already loaded
                matchname=strcmpi(temp.PatientName.FamilyName,PatientName);
                if matchname
                    % no, add this the name to the list
                    PatientName=strvcat(PatientName,temp.PatientName.FamilyName);
                    % add this patient to the studylist
                    matchname=size(studylist,1)+1;
                    studylist{matchname,1}=temp.PatientName.FamilyName;
                    % flag this is a new patient
                    newpatient=1;
                else
                    % nothing to be done for now
                    % flag this is not a new patient
                    newpatient=0;
                end
            else
                % no, add this name as first name in the list
                PatientName=strvcat(PatientName,temp.PatientName.FamilyName);
                % add this patient to the studylist
                studylist{1,1}=temp.PatientName.FamilyName;
                % flag this is a new patient
                newpatient=1;
                % set accordingly matchname
                matchname=1;
            end
            if newpatient==0
                % we are not dealing with a new patient
                % check if a study has been already loaded for this patient
                if isempty(studyUID)==0
                    % yes, a study has been already loaded
                    % check if this study has been already loaded
                    matchstudy = strcmpi(temp.StudyInstanceUID,studyUID);
                    if matchstudy
                        % no, add this study to the list
                        studyUID=strvcat(studyUID,temp.StudyInstanceUID);
                        % add this study to the studylist
                        matchstudy=size(studylist{matchname,2},1)+1;
                        if strcmpi(temp.Modality,'RTPLAN')==1
                            % update nrtplans
                            nrtplans=nrtplans+1;
                            studylist{matchname,2}{nrtplans,1}=temp.RTPlanLabel;
                            studylist{matchname,2}{nrtplans,2}=temp.StudyDate;
                            if size(studylist{matchname,2},2)==2
                                studylist{matchname,2}{nrtplans,3}=temp.Filename;
                            else
                                studylist{matchname,2}{nrtplans,3}=strvcat(studylist{matchname,2}{nrtplans,3},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'rtdose')==1
                            % update nrtdoses
                            nrtdoses=nrtdoses+1;
                            dosetype = temp.DoseSummationType;
                            studylist{matchname,3}{nrtdoses,1}=temp.StudyDate;
                            if size(studylist{matchname,3},2)==1
                                studylist{matchname,3}{nrtdoses,2}=temp.Filename;
                            else
                                studylist{matchname,3}{nrtdoses,2}=strvcat(studylist{matchname,3}{nrtdoses,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'CT')==1
                            % update nrtcts
                            nrtcts=nrtcts+1;
                            studylist{matchname,4}{nrtcts,1}=temp.StudyDate;
                            if size(studylist{matchname,4},2)==1
                                studylist{matchname,4}{nrtcts,2}=temp.Filename;
                            else
                                studylist{matchname,4}{nrtcts,2}=strvcat(studylist{matchname,4}{nrtcts,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'RTSTRUCT')==1
                            % update nrtstructs
                            nrtstructs=nrtstructs+1;
                            studylist{matchname,5}{nrtstructs,1}=temp.StudyDate;
                            if size(studylist{matchname,5},2)==1
                                studylist{matchname,5}{nrtstructs,2}=temp.Filename;
                            else
                                studylist{matchname,5}{nrtstructs,2}=strvcat(studylist{matchname,5}{nrtstructs,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'MR')==1
                            % update nrtmrs
                            nrtmrs=nrtmrs+1;
                            studylist{matchname,6}{nrtmrs,1}=temp.StudyDate;
                            if size(studylist{matchname,6},2)==1
                                studylist{matchname,6}{nrtmrs,2}=temp.Filename;
                            else
                                studylist{matchname,6}{nrtmrs,2}=strvcat(studylist{matchname,6}{nrtmrs,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'PET')==1 | strcmpi(temp.Modality,'PT')==1
                            % update nrtmrs
                            nrtpts=nrtpts+1;
                            studylist{matchname,7}{nrtpts,1}=temp.StudyDate;
                            if size(studylist{matchname,7},2)==1
                                studylist{matchname,7}{nrtpts,2}=temp.Filename;
                            else
                                studylist{matchname,7}{nrtpts,2}=strvcat(studylist{matchname,7}{nrtpts,2},temp.Filename);
                            end

                        % Scan for SPECT images
                        elseif strcmpi(temp.Modality,'NM')
                            nrtpts = nrtpts+1;
                            if nrtpts == 1
                                studylist{matchname,8}{nrtpts,1}= temp.StudyDate;
                                if size(studylist{matchname,8},2)==1
                                    studylist{matchname,8}{nrtpts,2}= temp.Filename;
                                else % add to new scan as SPECT are multiframe studies
                                    warning('Multiple SPECT study import work in progress');
                                end
                            else
                                warning('Multiple SPECT study import work in progress');
                            end
                        end
                        % flag this is a new study
                        newstudy=1;
                    else
                        % this study has been already loaded
                        if strcmpi(temp.Modality,'RTPLAN')==1
                            nrtplans=nrtplans+1;
                            try
                                studylist{matchname,2}{nrtplans,1}=temp.RTPlanLabel;
                            catch
                                studylist{matchname,2}{nrtplans,1}='Unknown';
                            end
                            studylist{matchname,2}{nrtplans,2}=temp.StudyDate;
                            if size(studylist{matchname,2},2)==2
                                studylist{matchname,2}{nrtplans,3}=temp.Filename;
                            else
                                studylist{matchname,2}{nrtplans,3}=strvcat(studylist{matchname,2}{nrtplans,3},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'rtdose')==1
                            % DK
                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                            if nrtdoses==0
                                nrtdoses=1;
                            elseif strcmpi(temp.DoseSummationType,'FRACTION')
                                nrtdoses = nrtdoses+1;
                            end
                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                            studylist{matchname,3}{nrtdoses,1}=temp.StudyDate;
                            if size(studylist{matchname,3},2)==1
                                studylist{matchname,3}{nrtdoses,2}=temp.Filename;
                            else
                                studylist{matchname,3}{nrtdoses,2}=strvcat(studylist{matchname,3}{nrtdoses,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'CT')==1
                            if nrtcts==0
                                nrtcts=1;
                            end
                            studylist{matchname,4}{nrtcts,1}=temp.StudyDate;
                            if size(studylist{matchname,4},2)==1
                                studylist{matchname,4}{nrtcts,2}=temp.Filename;
                            else
                                studylist{matchname,4}{nrtcts,2}=strvcat(studylist{matchname,4}{nrtcts,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'RTSTRUCT')==1
                            nrtstructs=nrtstructs+1;
                            studylist{matchname,5}{nrtstructs,1}=temp.StudyDate;
                            if size(studylist{matchname,5},2)==1
                                studylist{matchname,5}{nrtstructs,2}=temp.Filename;
                            else
                                studylist{matchname,5}{nrtstructs,2}=strvcat(studylist{matchname,5}{nrtstructs,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'MR')==1
                            studylist{matchname,6}{nrtmrs,1}=temp.StudyDate;
                            if size(studylist{matchname,6},2)==1
                                studylist{matchname,6}{nrtmrs,2}=temp.Filename;
                            else
                                studylist{matchname,6}{nrtmrs,2}=strvcat(studylist{matchname,6}{nrtmrs,2},temp.Filename);
                            end
                        elseif strcmpi(temp.Modality,'PET')==1 | strcmpi(temp.Modality,'PT')==1
                            % update nrtmrs
                            nrtpts=nrtpts+1;
                            studylist{matchname,7}{nrtpts,1}=temp.StudyDate;
                            if size(studylist{matchname,7},2)==1
                                studylist{matchname,7}{nrtpts,2}=temp.Filename;
                            else
                                studylist{matchname,7}{nrtpts,2}=strvcat(studylist{matchname,7}{nrtpts,2},temp.Filename);
                            end

                        elseif strcmpi(temp.Modality,'NM')
                            nrtpts = nrtpts+1;
                            if nrtpts == 1
                                studylist{matchname,8}{nrtpts,1}= temp.StudyDate;
                                if size(studylist{matchname,8},2)==1
                                    studylist{matchname,8}{nrtpts,2}= temp.Filename;
                                else % add to new scan as SPECT are multiframe studies
                                    warning('Multiple SPECT study import work in progress');
                                end
                            else
                                warning('Multiple SPECT study import work in progress');
                            end
                        end
                        % flag this is a new study
                        newstudy=0;
                    end
                else
                    error('dicomrt_DICOMdirscan: there is a problem: old patient and no study already in ?');
                end
            elseif newpatient==1
                % we are dealing with a new patient
                % add this study to the list
                studyUID=strvcat(studyUID,temp.StudyInstanceUID);
                % add this study to the studylist
                if strcmpi(temp.Modality,'RTPLAN')==1
                    % update nrtplans
                    nrtplans=1;
                    studylist{matchname,2}{nrtplans,1}=temp.RTPlanLabel;
                    studylist{matchname,2}{nrtplans,2}=temp.StudyDate;
                    if size(studylist{matchname,2},2)==2
                        studylist{matchname,2}{nrtplans,3}=temp.Filename;
                    else
                        studylist{matchname,2}{nrtplans,3}=strvcat(studylist{matchname,2}{nrtplans,3},temp.Filename);
                    end
                elseif strcmpi(temp.Modality,'rtdose')==1
                    % update nrtdoses
                    if strcmpi(temp.DoseSummationType,'FRACTION')
                        nrtdoses = nrtdoses +1;
                    else
                        nrtdoses=1;
                    end
                    studylist{matchname,3}{nrtdoses,1}=temp.StudyDate;
                    if size(studylist{matchname,3},2)==1
                        studylist{matchname,3}{nrtdoses,2}=temp.Filename;
                    else
                        studylist{matchname,3}{nrtdoses,2}=strvcat(studylist{matchname,3}{nrtdoses,2},temp.Filename);
                    end
                elseif strcmpi(temp.Modality,'CT')==1
                    % update nrtcts
                    nrtcts=1;
                    studylist{matchname,4}{nrtcts,1}=temp.StudyDate;
                    if size(studylist{matchname,4},2)==1
                        studylist{matchname,4}{nrtcts,2}=temp.Filename;
                    else
                        studylist{matchname,4}{nrtcts,2}=strvcat(studylist{matchname,4}{nrtcts,2},temp.Filename);
                    end
                elseif strcmpi(temp.Modality,'RTSTRUCT')==1
                    % update ntrstructs
                    ntrstructs=1;
                    studylist{matchname,5}{ntrstructs,1}=temp.StudyDate;
                    if size(studylist{matchname,5},2)==1
                        studylist{matchname,5}{ntrstructs,2}=temp.Filename;
                    else
                        studylist{matchname,5}{ntrstructs,2}=strvcat(studylist{matchname,5}{ntrstructs,2},temp.Filename);
                    end
                elseif strcmpi(temp.Modality,'MR')==1
                    % update ntrstructs
                    nrtmrs=1;
                    studylist{matchname,6}{nrtmrs,1}=temp.StudyDate;
                    if size(studylist{matchname,6},2)==1
                        studylist{matchname,6}{nrtmrs,2}=temp.Filename;
                    else
                        studylist{matchname,6}{nrtmrs,2}=strvcat(studylist{matchname,6}{nrtmrs,2},temp.Filename);
                    end
                elseif strcmpi(temp.Modality,'PET')==1 | strcmpi(temp.Modality,'PT')==1
                    % update nrtmrs
                    nrtpts=1;
                    studylist{matchname,7}{nrtpts,1}=temp.StudyDate;
                    if size(studylist{matchname,7},2)==1
                        studylist{matchname,7}{nrtpts,2}=temp.Filename;
                    else
                        studylist{matchname,7}{nrtpts,2}=strvcat(studylist{matchname,7}{nrtpts,2},temp.Filename);
                    end

                elseif strcmpi(temp.Modality,'NM')
                    nrtpts = nrtpts+1;
                    if nrtpts == 1
                        studylist{matchname,8}{nrtpts,1}= temp.StudyDate;
                        if size(studylist{matchname,8},2)==1
                            studylist{matchname,8}{nrtpts,2}= temp.Filename;
                        else % add to new scan as SPECT are multiframe studies
                            warning('Multiple SPECT study import work in progress');
                        end
                    else
                        warning('Multiple SPECT study import work in progress');
                    end

                end
            end
            % flag this is a new study
            newstudy=1;
        end
    end
    waitbar(k/size(list,1),h);
end

% Close progress bar
close(h);
clear temp


% POP_BIOSIG - import data files into EEGLAB using BIOSIG toolbox
%
% Usage:
%   >> OUTEEG = pop_biosig; % pop up window
%   >> OUTEEG = pop_biosig( filename, channels, type);
%
% Inputs:
%   filename - [string] file name
%
% Optional inputs:
%   'channels'   - [integer array] list of channel indices
%   'blockrange' - [min max] integer range of data blocks to import, in seconds.
%                  Entering [0 3] will import the first three blocks of data.
%                  Default is empty -> import all data blocks.
%   'ref'        - [integer] channel index or index(s) for the reference.
%                  Reference channels are not removed from the data,
%                  allowing easy re-referencing. If more than one
%                  channel, data are referenced to the average of the
%                  indexed channels. WARNING! Biosemi Active II data
%                  are recorded reference-free, but LOSE 40 dB of SNR
%                  if no reference is used!. If you do not know which
%                  channel to use, pick one and then re-reference after
%                  the channel locations are read in. {default: none}
%   'rmeventchan' - ['on'|'off'] remove event channel after event
%                  extraction. Default is 'on'.
%
% Outputs:
%   OUTEEG   - EEGLAB data structure
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, Oct. 29, 2003-
%
% Note: BIOSIG toolbox must be installed. Download BIOSIG at
%       http://biosig.sourceforge.net
%       Contact a.schloegl@ieee.org for troubleshooting using BIOSIG.

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

function [EEG, command] = my_pop_biosig(filename, varargin);
EEG = [];
command = '';

if nargin < 1
	% ask user
	[filename, filepath] = uigetfile('*.*', 'Choose an BDF file -- pop_biosig()'); %%% this is incorrect in original version!!!!!!!!!!!!!!
    drawnow;

	if filename == 0 return; end
	filename = [filepath filename];

    % open file to get infos
    % ----------------------
    disp('Reading data file header...');
    dat = sopen(filename);

    % special BIOSEMI
    % ---------------
    if strcmpi(dat.TYPE, 'BDF')
        disp('We highly recommend that you choose a reference channel IF these are Biosemi data');
        disp('(e.g., a mastoid or other channel). Otherwise the data will lose 40 dB of SNR!');
    end
    uilist = { { 'style' 'text' 'String' 'Channel list (default all):' } ...
                 { 'style' 'edit' 'string' '' } ...
                 { 'style' 'text' 'String' [ 'Data range (in seconds) to read (default all [0 ' int2str(dat.NRec) '])' ] } ...
                 { 'style' 'edit' 'string' '' } ...
                 { 'style' 'text' 'String' 'Extract event - cannot be unset (set=yes)' } ...
                 { 'style' 'checkbox' 'string' '' 'value' 1 'enable' 'on' } {} ...
                 { 'style' 'text' 'String' 'Import continuous data (set=yes)' 'value' 1} ...
                 { 'style' 'checkbox' 'string' '' 'value' 0 } {} ...
                 { 'style' 'text' 'String' 'Reference chan(s) indices - required for BIOSEMI' } ...
                 { 'style' 'edit' 'string' '' } };
    geom = { [3 1] [3 1] [3 0.35 0.5] [3 0.35 0.5] [3 1] };
    result = inputgui( geom, uilist, 'pophelp(''pop_biosig'')', ...
                                 'Load data using BIOSIG -- pop_biosig()');
    if length(result) == 0 return; end

    % decode GUI params
    % -----------------
    options = {};
    if ~isempty(result{1}), options = { options{:} 'channels'   eval( [ '[' result{1} ']' ] ) }; end
    if ~isempty(result{2}), options = { options{:} 'blockrange' eval( [ '[' result{2} ']' ] ) }; end
    if length(result) > 2
        if ~isempty(result{4}), options = { options{:} 'ref'        eval( [ '[' result{4} ']' ] ) }; end
        if ~result{3},          options = { options{:} 'rmeventchan' 'off' }; end
    end
else
    options = varargin;
end

% decode input parameters
% -----------------------
g = finputcheck( options, { 'blockrange'  'integer' [0 Inf]    [];
                            'channels'    'integer' [0 Inf]    [];
                            'ref'         'integer' [0 Inf]    [];
                            'rmeventchan' 'string'  { 'on';'off' } 'on' }, 'pop_biosig');
if ischar(g), error(g); end

% import data
% -----------
EEG = eeg_emptyset;
if ~isempty(g.channels)
     dat = sopen(filename, 'r', g.channels,'OVERFLOWDETECTION:OFF');
else dat = sopen(filename, 'r', 0,'OVERFLOWDETECTION:OFF');
end
fprintf('Reading data in %s format...\n', dat.TYPE);

if ~isempty(g.blockrange)
    newblockrange    = g.blockrange;
    newblockrange(2) = min(newblockrange(2), dat.NRec);
    newblockrange    = newblockrange*dat.Dur;
    DAT=sread(dat, newblockrange(2)-newblockrange(1), newblockrange(1))';
else
    DAT=sread(dat, Inf)';% this isn't transposed in original!!!!!!!!
    newblockrange    = [];
end
dat = sclose(dat);

% convert to seconds for sread
% ----------------------------
EEG.nbchan          = size(DAT,1);
EEG.srate           = dat.SampleRate(1);
EEG.data            = DAT;
clear DAT;
% $$$ try  % why would you do the following???????  JO
% $$$     EEG.data            = EEG.data';
% $$$ catch,
% $$$     pack;
% $$$     EEG.data            = EEG.data';
% $$$ end;
EEG.setname 		= sprintf('%s file', dat.TYPE);
EEG.comments        = [ 'Original file: ' filename ];
EEG.xmin            = 0;
if strcmpi(dat.TYPE, 'BDF') || strcmpi(dat.TYPE, 'EDF')
    EEG.trials   = 1;
    EEG.pnts     = size(EEG.data,2);
else
    EEG.trials   = dat.NRec;
    EEG.pnts     = size(EEG.data,2)/dat.NRec;
end
if isfield(dat, 'Label') && ~isempty(dat.Label)
    EEG.chanlocs = struct('labels', cellstr(char(dat.Label)));
end
EEG = eeg_checkset(EEG);

% extract events % this part I totally revamped to work...  JO
% --------------
disp('Extracting events from last EEG channel...');
EEG.event = [];

% $$$ startval = mode(EEG.data(end,:)); % my code
% $$$ for p = 2:size(EEG.data,2)-1
% $$$     [codeout] = code(EEG.data(end,p));
% $$$     if EEG.data(end,p) > EEG.data(end,p-1) & EEG.data(end,p) >= EEG.data(end,p+1)
% $$$         EEG.event(end+1).latency =  p;
% $$$         EEG.event(end).type = bitand(double(EEG.data(end,p)-startval),255);
% $$$     end
% $$$ end

% lastout = mod(EEG.data(end,1),256);newevs = []; % andrey's code 8 bits
% codeout = mod(EEG.data(end,2),256);
% for p = 2:size(EEG.data,2)-1
%     nextcode = mod(EEG.data(end,p+1),256);
%     if codeout > lastout & codeout >= nextcode
%         newevs = [newevs codeout];
%         EEG.event(end+1).latency =  p;
%         EEG.event(end).type = codeout;
%     end
%     lastout = codeout;
%     codeout = nextcode;
% end

%lastout = mod(EEG.data(end,1),256*256);newevs = []; % andrey's code 16 bits
%codeout = mod(EEG.data(end,2),256*256);
%for p = 2:size(EEG.data,2)-1
%    nextcode = mod(EEG.data(end,p+1),256*256);
%    if (codeout > lastout) & (codeout >= nextcode)
%        newevs = [newevs codeout];
%        EEG.event(end+1).latency =  p;
%        EEG.event(end).type = codeout;
%    end
%    lastout = codeout;
%    codeout = nextcode;
%end

% Modifieded by Andrey (Aug.5,2008) to detect all non-zero codes:
thiscode = 0;lastcode=0;
for p = 1:size(EEG.data,2)-1
    prevcode = thiscode;
    thiscode = mod(EEG.data(end,p),256*256);   % andrey's code - 16 bits
    if (thiscode ~= 0) && (thiscode~=prevcode) && (thiscode~=lastcode) % fix to avoid repeated codes (per Ying's demand)
        EEG.event(end+1).latency =  p;
        EEG.event(end).type = thiscode;
        lastcode = thiscode;
    end
end

if strcmpi(g.rmeventchan, 'on')
    EEG.data(dat.BDF.Status.Channel,:) = [];
    EEG.nbchan = size(EEG.data,1);
    if ~isempty(EEG.chanlocs)
        EEG.chanlocs(dat.BDF.Status.Channel,:) = [];
    end
end
EEG = eeg_checkset(EEG, 'eventconsistency');

% $$$ if ~isempty(dat.EVENT)
% $$$     if isfield(dat, 'out') % Alois fix for event interval does not work
% $$$         if isfield(dat.out, 'EVENT')
% $$$             dat.EVENT = dat.out.EVENT;
% $$$         end
% $$$     end
% $$$     if ~isempty(newblockrange)
% $$$         interval(1) = newblockrange(1) * dat.SampleRate(1) + 1;
% $$$         interval(2) = newblockrange(2) * dat.SampleRate(1);
% $$$     else interval = [];
% $$$     end
% $$$     EEG.event = biosig2eeglabevent(dat.EVENT, interval); % Toby's fix
% $$$     if strcmpi(g.rmeventchan, 'on') & strcmpi(dat.TYPE, 'BDF') & isfield(dat, 'BDF')
% $$$         disp('Removing event channel...');
% $$$         EEG.data(dat.BDF.Status.Channel,:) = [];
% $$$         EEG.nbchan = size(EEG.data,1);
% $$$         if ~isempty(EEG.chanlocs)
% $$$             EEG.chanlocs(dat.BDF.Status.Channel,:) = [];
% $$$         end
% $$$     end
% $$$     EEG = eeg_checkset(EEG, 'eventconsistency');
% $$$ else
% $$$     disp('Warning: no event found. Events might be embedded in a data channel.');
% $$$     disp('         To extract events, use menu File > Import Event Info > From data channel');
% $$$ end

% rerefencing
% -----------
if ~isempty(g.ref)
    disp('Re-referencing...');
    EEG.data = EEG.data - repmat(mean(EEG.data(g.ref,:),1), [size(EEG.data,1) 1]);
    if length(g.ref) == size(EEG.data,1)
        EEG.ref  = 'average';
    end
    if length(g.ref) == 1
        disp([ 'Warning: channel ' int2str(g.ref) ' is now zeroed (but still present in the data)' ]);
    else
        disp([ 'Warning: data matrix rank has decreased through re-referencing' ]);
    end
end

% convert data to single if necessary
% -----------------------------------
EEG = eeg_checkset(EEG,'makeur');   % Make EEG.urevent field
EEG = eeg_checkset(EEG);

% history
% -------
if isempty(options)
    command = sprintf('EEG = my_pop_biosig(''%s'');', filename);
else
    command = sprintf('EEG = my_pop_biosig(''%s'', %s);', filename, vararg2str(options));
end;


function F = feval(d, loc, varargin)
%FEVAL     Evaluation functional.
%   This function is deprecated. Use FUNCTIONALBLOCK.FEVAL.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

F = linop( functionalBlock.feval(loc, double(d), varargin{:}) );

end


function alphaMets = alphabetizeMetFormulas(metFormulas)
%alphabetizeMetFormulas  Reorder metabolite formulas alphabetically.
%
% USAGE:
%
%   alphaMets = alphabetizeMetFormulas(metFormulas);
%
% INPUTS:
%
%   metFormulas     Cell array vector of metabolite atomic formulas.
%                   NOTE: formulas containing characters other than letters
%                         or digits (A-Z,a-z,0-9), or those containing the
%                         phrase "FULLR", will NOT be reordered.
%
% OUTPUTS:
%
%   alphaMets       Cell array vector of metabolite formulas rearranged in
%                   alphabetical order of the element symbols.
%                   Ex: C17H27N5O4SR2 -> C17H27N5O4R2S
%

% Ignore metabolite formulas containing special characters, or "FULLR",
% which appears in the Recon3D model.
ignoreMets = ~cellfun(@isempty,regexp(metFormulas,'[^A-Za-z0-9]','match')) | ...
             contains(metFormulas,'FULLR');

% initialize alphabetized met formulas with original met formulas
alphaMets = metFormulas;

% reorder metabolite formulas
alphaMets(~ignoreMets) = cellfun(@(F) strjoin(sort(regexp(F,'[A-Z][a-z]*\d*','match')),''),alphaMets(~ignoreMets),'UniformOutput',false);


function txt = event_datacursor_txt(~,event_obj)
    % show events off a map, filtering for x,y,z on main catalog
    % capable of showing multiple events if they
    % event_obj   Object containing event data structure
    % output_txt  Data cursor text
    %
    % currently selected event(s) can be found on desktop as datacursor_catalog

    %event_obj.Target : handle of object data cursor is referencing
    %event_obj.Position : array specifying x,y,z

    % redefine data cursor Updatefcn
    % set(dcm_obj, 'UpdateFcn',@myupdatefcn)
    %ZG=ZmapGlobal.Data;
    event_obj.Target
    event_obj.Position

    switch lower(event_obj.Target.DisplayName)
        case {'stations'}
            txt=parse_stations(event_obj);
        case {'volcanoes'}
            txt=parse_volcanoes(event_obj);
        case {'main faultline'}
            txt=parse_faults(event_obj);
        case {'plate boundaries'}
            txt=parse_plates(event_obj);

        otherwise

            txt=parse_quakes(event_obj);
    end

end

%% These parsing routines should probably be assigned to the features themselves.

function txt = parse_quakes(event_obj)
    disp(event_obj.Target)
    latTol = 0.001;
    lonTol = 0.001;
    depthTol = 0.1;
    OUTPUT_NAME='datacursor_catalog';

    assignin('base','clickedevent',event_obj)
    evpos=event_obj.Position;
    ZG=ZmapGlobal.Data;
    globalcatalog = ZG.primeCatalog;
      evNum=find(...
         abs(evpos(2) - globalcatalog.Y) < latTol &...
         abs(evpos(1) - globalcatalog.X) < lonTol &...
         abs(evpos(3) - globalcatalog.Z < depthTol));

     minicat=globalcatalog.subset(evNum);
     minicat.Name=sprintf('datacursor:%s', char(datetime(),'uuuuMMdd''T''hhmmss'));
     assignin('base',OUTPUT_NAME,minicat);

     txt={};
     if minicat.Count > 1
         txt={sprintf('%d events within position tolerance',minicat.Count)};
     end

     if minicat.Count > 3
         try
             for i =1:minicat.Count
                 if i > 5 && i<minicat.Count
                     continue
                 end
                 if i== 5 && i < minicat.Count
                     txt = [txt, {sprintf('  ... skipping %d events ...',minicat.Count-5)}];
                     continue
                 end

                 lattxt=lat_text(event_obj,minicat,i);
                 lontxt=lon_text(event_obj,minicat,i);

                 mt = minicat.MagnitudeType{i};
                 if isempty(mt)
                     mt='magnitude';
                 end
                 txt=[txt,{[sprintf('[# %5d] ',evNum(i)),...
                     sprintf(' %s',char(minicat.Date(i),'uuuu-MM-dd HH:mm:ss ; ')),...
                     sprintf('( %8s , %8s ) at %5.2f km ; ',lattxt,lontxt,evpos(3)),...
                     sprintf('%s %.1f',mt, minicat.Magnitude(i))]};
                     ];
             end

         catch ME
             warning(ME.message)
         end

     else
         try
             for i =1:minicat.Count
                 lattxt=lat_text(event_obj,minicat,i);
                 lontxt=lon_text(event_obj,minicat,i);
                 mt = minicat.MagnitudeType{i};
                 if isempty(mt)
                     mt='magnitude';
                 end
                 txt=[txt,{sprintf('- Event # %d -',evNum(i)),...
                     sprintf('%s',char(minicat.Date(i),'uuuu-MM-dd HH:mm:ss')),...
                     sprintf('( %s , %s )',lattxt,lontxt),...
                     sprintf(' %.2f km depth',minicat.Depth(i)),...
                     sprintf('%s : %.1f',mt, minicat.Magnitude(i))};
                     ];
                 if i < minicat.Count
                     txt=[txt,{''}];
                 end
             end

         catch ME
             warning(ME.message)
         end
     end
end

function txt = parse_stations(event_obj)
    % get table information

    latTol = 0.01;
    lonTol = 0.01;

    ZG=ZmapGlobal.Data;
    f=ZG.features('stations');
    evpos=event_obj.Position;
    idx=find(...
        abs(evpos(2) - f.Latitude) < latTol &...
        abs(evpos(1) - f.Longitude) < lonTol);
    txt={sprintf('%d station(s):',numel(idx))};
    for i=1:numel(idx)
        lattxt=lat_text(event_obj,f,i);
        lontxt=lon_text(event_obj,f,i);

        txt=[txt,f.Names(idx(i)),...
            sprintf('( %s , %s )',lattxt,lontxt),...
            sprintf(' %.2f km elev',-f.Depth(i))];
    end
end

function txt = parse_faults(event_obj)
    txt='fault'
    %f=ZG.features('faults');
end
function txt = parse_plates(event_obj)
    txt='plate boundary'
    %f=ZG.features('plates');
end

function txt = parse_volcanoes(event_obj)    % get table information
    latTol = 0.001;
    lonTol = 0.001;

    ZG=ZmapGlobal.Data;
    f=ZG.features('volcanoes');
    evpos=event_obj.Position;
    idx=find(...
        abs(evpos(2) - f.Latitude) < latTol &...
        abs(evpos(1) - f.Longitude) < lonTol)
    txt={sprintf('%d volcanic feature(s):',numel(idx))};
    for i=1:numel(idx)
        lattxt=lat_text(event_obj,f,i);
        lontxt=lon_text(event_obj,f,i);

        txt=[txt,f.Names(idx(i)),...
            sprintf('( %s , %s )',lattxt,lontxt),...
            sprintf(' %.2f km elev',-f.Depth(i))];
    end

end

function lattxt=lat_text(event_obj,obj,idx)
    if ~exist('idx','var')
        idx=1;
    end
    evpos=event_obj.Position;
    if evpos(1)>0
        lattxt=[num2str(obj.Latitude(idx)) ' N'];
    else
        lattxt=[num2str(obj.Latitude(idx)) ' S'];
    end
end

function lontxt=lon_text(event_obj,obj,idx)
    if ~exist('idx','var')
        idx=1;
    end
    evpos=event_obj.Position;
    if evpos(1)>0
        lontxt=[num2str(obj.Longitude(idx)) ' E'];
    else
        lontxt=[num2str(obj.Longitude(idx)) ' W'];
    end
end