Contents

function [handles,levels,parentIdx,listing] = findjobj(container,varargin)
%findjobj Find java objects contained within a specified java container or Matlab GUI handle
%
% Syntax:
%    [handles, levels, parentIds, listing] = findjobj(container, 'PropName',PropValue(s), ...)
%
% Input parameters:
%    container - optional handle to java container uipanel or figure. If unsupplied then current figure will be used
%    'PropName',PropValue - optional list of property pairs (case insensitive). PropName may also be named -PropName
%         'position' - filter results based on those elements that contain the specified X,Y position or a java element
%                      Note: specify a Matlab position (X,Y = pixels from bottom left corner), not a java one
%         'size'     - filter results based on those elements that have the specified W,H (in pixels)
%         'class'    - filter results based on those elements that contain the substring  (or java class) PropValue
%                      Note1: filtering is case insensitive and relies on regexp, so you can pass wildcards etc.
%                      Note2: '-class' is an undocumented findobj PropName, but only works on Matlab (not java) classes
%         'property' - filter results based on those elements that possess the specified case-insensitive property string
%                      Note1: passing a property value is possible if the argument following 'property' is a cell in the
%                             format of {'propName','propValue'}. Example: FINDJOBJ(...,'property',{'Text','click me'})
%                      Note2: partial property names (e.g. 'Tex') are accepted, as long as they're not ambiguous
%         'depth'    - filter results based on specified depth. 0=top-level, Inf=all levels (default=Inf)
%         'flat'     - same as specifying: 'depth',0
%         'not'      - negates the following filter: 'not','class','c' returns all elements EXCEPT those with class 'c'
%         'persist'  - persist figure components information, allowing much faster results for subsequent invocations
%         'nomenu'   - skip menu processing, for "lean" list of handles & much faster processing;
%                      This option is the default for HG containers but not for figure, Java or no container
%         'print'    - display all java elements in a hierarchical list, indented appropriately
%                      Note1: optional PropValue of element index or handle to java container
%                      Note2: normally this option would be placed last, after all filtering is complete. Placing this
%                             option before some filters enables debug print-outs of interim filtering results.
%                      Note3: output is to the Matlab command window unless the 'listing' (4th) output arg is requested
%         'list'     - same as 'print'
%         'debug'    - list found component positions in the Command Window
%
% Output parameters:
%    handles   - list of handles to java elements
%    levels    - list of corresponding hierarchy level of the java elements (top=0)
%    parentIds - list of indexes (in unfiltered handles) of the parent container of the corresponding java element
%    listing   - results of 'print'/'list' options (empty if these options were not specified)
%
%    Note: If no output parameter is specified, then an interactive window will be displayed with a
%    ^^^^  tree view of all container components, their properties and callbacks.
%
% Examples:
%    findjobj;                     % display list of all javaelements of currrent figure in an interactive GUI
%    handles = findjobj;           % get list of all java elements of current figure (inc. menus, toolbars etc.)
%    findjobj('print');            % list all java elements in current figure
%    findjobj('print',6);          % list all java elements in current figure, contained within its 6th element
%    handles = findjobj(hButton);                                     % hButton is a matlab button
%    handles = findjobj(gcf,'position',getpixelposition(hButton,1));  % same as above but also return hButton's panel
%    handles = findjobj(hButton,'persist');                           % same as above, persist info for future reuse
%    handles = findjobj('class','pushbutton');                        % get all pushbuttons in current figure
%    handles = findjobj('class','pushbutton','position',123,456);     % get all pushbuttons at the specified position
%    handles = findjobj(gcf,'class','pushbutton','size',23,15);       % get all pushbuttons with the specified size
%    handles = findjobj('property','Text','not','class','button');    % get all non-button elements with 'text' property
%    handles = findjobj('-property',{'Text','click me'});             % get all elements with 'text' property = 'click me'
%
% Sample usage:
%    hButton = uicontrol('string','click me');
%    jButton = findjobj(hButton,'nomenu');
%      % or: jButton = findjobj('property',{'Text','click me'});
%    jButton.setFlyOverAppearance(1);
%    jButton.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR));
%    set(jButton,'FocusGainedCallback',@myMatlabFunction);   % some 30 callback points available...
%    jButton.get;   % list all changeable properties...
%
%    hEditbox = uicontrol('style','edit');
%    jEditbox = findjobj(hEditbox,'nomenu');
%    jEditbox.setCaretColor(java.awt.Color.red);
%    jEditbox.KeyTypedCallback = @myCallbackFunc;  % many more callbacks where this came from...
%    jEdit.requestFocus;
%
% Known issues/limitations:
%    - Cannot currently process multiple container objects - just one at a time
%    - Initial processing is a bit slow when the figure is laden with many UI components (so better use 'persist')
%    - Passing a simple container Matlab handle is currently filtered by its position+size: should find a better way to do this
%    - Matlab uipanels are not implemented as simple java panels, and so they can't be found using this utility
%    - Labels have a write-only text property in java, so they can't be found using the 'property',{'Text','string'} notation
%
% Warning:
%    This code heavily relies on undocumented and unsupported Matlab functionality.
%    It works on Matlab 7+, but use at your own risk!
%
% Bugs and suggestions:
%    Please send to Yair Altman (altmany at gmail dot com)
%
% Change log:
%    2012-07-25: Fixes for R2012b as well as some older Matlab releases
%    2011-12-07: Fixed 'File is empty' messages in compiled apps
%    2011-11-22: Fix suggested by Ward
%    2011-02-01: Fixes for R2011a
%    2010-06-13: Fixes for R2010b; fixed download (m-file => zip-file)
%    2010-04-21: Minor fix to support combo-boxes (aka drop-down, popup-menu) on Windows
%    2010-03-17: Important release: Fixes for R2010a, debug listing, objects not found, component containers that should be ignored etc.
%    2010-02-04: Forced an EDT redraw before processing; warned if requested handle is invisible
%    2010-01-18: Found a way to display label text next to the relevant node name
%    2009-10-28: Fixed uitreenode warning
%    2009-10-27: Fixed auto-collapse of invisible container nodes; added dynamic tree tooltips & context-menu; minor fix to version-check display
%    2009-09-30: Fix for Matlab 7.0 as suggested by Oliver W; minor GUI fix (classname font)
%    2009-08-07: Fixed edge-case of missing JIDE tables
%    2009-05-24: Added support for future Matlab versions that will not support JavaFrame
%    2009-05-15: Added sanity checks for axes items
%    2009-04-28: Added 'debug' input arg; increased size tolerance 1px => 2px
%    2009-04-23: Fixed location of popupmenus (always 20px high despite what's reported by Matlab...); fixed uiinspect processing issues; added blog link; narrower action buttons
%    2009-04-09: Automatic 'nomenu' for uicontrol inputs; significant performance improvement
%    2009-03-31: Fixed position of some Java components; fixed properties tooltip; fixed node visibility indication
%    2009-02-26: Indicated components visibility (& auto-collapse non-visible containers); auto-highlight selected component; fixes in node icons, figure title & tree refresh; improved error handling; display FindJObj version update description if available
%    2009-02-24: Fixed update check; added dedicated labels icon
%    2009-02-18: Fixed compatibility with old Matlab versions
%    2009-02-08: Callbacks table fixes; use uiinspect if available; fix update check according to new FEX website
%    2008-12-17: R2008b compatibility
%    2008-09-10: Fixed minor bug as per Johnny Smith
%    2007-11-14: Fixed edge case problem with class properties tooltip; used existing object icon if available; added checkbox option to hide standard callbacks
%    2007-08-15: Fixed object naming relative property priorities; added sanity check for illegal container arg; enabled desktop (0) container; cleaned up warnings about special class objects
%    2007-08-03: Fixed minor tagging problems with a few Java sub-classes; displayed UIClassID if text/name/tag is unavailable
%    2007-06-15: Fixed problems finding HG components found by J. Wagberg
%    2007-05-22: Added 'nomenu' option for improved performance; fixed 'export handles' bug; fixed handle-finding/display bugs; "cleaner" error handling
%    2007-04-23: HTMLized classname tooltip; returned top-level figure Frame handle for figure container; fixed callbacks table; auto-checked newer version; fixed Matlab 7.2 compatibility issue; added HG objects tree
%    2007-04-19: Fixed edge case of missing figure; displayed tree hierarchy in interactive GUI if no output args; workaround for figure sub-menus invisible unless clicked
%    2007-04-04: Improved performance; returned full listing results in 4th output arg; enabled partial property names & property values; automatically filtered out container panels if children also returned; fixed finding sub-menu items
%    2007-03-20: First version posted on the MathWorks file exchange: <a href="http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=14317">http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=14317</a>
%
% See also:
%    java, handle, findobj, findall, javaGetHandles, uiinspect (on the File Exchange)

% License to use and modify this code is granted freely to all interested, as long as the original author is
% referenced and attributed as such. The original author maintains the right to be solely associated with this work.

% Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com


    % Ensure Java AWT is enabled
    error(javachk('awt'));

    persistent pContainer pHandles pLevels pParentIdx pPositions

    try
        % Initialize
        handles = handle([]);
        levels = [];
        parentIdx = [];
        positions = [];   % Java positions start at the top-left corner
        %sizes = [];
        listing = '';
        hg_levels = [];
        hg_handles = handle([]);  % HG handles are double
        hg_parentIdx = [];
        nomenu = false;
        menuBarFoundFlag = false;

        % Force an EDT redraw before processing, to ensure all uicontrols etc. are rendered
        drawnow;  pause(0.02);

        % Default container is the current figure's root panel
        if nargin
            if isempty(container)  % empty container - bail out
                return;
            elseif ischar(container)  % container skipped - this is part of the args list...
                varargin = {container, varargin{:}};
                origContainer = getCurrentFigure;
                [container,contentSize] = getRootPanel(origContainer);
            elseif isequal(container,0)  % root
                origContainer = handle(container);
                container = com.mathworks.mde.desk.MLDesktop.getInstance.getMainFrame;
                contentSize = [container.getWidth, container.getHeight];
            elseif ishghandle(container) % && ~isa(container,'java.awt.Container')
                container = container(1);  % another current limitation...
                hFig = ancestor(container,'figure');
                origContainer = handle(container);
                if isa(origContainer,'uimenu')
                    % getpixelposition doesn't work for menus... - damn!
                    varargin = {'class','MenuPeer', 'property',{'Label',strrep(get(container,'Label'),'&','')}, varargin{:}};
                elseif ~isa(origContainer, 'figure') && ~isempty(hFig)
                    % See limitations section above: should find a better way to directly refer to the element's java container
                    try
                        % Note: 'PixelBounds' is undocumented and unsupported, but much faster than getpixelposition!
                        % ^^^^  unfortunately, its Y position is inaccurate in some cases - damn!
                        %size = get(container,'PixelBounds');
                        pos = fix(getpixelposition(container,1));
                        %varargin = {'position',pos(1:2), 'size',pos(3:4), 'not','class','java.awt.Panel', varargin{:}};
                    catch
                        try
                            figName = get(hFig,'name');
                            if strcmpi(get(hFig,'number'),'on')
                                figName = regexprep(['Figure ' num2str(hFig) ': ' figName],': $','');
                            end
                            mde = com.mathworks.mde.desk.MLDesktop.getInstance;
                            jFig = mde.getClient(figName);
                            if isempty(jFig), error('dummy');  end
                        catch
                            warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');  % R2008b compatibility
                            jFig = get(get(hFig,'JavaFrame'),'FigurePanelContainer');
                        end
                        pos = [];
                        try
                            pxsize = get(container,'PixelBounds');
                            pos = [pxsize(1)+5, jFig.getHeight - (pxsize(4)-5)];
                        catch
                            % never mind...
                        end
                    end
                    if size(pos,2) == 2
                        pos(:,3:4) = 0;
                    end
                    if ~isempty(pos)
                        if isa(handle(container),'uicontrol') && strcmp(get(container,'style'),'popupmenu')
                            % popupmenus (combo-box dropdowns) are always 20px high
                            pos(2) = pos(2) + pos(4) - 20;
                            pos(4) = 20;
                        end
                        %varargin = {'position',pos(1:2), 'size',size(3:4)-size(1:2)-10, 'not','class','java.awt.Panel', varargin{:}};
                        varargin = {'position',pos(1:2)+[0,pos(4)], 'size',pos(3:4), 'not','class','java.awt.Panel', 'nomenu', varargin{:}};
                    end
                elseif isempty(hFig)
                    hFig = handle(container);
                end
                [container,contentSize] = getRootPanel(hFig);
            elseif isjava(container)
                % Maybe a java container obj (will crash otherwise)
                origContainer = container;
                contentSize = [container.getWidth, container.getHeight];
            else
                error('YMA:findjobj:IllegalContainer','Input arg does not appear to be a valid GUI object');
            end
        else
            % Default container = current figure
            origContainer = getCurrentFigure;
            [container,contentSize] = getRootPanel(origContainer);
        end

        % Check persistency
        if isequal(pContainer,container)
            % persistency requested and the same container is reused, so reuse the hierarchy information
            [handles,levels,parentIdx,positions] = deal(pHandles, pLevels, pParentIdx, pPositions);
        else
            % Pre-allocate space of complex data containers for improved performance
            handles = repmat(handles,1,1000);
            positions = zeros(1000,2);

            % Check whether to skip menu processing
            nomenu = paramSupplied(varargin,'nomenu');

            % Traverse the container hierarchy and extract the elements within
            traverseContainer(container,0,1);

            % Remove unnecessary pre-allocated elements
            dataLen = length(levels);
            handles  (dataLen+1:end) = [];
            positions(dataLen+1:end,:) = [];
        end

        % Process persistency check before any filtering is done
        if paramSupplied(varargin,'persist')
            [pContainer, pHandles, pLevels, pParentIdx, pPositions] = deal(container,handles,levels,parentIdx,positions);
        end

        % Save data for possible future use in presentObjectTree() below
        allHandles = handles;
        allLevels  = levels;
        allParents = parentIdx;
        selectedIdx = 1:length(handles);
        %[positions(:,1)-container.getX, container.getHeight - positions(:,2)]

        % Debug-list all found compponents and their positions
        if paramSupplied(varargin,'debug')
            for handleIdx = 1 : length(allHandles)
                thisObj = handles(handleIdx);
                pos = sprintf('%d,%d %dx%d',[positions(handleIdx,:) getWidth(thisObj) getHeight(thisObj)]);
                disp([repmat(' ',1,levels(handleIdx)) '[' pos '] ' char(toString(thisObj))]);
            end
        end

        % Process optional args
        % Note: positions is NOT returned since it's based on java coord system (origin = top-left): will confuse Matlab users
        processArgs(varargin{:});  %#ok

        % De-cell and trim listing, if only one element was found (no use for indented listing in this case)
        if iscell(listing) && length(listing)==1
            listing = strtrim(listing{1});
        end

        % If no output args and no listing requested, present the FINDJOBJ interactive GUI
        if nargout == 0 && isempty(listing)

            % Get all label positions
            hg_labels = [];
            labelPositions = getLabelsJavaPos(container);

            % Present the GUI (object tree)
            if ~isempty(container)
                presentObjectTree();
            else
                warnInvisible;
            end

        % Display the listing, if this was specifically requested yet no relevant output arg was specified
        elseif nargout < 4 && ~isempty(listing)
            if ~iscell(listing)
                disp(listing);
            else
                for listingIdx = 1 : length(listing)
                    disp(listing{listingIdx});
                end
            end
        end

        % Display a warning if the requested handle was not found because it's invisible
        if nargout && isempty(handles)
            warnInvisible;
        end

        return;  %debug point

    catch
        % 'Cleaner' error handling - strip the stack info etc.
        err = lasterror;  %#ok
        err.message = regexprep(err.message,'Error using ==> [^\n]+\n','');
        if isempty(findstr(mfilename,err.message))
            % Indicate error origin, if not already stated within the error message
            err.message = [mfilename ': ' err.message];
        end
        rethrow(err);
    end

Display a warning if the requested handle was not found because it's invisible

    function warnInvisible
        try
            stk = dbstack;
            stkNames = {stk.name};
            OutputFcnIdx = find(~cellfun(@isempty,regexp(stkNames,'_OutputFcn')));
            if ~isempty(OutputFcnIdx)
                OutputFcnName = stkNames{OutputFcnIdx};
                warning('YMA:FindJObj:OutputFcn',['No Java reference was found for the requested handle, because the figure is still invisible in ' OutputFcnName '()']);
            elseif ishandle(origContainer) && isprop(origContainer,'Visible') && strcmpi(get(origContainer,'Visible'),'off')
                warning('YMA:FindJObj:invisibleHandle','No Java reference was found for the requested handle, probably because it is still invisible');
            end
        catch
            % Never mind...
        end
    end

Check existence of a (case-insensitive) optional parameter in the params list

    function [flag,idx] = paramSupplied(paramsList,paramName)
        %idx = find(~cellfun('isempty',regexpi(paramsList(cellfun(@ischar,paramsList)),['^-?' paramName])));
        idx = find(~cellfun('isempty',regexpi(paramsList(cellfun('isclass',paramsList,'char')),['^-?' paramName])));  % 30/9/2009 fix for ML 7.0 suggested by Oliver W
        flag = any(idx);
    end

Get current figure (even if its 'HandleVisibility' property is 'off')

    function curFig = getCurrentFigure
        oldShowHidden = get(0,'ShowHiddenHandles');
        set(0,'ShowHiddenHandles','on');  % minor fix per Johnny Smith
        curFig = gcf;
        set(0,'ShowHiddenHandles',oldShowHidden);
    end

Get Java reference to top-level (root) panel - actually, a reference to the java figure

    function [jRootPane,contentSize] = getRootPanel(hFig)
        try
            contentSize = [0,0];  % initialize
            jRootPane = hFig;
            figName = get(hFig,'name');
            if strcmpi(get(hFig,'number'),'on')
                figName = regexprep(['Figure ' num2str(hFig) ': ' figName],': $','');
            end
            mde = com.mathworks.mde.desk.MLDesktop.getInstance;
            jFigPanel = mde.getClient(figName);
            jRootPane = jFigPanel;
            jRootPane = jFigPanel.getRootPane;
        catch
            try
                warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');  % R2008b compatibility
                jFrame = get(hFig,'JavaFrame');
                jFigPanel = get(jFrame,'FigurePanelContainer');
                jRootPane = jFigPanel;
                jRootPane = jFigPanel.getComponent(0).getRootPane;
            catch
                % Never mind
            end
        end
        try
            % If invalid RootPane - try another method...
            warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');  % R2008b compatibility
            jFrame = get(hFig,'JavaFrame');
            jAxisComponent = get(jFrame,'AxisComponent');
            jRootPane = jAxisComponent.getParent.getParent.getRootPane;
        catch
            % Never mind
        end
        try
            % If invalid RootPane, retry up to N times
            tries = 10;
            while isempty(jRootPane) && tries>0  % might happen if figure is still undergoing rendering...
                drawnow; pause(0.001);
                tries = tries - 1;
                jRootPane = jFigPanel.getComponent(0).getRootPane;
            end

            % If still invalid, use FigurePanelContainer which is good enough in 99% of cases... (menu/tool bars won't be accessible, though)
            if isempty(jRootPane)
                jRootPane = jFigPanel;
            end
            contentSize = [jRootPane.getWidth, jRootPane.getHeight];

            % Try to get the ancestor FigureFrame
            jRootPane = jRootPane.getTopLevelAncestor;
        catch
            % Never mind - FigurePanelContainer is good enough in 99% of cases... (menu/tool bars won't be accessible, though)
        end
    end

Traverse the container hierarchy and extract the elements within

    function traverseContainer(jcontainer,level,parent)
        persistent figureComponentFound menuRootFound

        % Record the data for this node
        %disp([repmat(' ',1,level) '<= ' char(jcontainer.toString)])
        thisIdx = length(levels) + 1;
        levels(thisIdx) = level;
        parentIdx(thisIdx) = parent;
        handles(thisIdx) = handle(jcontainer,'callbackproperties');
        try
            positions(thisIdx,:) = getXY(jcontainer);
            %sizes(thisIdx,:) = [jcontainer.getWidth, jcontainer.getHeight];
        catch
            positions(thisIdx,:) = [0,0];
            %sizes(thisIdx,:) = [0,0];
        end
        if level>0
            positions(thisIdx,:) = positions(thisIdx,:) + positions(parent,:);
            if ~figureComponentFound && ...
               strcmp(jcontainer.getName,'fComponentContainer') && ...
               isa(jcontainer,'com.mathworks.hg.peer.FigureComponentContainer')   % there are 2 FigureComponentContainers - only process one...

                % restart coordinate system, to exclude menu & toolbar areas
                positions(thisIdx,:) = positions(thisIdx,:) - [jcontainer.getRootPane.getX, jcontainer.getRootPane.getY];
                figureComponentFound = true;
            end
        elseif level==1
            positions(thisIdx,:) = positions(thisIdx,:) + positions(parent,:);
        else
            % level 0 - initialize flags used later
            figureComponentFound = false;
            menuRootFound = false;
        end
        parentId = length(parentIdx);

        % Traverse Menu items, unless the 'nomenu' option was requested
        if ~nomenu
            try
                for child = 1 : getNumMenuComponents(jcontainer)
                    traverseContainer(jcontainer.getMenuComponent(child-1),level+1,parentId);
                end
            catch
                % Probably not a Menu container, but maybe a top-level JMenu, so discard duplicates
                %if isa(handles(end).java,'javax.swing.JMenuBar')
                if ~menuRootFound && strcmp(handles(end).java.class,'javax.swing.JMenuBar')  %faster...
                    if removeDuplicateNode(thisIdx)
                        menuRootFound = true;
                        return;
                    end
                end
            end
        end

        % Now recursively process all this node's children (if any), except menu items if so requested
        %if isa(jcontainer,'java.awt.Container')
        try  % try-catch is faster than checking isa(jcontainer,'java.awt.Container')...
            %if jcontainer.getComponentCount,  jcontainer.getComponents,  end
            if ~nomenu || menuBarFoundFlag || isempty(strfind(jcontainer.class,'FigureMenuBar'))
                lastChildComponent = java.lang.Object;
                child = 0;
                while (child < jcontainer.getComponentCount)
                    childComponent = jcontainer.getComponent(child);
                    % Looping over menus sometimes causes jcontainer to get mixed up (probably a JITC bug), so identify & fix
                    if isequal(childComponent,lastChildComponent)
                        child = child + 1;
                        childComponent = jcontainer.getComponent(child);
                    end
                    lastChildComponent = childComponent;
                    %disp([repmat(' ',1,level) '=> ' num2str(child) ': ' char(childComponent.class)])
                    traverseContainer(childComponent,level+1,parentId);
                    child = child + 1;
                end
            else
                menuBarFoundFlag = true;  % use this flag to skip further testing for FigureMenuBar
            end
        catch
            % do nothing - probably not a container
            %dispError
        end

        % ...and yet another type of child traversal...
        try
            if ~isdeployed  % prevent 'File is empty' messages in compiled apps
                jc = jcontainer.java;
            else
                jc = jcontainer;
            end
            for child = 1 : jc.getChildCount
                traverseContainer(jc.getChildAt(child-1),level+1,parentId);
            end
        catch
            % do nothing - probably not a container
            %dispError
        end

        % TODO: Add axis (plot) component handles
    end

Get the XY location of a Java component

    function xy = getXY(jcontainer)
            % Note: getX/getY are better than get(..,'X') (mem leaks),
            % ^^^^  but sometimes they fail and revert to getx.m ...
            % Note2: try awtinvoke() catch is faster than checking ismethod()...
            % Note3: using AWTUtilities.invokeAndWait() directly is even faster than awtinvoke()...
            try %if ismethod(jcontainer,'getX')
                %positions(thisIdx,:) = [jcontainer.getX, jcontainer.getY];
                cls = getClass(jcontainer);
                location = com.mathworks.jmi.AWTUtilities.invokeAndWait(jcontainer,getMethod(cls,'getLocation',[]),[]);
                x = location.getX;
                y = location.getY;
            catch %else
                try
                    x = com.mathworks.jmi.AWTUtilities.invokeAndWait(jcontainer,getMethod(cls,'getX',[]),[]);
                    y = com.mathworks.jmi.AWTUtilities.invokeAndWait(jcontainer,getMethod(cls,'getY',[]),[]);
                catch
                    try
                        x = awtinvoke(jcontainer,'getX()');
                        y = awtinvoke(jcontainer,'getY()');
                    catch
                        x = get(jcontainer,'X');
                        y = get(jcontainer,'Y');
                    end
                end
            end
            %positions(thisIdx,:) = [x, y];
            xy = [x,y];
    end

Get the number of menu sub-elements

    function numMenuComponents  = getNumMenuComponents(jcontainer)

        % The following line will raise an Exception for anything except menus
        numMenuComponents = jcontainer.getMenuComponentCount;

        % No error so far, so this must be a menu container...
        % Note: Menu subitems are not visible until the top-level (root) menu gets initial focus...
        % Try several alternatives, until we get a non-empty menu (or not...)
        % TODO: Improve performance - this takes WAY too long...
        if jcontainer.isTopLevelMenu && (numMenuComponents==0)
            jcontainer.requestFocus;
            numMenuComponents = jcontainer.getMenuComponentCount;
            if (numMenuComponents == 0)
                drawnow; pause(0.001);
                numMenuComponents = jcontainer.getMenuComponentCount;
                if (numMenuComponents == 0)
                    jcontainer.setSelected(true);
                    numMenuComponents = jcontainer.getMenuComponentCount;
                    if (numMenuComponents == 0)
                        drawnow; pause(0.001);
                        numMenuComponents = jcontainer.getMenuComponentCount;
                        if (numMenuComponents == 0)
                            jcontainer.doClick;  % needed in order to populate the sub-menu components
                            numMenuComponents = jcontainer.getMenuComponentCount;
                            if (numMenuComponents == 0)
                                drawnow; %pause(0.001);
                                numMenuComponents = jcontainer.getMenuComponentCount;
                                jcontainer.doClick;  % close menu by re-clicking...
                                if (numMenuComponents == 0)
                                    drawnow; %pause(0.001);
                                    numMenuComponents = jcontainer.getMenuComponentCount;
                                end
                            else
                                % ok - found sub-items
                                % Note: no need to close menu since this will be done when focus moves to the FindJObj window
                                %jcontainer.doClick;  % close menu by re-clicking...
                            end
                        end
                    end
                    jcontainer.setSelected(false);  % de-select the menu
                end
            end
        end
    end

Remove a specific tree node's data

    function nodeRemovedFlag = removeDuplicateNode(thisIdx)
        nodeRemovedFlag = false;
        for idx = 1 : thisIdx-1
            if isequal(handles(idx),handles(thisIdx))
                levels(thisIdx) = [];
                parentIdx(thisIdx) = [];
                handles(thisIdx) = [];
                positions(thisIdx,:) = [];
                %sizes(thisIdx,:) = [];
                nodeRemovedFlag = true;
                return;
            end
        end
    end

Process optional args

    function processArgs(varargin)

        % Initialize
        invertFlag = false;
        listing = '';

        % Loop over all optional args
        while ~isempty(varargin) && ~isempty(handles)

            % Process the arg (and all its params)
            foundIdx = 1 : length(handles);
            if iscell(varargin{1}),  varargin{1} = varargin{1}{1};  end
            if ~isempty(varargin{1}) && varargin{1}(1)=='-'
                varargin{1}(1) = [];
            end
            switch lower(varargin{1})
                case 'not'
                    invertFlag = true;
                case 'position'
                    [varargin,foundIdx] = processPositionArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case 'size'
                    [varargin,foundIdx] = processSizeArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case 'class'
                    [varargin,foundIdx] = processClassArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case 'property'
                    [varargin,foundIdx] = processPropertyArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case 'depth'
                    [varargin,foundIdx] = processDepthArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case 'flat'
                    varargin = {'depth',0, varargin{min(2:end):end}};
                    [varargin,foundIdx] = processDepthArgs(varargin{:});
                    if invertFlag,  foundIdx = ~foundIdx;  invertFlag = false;  end
                case {'print','list'}
                    [varargin,listing] = processPrintArgs(varargin{:});
                case {'persist','nomenu','debug'}
                    % ignore - already handled in main function above
                otherwise
                    error('YMA:findjobj:IllegalOption',['Option ' num2str(varargin{1}) ' is not a valid option. Type ''help ' mfilename ''' for the full options list.']);
            end

            % If only parent-child pairs found
            foundIdx = find(foundIdx);
            if ~isempty(foundIdx) && isequal(parentIdx(foundIdx(2:2:end)),foundIdx(1:2:end))
                % Return just the children (the parent panels are uninteresting)
                foundIdx(1:2:end) = [];
            end

            % If several possible handles were found and the first is the container of the second
            if length(foundIdx) > 1 && isequal(handles(foundIdx(1)).java, handles(foundIdx(2)).getParent)
                % Discard the uninteresting component container
                foundIdx(1) = [];
            end

            % Filter the results
            selectedIdx = selectedIdx(foundIdx);
            handles   = handles(foundIdx);
            levels    = levels(foundIdx);
            parentIdx = parentIdx(foundIdx);
            positions = positions(foundIdx,:);

            % Remove this arg and proceed to the next one
            varargin(1) = [];

        end  % Loop over all args
    end

Process 'print' option

    function [varargin,listing] = processPrintArgs(varargin)
        if length(varargin)<2 || ischar(varargin{2})
            % No second arg given, so use the first available element
            listingContainer = handles(1);  %#ok - used in evalc below
        else
            % Get the element to print from the specified second arg
            if isnumeric(varargin{2}) && (varargin{2} == fix(varargin{2}))  % isinteger doesn't work on doubles...
                if (varargin{2} > 0) && (varargin{2} <= length(handles))
                    listingContainer = handles(varargin{2});  %#ok - used in evalc below
                elseif varargin{2} > 0
                    error('YMA:findjobj:IllegalPrintFilter','Print filter index %g > number of available elements (%d)',varargin{2},length(handles));
                else
                    error('YMA:findjobj:IllegalPrintFilter','Print filter must be a java handle or positive numeric index into handles');
                end
            elseif ismethod(varargin{2},'list')
                listingContainer = varargin{2};  %#ok - used in evalc below
            else
                error('YMA:findjobj:IllegalPrintFilter','Print filter must be a java handle or numeric index into handles');
            end
            varargin(2) = [];
        end

        % use evalc() to capture output into a Matlab variable
        %listing = evalc('listingContainer.list');

        % Better solution: loop over all handles and process them one by one
        listing = cell(length(handles),1);
        for componentIdx = 1 : length(handles)
            listing{componentIdx} = [repmat(' ',1,levels(componentIdx)) char(handles(componentIdx).toString)];
        end
    end

Process 'position' option

    function [varargin,foundIdx] = processPositionArgs(varargin)
        if length(varargin)>1
            positionFilter = varargin{2};
            %if (isjava(positionFilter) || iscom(positionFilter)) && ismethod(positionFilter,'getLocation')
            try  % try-catch is faster...
                % Java/COM object passed - get its position
                positionFilter = positionFilter.getLocation;
                filterXY = [positionFilter.getX, positionFilter.getY];
            catch
                if ~isscalar(positionFilter)
                    % position vector passed
                    if (length(positionFilter)>=2) && isnumeric(positionFilter)
                        % Remember that java coordinates start at top-left corner, Matlab coords start at bottom left...
                        %positionFilter = java.awt.Point(positionFilter(1), container.getHeight - positionFilter(2));
                        filterXY = [container.getX + positionFilter(1), container.getY + contentSize(2) - positionFilter(2)];

                        % Check for full Matlab position vector (x,y,w,h)
                        %if (length(positionFilter)==4)
                        %    varargin{end+1} = 'size';
                        %    varargin{end+1} = fix(positionFilter(3:4));
                        %end
                    else
                        error('YMA:findjobj:IllegalPositionFilter','Position filter must be a java UI component, or X,Y pair');
                    end
                elseif length(varargin)>2
                    % x,y passed as separate arg values
                    if isnumeric(positionFilter) && isnumeric(varargin{3})
                        % Remember that java coordinates start at top-left corner, Matlab coords start at bottom left...
                        %positionFilter = java.awt.Point(positionFilter, container.getHeight - varargin{3});
                        filterXY = [container.getX + positionFilter, container.getY + contentSize(2) - varargin{3}];
                        varargin(3) = [];
                    else
                        error('YMA:findjobj:IllegalPositionFilter','Position filter must be a java UI component, or X,Y pair');
                    end
                else
                    error('YMA:findjobj:IllegalPositionFilter','Position filter must be a java UI component, or X,Y pair');
                end
            end

            % Compute the required element positions in order to be eligible for a more detailed examination
            % Note: based on the following constraints: 0 <= abs(elementX-filterX) + abs(elementY+elementH-filterY) < 7
            baseDeltas = [positions(:,1)-filterXY(1), positions(:,2)-filterXY(2)];  % faster than repmat()...
            %baseHeight = - baseDeltas(:,2);% -abs(baseDeltas(:,1));
            %minHeight = baseHeight - 7;
            %maxHeight = baseHeight + 7;
            %foundIdx = ~arrayfun(@(b)(invoke(b,'contains',positionFilter)),handles);  % ARGH! - disallowed by Matlab!
            %foundIdx = repmat(false,1,length(handles));
            %foundIdx(length(handles)) = false;  % faster than repmat()...
            foundIdx = (abs(baseDeltas(:,1)) < 7) & (abs(baseDeltas(:,2)) < 7); % & (minHeight >= 0);
            %fi = find(foundIdx);
            %for componentIdx = 1 : length(fi)
                %foundIdx(componentIdx) = handles(componentIdx).getBounds.contains(positionFilter);

                % Search for a point no farther than 7 pixels away (prevents rounding errors)
                %foundIdx(componentIdx) = handles(componentIdx).getLocationOnScreen.distanceSq(positionFilter) < 50;  % fails for invisible components...

                %p = java.awt.Point(positions(componentIdx,1), positions(componentIdx,2) + handles(componentIdx).getHeight);
                %foundIdx(componentIdx) = p.distanceSq(positionFilter) < 50;

                %foundIdx(componentIdx) = sum(([baseDeltas(componentIdx,1),baseDeltas(componentIdx,2)+handles(componentIdx).getHeight]).^2) < 50;

                % Following is the fastest method found to date: only eligible elements are checked in detailed
            %    elementHeight = handles(fi(componentIdx)).getHeight;
            %    foundIdx(fi(componentIdx)) = elementHeight > minHeight(fi(componentIdx)) && ...
            %                                 elementHeight < maxHeight(fi(componentIdx));
                %disp([componentIdx,elementHeight,minHeight(fi(componentIdx)),maxHeight(fi(componentIdx)),foundIdx(fi(componentIdx))])
            %end

            varargin(2) = [];
        else
            foundIdx = [];
        end
    end

Process 'size' option

    function [varargin,foundIdx] = processSizeArgs(varargin)
        if length(varargin)>1
            sizeFilter = lower(varargin{2});
            %if (isjava(sizeFilter) || iscom(sizeFilter)) && ismethod(sizeFilter,'getSize')
            try  % try-catch is faster...
                % Java/COM object passed - get its size
                sizeFilter = sizeFilter.getSize;
                filterWidth  = sizeFilter.getWidth;
                filterHeight = sizeFilter.getHeight;
            catch
                if ~isscalar(sizeFilter)
                    % size vector passed
                    if (length(sizeFilter)>=2) && isnumeric(sizeFilter)
                        %sizeFilter = java.awt.Dimension(sizeFilter(1),sizeFilter(2));
                        filterWidth  = sizeFilter(1);
                        filterHeight = sizeFilter(2);
                    else
                        error('YMA:findjobj:IllegalSizeFilter','Size filter must be a java UI component, or W,H pair');
                    end
                elseif length(varargin)>2
                    % w,h passed as separate arg values
                    if isnumeric(sizeFilter) && isnumeric(varargin{3})
                        %sizeFilter = java.awt.Dimension(sizeFilter,varargin{3});
                        filterWidth  = sizeFilter;
                        filterHeight = varargin{3};
                        varargin(3) = [];
                    else
                        error('YMA:findjobj:IllegalSizeFilter','Size filter must be a java UI component, or W,H pair');
                    end
                else
                    error('YMA:findjobj:IllegalSizeFilter','Size filter must be a java UI component, or W,H pair');
                end
            end
            %foundIdx = ~arrayfun(@(b)(invoke(b,'contains',sizeFilter)),handles);  % ARGH! - disallowed by Matlab!
            foundIdx(length(handles)) = false;  % faster than repmat()...
            for componentIdx = 1 : length(handles)
                %foundIdx(componentIdx) = handles(componentIdx).getSize.equals(sizeFilter);
                % Allow a 2-pixel tollerance to account for non-integer pixel sizes
                foundIdx(componentIdx) = abs(handles(componentIdx).getWidth  - filterWidth)  <= 3 && ...  % faster than getSize.equals()
                                         abs(handles(componentIdx).getHeight - filterHeight) <= 3;
            end
            varargin(2) = [];
        else
            foundIdx = [];
        end
    end

Process 'class' option

    function [varargin,foundIdx] = processClassArgs(varargin)
        if length(varargin)>1
            classFilter = varargin{2};
            %if ismethod(classFilter,'getClass')
            try  % try-catch is faster...
                classFilter = char(classFilter.getClass);
            catch
                if ~ischar(classFilter)
                    error('YMA:findjobj:IllegalClassFilter','Class filter must be a java object, class or string');
                end
            end

            % Now convert all java classes to java.lang.Strings and compare to the requested filter string
            try
                foundIdx(length(handles)) = false;  % faster than repmat()...
                jClassFilter = java.lang.String(classFilter).toLowerCase;
                for componentIdx = 1 : length(handles)
                    % Note: JVM 1.5's String.contains() appears slightly slower and is available only since Matlab 7.2
                    foundIdx(componentIdx) = handles(componentIdx).getClass.toString.toLowerCase.indexOf(jClassFilter) >= 0;
                end
            catch
                % Simple processing: slower since it does extra processing within opaque.char()
                for componentIdx = 1 : length(handles)
                    % Note: using @toChar is faster but returns java String, not a Matlab char
                    foundIdx(componentIdx) = ~isempty(regexpi(char(handles(componentIdx).getClass),classFilter));
                end
            end

            varargin(2) = [];
        else
            foundIdx = [];
        end
    end

Process 'property' option

    function [varargin,foundIdx] = processPropertyArgs(varargin)
        if length(varargin)>1
            propertyName = varargin{2};
            if iscell(propertyName)
                if length(propertyName) == 2
                    propertyVal  = propertyName{2};
                    propertyName = propertyName{1};
                elseif length(propertyName) == 1
                    propertyName = propertyName{1};
                else
                    error('YMA:findjobj:IllegalPropertyFilter','Property filter must be a string (case insensitive name of property) or cell array {propName,propValue}');
                end
            end
            if ~ischar(propertyName)
                error('YMA:findjobj:IllegalPropertyFilter','Property filter must be a string (case insensitive name of property) or cell array {propName,propValue}');
            end
            propertyName = lower(propertyName);
            %foundIdx = arrayfun(@(h)isprop(h,propertyName),handles);  % ARGH! - disallowed by Matlab!
            foundIdx(length(handles)) = false;  % faster than repmat()...

            % Split processing depending on whether a specific property value was requested (ugly but faster...)
            if exist('propertyVal','var')
                for componentIdx = 1 : length(handles)
                    try
                        % Find out whether this element has the specified property
                        % Note: findprop() and its return value schema.prop are undocumented and unsupported!
                        prop = findprop(handles(componentIdx),propertyName);  % faster than isprop() & enables partial property names

                        % If found, compare it to the actual element's property value
                        foundIdx(componentIdx) = ~isempty(prop) && isequal(get(handles(componentIdx),prop.Name),propertyVal);
                    catch
                        % Some Java classes have a write-only property (like LabelPeer with 'Text'), so we end up here
                        % In these cases, simply assume that the property value doesn't match and continue
                        foundIdx(componentIdx) = false;
                    end
                end
            else
                for componentIdx = 1 : length(handles)
                    try
                        % Find out whether this element has the specified property
                        % Note: findprop() and its return value schema.prop are undocumented and unsupported!
                        foundIdx(componentIdx) = ~isempty(findprop(handles(componentIdx),propertyName));
                    catch
                        foundIdx(componentIdx) = false;
                    end
                end
            end
            varargin(2) = [];
        else
            foundIdx = [];
        end
    end

Process 'depth' option

    function [varargin,foundIdx] = processDepthArgs(varargin)
        if length(varargin)>1
            level = varargin{2};
            if ~isnumeric(level)
                error('YMA:findjobj:IllegalDepthFilter','Depth filter must be a number (=maximal element depth)');
            end
            foundIdx = (levels <= level);
            varargin(2) = [];
        else
            foundIdx = [];
        end
    end

Convert property data into a string

    function data = charizeData(data)
        if isa(data,'com.mathworks.hg.types.HGCallback')
            data = get(data,'Callback');
        end
        if ~ischar(data)
            newData = strtrim(evalc('disp(data)'));
            try
                newData = regexprep(newData,'  +',' ');
                newData = regexprep(newData,'Columns \d+ through \d+\s','');
                newData = regexprep(newData,'Column \d+\s','');
            catch
                %never mind...
            end
            if iscell(data)
                newData = ['{ ' newData ' }'];
            elseif isempty(data)
                newData = '';
            elseif isnumeric(data) || islogical(data) || any(ishandle(data)) || numel(data) > 1 %&& ~isscalar(data)
                newData = ['[' newData ']'];
            end
            data = newData;
        elseif ~isempty(data)
            data = ['''' data ''''];
        end
    end  % charizeData

Get callbacks table data

    function [cbData, cbHeaders, cbTableEnabled] = getCbsData(obj, stripStdCbsFlag)
        classHdl = classhandle(handle(obj));
        cbNames = get(classHdl.Events,'Name');
        if ~isempty(cbNames) && ~iscom(obj)  %only java-based please...
            cbNames = strcat(cbNames,'Callback');
        end
        propNames = get(classHdl.Properties,'Name');
        propCbIdx = [];
        if ~isempty(propNames)
            propCbIdx = find(~cellfun(@isempty,regexp(propNames,'(Fcn|Callback)$')));
            cbNames = unique([cbNames; propNames(propCbIdx)]);  %#ok logical is faster but less debuggable...
        end
        if ~isempty(cbNames)
            if stripStdCbsFlag
                cbNames = stripStdCbs(cbNames);
            end
            if iscell(cbNames)
                cbNames = sort(cbNames);
            end
            hgHandleFlag = 0;  try hgHandleFlag = ishghandle(obj); catch, end  %#ok
            try
                obj = handle(obj,'CallbackProperties');
            catch
                hgHandleFlag = 1;
            end
            if hgHandleFlag
                % HG handles don't allow CallbackProperties - search only for *Fcn
                cbNames = propNames(propCbIdx);
            end
            if iscom(obj)
                cbs = obj.eventlisteners;
                if ~isempty(cbs)
                    cbNamesRegistered = cbs(:,1);
                    cbData = setdiff(cbNames,cbNamesRegistered);
                    %cbData = charizeData(cbData);
                    if size(cbData,2) > size(cbData(1))
                        cbData = cbData';
                    end
                    cbData = [cbData, cellstr(repmat(' ',length(cbData),1))];
                    cbData = [cbData; cbs];
                    [sortedNames, sortedIdx] = sort(cbData(:,1));
                    sortedCbs = cellfun(@charizeData,cbData(sortedIdx,2),'un',0);
                    cbData = [sortedNames, sortedCbs];
                else
                    cbData = [cbNames, cellstr(repmat(' ',length(cbNames),1))];
                end
            elseif iscell(cbNames)
                cbNames = sort(cbNames);
                %cbData = [cbNames, get(obj,cbNames)'];
                cbData = cbNames;
                for idx = 1 : length(cbNames)
                    try
                        cbData{idx,2} = charizeData(get(obj,cbNames{idx}));
                    catch
                        cbData{idx,2} = '(callback value inaccessible)';
                    end
                end
            else  % only one event callback
                %cbData = {cbNames, get(obj,cbNames)'};
                %cbData{1,2} = charizeData(cbData{1,2});
                try
                    cbData = {cbNames, charizeData(get(obj,cbNames))};
                catch
                    cbData = {cbNames, '(callback value inaccessible)'};
                end
            end
            cbHeaders = {'Callback name','Callback value'};
            cbTableEnabled = true;
        else
            cbData = {'(no callbacks)'};
            cbHeaders = {'Callback name'};
            cbTableEnabled = false;
        end
    end  % getCbsData

Get relative (0.0-1.0) divider location

    function divLocation = getRalativeDivlocation(jDiv)
        divLocation = jDiv.getDividerLocation;
        if divLocation > 1  % i.e. [pixels]
            visibleRect = jDiv.getVisibleRect;
            if jDiv.getOrientation == 0  % vertical
                start = visibleRect.getY;
                extent = visibleRect.getHeight - start;
            else
                start = visibleRect.getX;
                extent = visibleRect.getWidth - start;
            end
            divLocation = (divLocation - start) / extent;
        end
    end  % getRalativeDivlocation

Try to set a treenode icon based on a container's icon

    function setTreeNodeIcon(treenode,container)
        try
            iconImage = [];
            iconImage = container.getIcon;
            if ~isempty(findprop(handle(iconImage),'Image'))  % get(iconImage,'Image') is easier but leaks memory...
                iconImage = iconImage.getImage;
            else
                a=b; %#ok cause an error
            end
        catch
            try
                iconImage = container.getIconImage;
            catch
                try
                    if ~isempty(iconImage)
                        ge = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment;
                        gd = ge.getDefaultScreenDevice;
                        gc = gd.getDefaultConfiguration;
                        image = gc.createCompatibleImage(iconImage.getIconWidth, iconImage.getIconHeight);  % a BufferedImage object
                        g = image.createGraphics;
                        iconImage.paintIcon([], g, 0, 0);
                        g.dispose;
                        iconImage = image;
                    end
                catch
                    % never mind...
                end
            end
        end
        if ~isempty(iconImage)
            iconImage = setIconSize(iconImage);
            treenode.setIcon(iconImage);
        end
    end  % setTreeNodeIcon

Present the object hierarchy tree

    function presentObjectTree()
        import java.awt.*
        import javax.swing.*
        hTreeFig = findall(0,'tag','findjobjFig');
        iconpath = [matlabroot, '/toolbox/matlab/icons/'];
        cbHideStd = 0;  % Initial state of the cbHideStdCbs checkbox
        if isempty(hTreeFig)
            % Prepare the figure
            hTreeFig = figure('tag','findjobjFig','menuBar','none','toolBar','none','Name','FindJObj','NumberTitle','off','handleVisibility','off','IntegerHandle','off');
            figIcon = ImageIcon([iconpath 'tool_legend.gif']);
            drawnow;
            try
                mde = com.mathworks.mde.desk.MLDesktop.getInstance;
                jTreeFig = mde.getClient('FindJObj').getTopLevelAncestor;
                jTreeFig.setIcon(figIcon);
            catch
                warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');  % R2008b compatibility
                jTreeFig = get(hTreeFig,'JavaFrame');
                jTreeFig.setFigureIcon(figIcon);
            end
            vsplitPaneLocation = 0.8;
            hsplitPaneLocation = 0.5;
        else
            % Remember cbHideStdCbs checkbox & dividers state for later
            userdata = get(hTreeFig, 'userdata');
            try cbHideStd = userdata.cbHideStdCbs.isSelected; catch, end  %#ok
            try
                vsplitPaneLocation = getRalativeDivlocation(userdata.vsplitPane);
                hsplitPaneLocation = getRalativeDivlocation(userdata.hsplitPane);
            catch
                vsplitPaneLocation = 0.8;
                hsplitPaneLocation = 0.5;
            end

            % Clear the figure and redraw
            clf(hTreeFig);
            figure(hTreeFig);   % bring to front
        end

        % Traverse all HG children, if root container was a HG handle
        if ishghandle(origContainer) %&& ~isequal(origContainer,container)
            traverseHGContainer(origContainer,0,0);
        end

        % Prepare the tree pane
        warning('off','MATLAB:uitreenode:MigratingFunction');  % R2008b compatibility
        warning('off','MATLAB:uitreenode:DeprecatedFunction'); % R2008a compatibility
        tree_h = com.mathworks.hg.peer.UITreePeer;
        hasChildren = sum(allParents==1) > 1;
        icon = [iconpath 'upfolder.gif'];
        [rootName, rootTitle] = getNodeName(container);
        try
            root = uitreenode('v0', handle(container), rootName, icon, ~hasChildren);
        catch  % old matlab version don't have the 'v0' option
            root = uitreenode(handle(container), rootName, icon, ~hasChildren);
        end
        setTreeNodeIcon(root,container);  % constructor must accept a char icon unfortunately, so need to do this afterwards...
        if ~isempty(rootTitle)
            set(hTreeFig, 'Name',['FindJObj - ' char(rootTitle)]);
        end
        nodedata.idx = 1;
        nodedata.obj = container;
        set(root,'userdata',nodedata);
        root.setUserObject(container);
        setappdata(root,'childHandle',container);
        tree_h.setRoot(root);
        treePane = tree_h.getScrollPane;
        treePane.setMinimumSize(Dimension(50,50));
        jTreeObj = treePane.getViewport.getComponent(0);
        jTreeObj.setShowsRootHandles(true)
        jTreeObj.getSelectionModel.setSelectionMode(javax.swing.tree.TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
        %jTreeObj.setVisible(0);
        %jTreeObj.getCellRenderer.setLeafIcon([]);
        %jTreeObj.getCellRenderer.setOpenIcon(figIcon);
        %jTreeObj.getCellRenderer.setClosedIcon([]);
        treePanel = JPanel(BorderLayout);
        treePanel.add(treePane, BorderLayout.CENTER);
        progressBar = JProgressBar(0);
        progressBar.setMaximum(length(allHandles) + length(hg_handles));  % = # of all nodes
        treePanel.add(progressBar, BorderLayout.SOUTH);

        % Prepare the image pane
%disable for now, until we get it working...
%{
        try
            hFig = ancestor(origContainer,'figure');
            [cdata, cm] = getframe(hFig);  %#ok cm unused
            tempfname = [tempname '.png'];
            imwrite(cdata,tempfname);  % don't know how to pass directly to BufferedImage, so use disk...
            jImg = javax.imageio.ImageIO.read(java.io.File(tempfname));
            try delete(tempfname);  catch  end
            imgPanel = JPanel();
            leftPanel = JSplitPane(JSplitPane.VERTICAL_SPLIT, treePanel, imgPanel);
            leftPanel.setOneTouchExpandable(true);
            leftPanel.setContinuousLayout(true);
            leftPanel.setResizeWeight(0.8);
        catch
            leftPanel = treePanel;
        end
%}
        leftPanel = treePanel;

        % Prepare the inspector pane
        classNameLabel = JLabel(['      ' char(container.class)]);
        classNameLabel.setForeground(Color.blue);
        updateNodeTooltip(container, classNameLabel);
        inspectorPanel = JPanel(BorderLayout);
        inspectorPanel.add(classNameLabel, BorderLayout.NORTH);
        % TODO: Maybe uncomment the following when we add the HG tree - in the meantime it's unused (java properties are un-groupable)
        %objReg = com.mathworks.services.ObjectRegistry.getLayoutRegistry;
        %toolBar = awtinvoke('com.mathworks.mlwidgets.inspector.PropertyView$ToolBarStyle','valueOf(Ljava.lang.String;)','GROUPTOOLBAR');
        %inspectorPane = com.mathworks.mlwidgets.inspector.PropertyView(objReg, toolBar);
        inspectorPane = com.mathworks.mlwidgets.inspector.PropertyView;
        identifiers = disableDbstopError;  %#ok "dbstop if error" causes inspect.m to croak due to a bug - so workaround
        inspectorPane.setObject(container);
        inspectorPane.setAutoUpdate(true);
        % TODO: Add property listeners
        % TODO: Display additional props
        inspectorTable = inspectorPane;
        try
            while ~isa(inspectorTable,'javax.swing.JTable')
                inspectorTable = inspectorTable.getComponent(0);
            end
        catch
            % R2010a
            inspectorTable = inspectorPane.getComponent(0).getScrollPane.getViewport.getComponent(0);
        end
        toolTipText = 'hover mouse over the red classname above to see the full list of properties';
        inspectorTable.setToolTipText(toolTipText);
        jideTableUtils = [];
        try
            % Try JIDE features - see http://www.jidesoft.com/products/JIDE_Grids_Developer_Guide.pdf
            com.mathworks.mwswing.MJUtilities.initJIDE;
            jideTableUtils = eval('com.jidesoft.grid.TableUtils;');  % prevent JIDE alert by run-time (not load-time) evaluation
            jideTableUtils.autoResizeAllColumns(inspectorTable);
            inspectorTable.setRowAutoResizes(true);
            inspectorTable.getModel.setShowExpert(1);
        catch
            % JIDE is probably unavailable - never mind...
        end
        inspectorPanel.add(inspectorPane, BorderLayout.CENTER);
        % TODO: Add data update listeners

        % Prepare the callbacks pane
        callbacksPanel = JPanel(BorderLayout);
        classHdl = classhandle(handle(container));
        eventNames = get(classHdl.Events,'Name');
        if ~isempty(eventNames)
            cbNames = sort(strcat(eventNames,'Callback'));
            try
                cbData = [cbNames, get(container,cbNames)'];
            catch
                % R2010a
                cbData = cbNames;
                if isempty(cbData)
                    cbData = {};
                elseif ~iscell(cbData)
                    cbData = {cbData};
                end
                for idx = 1 : length(cbNames)
                    cbData{idx,2} = get(container,cbNames{idx});
                end
            end
            cbTableEnabled = true;
        else
            cbData = {'(no callbacks)',''};
            cbTableEnabled = false;
        end
        cbHeaders = {'Callback name','Callback value'};
        try
            % Use JideTable if available on this system
            %callbacksTableModel = javax.swing.table.DefaultTableModel(cbData,cbHeaders);  %#ok
            %callbacksTable = eval('com.jidesoft.grid.PropertyTable(callbacksTableModel);');  % prevent JIDE alert by run-time (not load-time) evaluation
            callbacksTable = eval('com.jidesoft.grid.TreeTable(cbData,cbHeaders);');  % prevent JIDE alert by run-time (not load-time) evaluation
            callbacksTable.setRowAutoResizes(true);
            callbacksTable.setColumnAutoResizable(true);
            callbacksTable.setColumnResizable(true);
            jideTableUtils.autoResizeAllColumns(callbacksTable);
            callbacksTable.setTableHeader([]);  % hide the column headers since now we can resize columns with the gridline
            callbacksLabel = JLabel(' Callbacks:');  % The column headers are replaced with a header label
            %callbacksPanel.add(callbacksLabel, BorderLayout.NORTH);

            % Add checkbox to show/hide standard callbacks
            callbacksTopPanel = JPanel;
            callbacksTopPanel.setLayout(BoxLayout(callbacksTopPanel, BoxLayout.LINE_AXIS));
            callbacksTopPanel.add(callbacksLabel);
            callbacksTopPanel.add(Box.createHorizontalGlue);
            jcb = JCheckBox('Hide standard callbacks', cbHideStd);
            set(handle(jcb,'CallbackProperties'), 'ActionPerformedCallback',{@cbHideStdCbs_Callback,callbacksTable});
            try
                set(jcb, 'userdata',callbacksTable, 'tooltip','Hide standard Swing callbacks - only component-specific callbacks will be displayed');
            catch
                jcb.setToolTipText('Hide standard Swing callbacks - only component-specific callbacks will be displayed');
                %setappdata(jcb,'userdata',callbacksTable);
            end
            callbacksTopPanel.add(jcb);
            callbacksPanel.add(callbacksTopPanel, BorderLayout.NORTH);
        catch
            % Otherwise, use a standard Swing JTable (keep the headers to enable resizing)
            callbacksTable = JTable(cbData,cbHeaders);
        end
        cbToolTipText = 'Callbacks may be ''strings'', @funcHandle or {@funcHandle,arg1,...}';
        callbacksTable.setToolTipText(cbToolTipText);
        callbacksTable.setGridColor(inspectorTable.getGridColor);
        cbNameTextField = JTextField;
        cbNameTextField.setEditable(false);  % ensure that the callback names are not modified...
        cbNameCellEditor = DefaultCellEditor(cbNameTextField);
        cbNameCellEditor.setClickCountToStart(intmax);  % i.e, never enter edit mode...
        callbacksTable.getColumnModel.getColumn(0).setCellEditor(cbNameCellEditor);
        if ~cbTableEnabled
            callbacksTable.getColumnModel.getColumn(1).setCellEditor(cbNameCellEditor);
        end
        hModel = callbacksTable.getModel;
        set(handle(hModel,'CallbackProperties'), 'TableChangedCallback',{@tbCallbacksChanged,container});
        %set(hModel, 'UserData',container);
        cbScrollPane = JScrollPane(callbacksTable);
        cbScrollPane.setVerticalScrollBarPolicy(cbScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        callbacksPanel.add(cbScrollPane, BorderLayout.CENTER);
        callbacksPanel.setToolTipText(cbToolTipText);

        % Prepare the top-bottom JSplitPanes
        vsplitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, inspectorPanel, callbacksPanel);
        vsplitPane.setOneTouchExpandable(true);
        vsplitPane.setContinuousLayout(true);
        vsplitPane.setResizeWeight(0.8);

        % Prepare the left-right JSplitPane
        hsplitPane = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, vsplitPane);
        hsplitPane.setOneTouchExpandable(true);
        hsplitPane.setContinuousLayout(true);
        hsplitPane.setResizeWeight(0.6);
        pos = getpixelposition(hTreeFig);

        % Prepare the bottom pane with all buttons
        lowerPanel = JPanel(FlowLayout);
        blogUrlLabel = '<a href="http://UndocumentedMatlab.com">Undocumented<br>Matlab.com</a>';
        jWebsite = createJButton(blogUrlLabel, @btWebsite_Callback, 'Visit the UndocumentedMatlab.com blog');
        jWebsite.setContentAreaFilled(0);
        lowerPanel.add(jWebsite);
        lowerPanel.add(createJButton('Refresh<br>tree',        {@btRefresh_Callback, origContainer, hTreeFig}, 'Rescan the component tree, from the root down'));
        lowerPanel.add(createJButton('Export to<br>workspace', {@btExport_Callback,  jTreeObj, classNameLabel}, 'Export the selected component handles to workspace variable findjobj_hdls'));
        lowerPanel.add(createJButton('Request<br>focus',       {@btFocus_Callback,   jTreeObj, root}, 'Set the focus on the first selected component'));
        lowerPanel.add(createJButton('Inspect<br>object',      {@btInspect_Callback, jTreeObj, root}, 'View the signature of all methods supported by the first selected component'));
        lowerPanel.add(createJButton('Check for<br>updates',   {@btCheckFex_Callback}, 'Check the MathWorks FileExchange for the latest version of FindJObj'));

        % Display everything on-screen
        globalPanel = JPanel(BorderLayout);
        globalPanel.add(hsplitPane, BorderLayout.CENTER);
        globalPanel.add(lowerPanel, BorderLayout.SOUTH);
        [obj, hcontainer] = javacomponent(globalPanel, [0,0,pos(3:4)], hTreeFig);
        set(hcontainer,'units','normalized');
        drawnow;
        hsplitPane.setDividerLocation(hsplitPaneLocation);  % this only works after the JSplitPane is displayed...
        vsplitPane.setDividerLocation(vsplitPaneLocation);  % this only works after the JSplitPane is displayed...
        %restoreDbstopError(identifiers);

        % Refresh & resize the screenshot thumbnail
%disable for now, until we get it working...
%{
        try
            hAx = axes('Parent',hTreeFig, 'units','pixels', 'position',[10,10,250,150], 'visible','off');
            axis(hAx,'image');
            image(cdata,'Parent',hAx);
            axis(hAx,'off');
            set(hAx,'UserData',cdata);
            set(imgPanel, 'ComponentResizedCallback',{@resizeImg, hAx}, 'UserData',lowerPanel);
            imgPanel.getGraphics.drawImage(jImg, 0, 0, []);
        catch
            % Never mind...
        end
%}
        % If all handles were selected (i.e., none were filtered) then only select the first
        if (length(selectedIdx) == length(allHandles)) && ~isempty(selectedIdx)
            selectedIdx = 1;
        end

        % Store handles for callback use
        userdata.handles = allHandles;
        userdata.levels  = allLevels;
        userdata.parents = allParents;
        userdata.hg_handles = hg_handles;
        userdata.hg_levels  = hg_levels;
        userdata.hg_parents = hg_parentIdx;
        userdata.initialIdx = selectedIdx;
        userdata.userSelected = false;  % Indicates the user has modified the initial selections
        userdata.inInit = true;
        userdata.jTree = jTreeObj;
        userdata.jTreePeer = tree_h;
        userdata.vsplitPane = vsplitPane;
        userdata.hsplitPane = hsplitPane;
        userdata.classNameLabel = classNameLabel;
        userdata.inspectorPane = inspectorPane;
        userdata.callbacksTable = callbacksTable;
        userdata.jideTableUtils = jideTableUtils;
        try
            userdata.cbHideStdCbs = jcb;
        catch
            userdata.cbHideStdCbs = [];
        end

        % Update userdata for use in callbacks
        try
            set(tree_h,'userdata',userdata);
        catch
            setappdata(tree_h,'userdata',userdata);
        end
        try
            set(callbacksTable,'userdata',userdata);
        catch
            setappdata(callbacksTable,'userdata',userdata);
        end
        set(hTreeFig,'userdata',userdata);

        % Select the root node if requested
        % Note: we must do so here since all other nodes except the root are processed by expandNode
        if any(selectedIdx==1)
            tree_h.setSelectedNode(root);
        end

        % Set the initial cbHideStdCbs state
        try
            if jcb.isSelected
                drawnow;
                evd.getSource.isSelected = jcb.isSelected;
                cbHideStdCbs_Callback(jcb,evd,callbacksTable);
            end
        catch
            % never mind...
        end

        % Set the callback functions
        tree_hh = handle(tree_h,'CallbackProperties');
        set(tree_hh, 'NodeExpandedCallback', {@nodeExpanded, tree_h});
        set(tree_hh, 'NodeSelectedCallback', {@nodeSelected, tree_h});

        % Set the tree mouse-click callback
        % Note: default actions (expand/collapse) will still be performed?
        % Note: MousePressedCallback is better than MouseClickedCallback
        %       since it fires immediately when mouse button is pressed,
        %       without waiting for its release, as MouseClickedCallback does
        handleTree = tree_h.getScrollPane;
        jTreeObj = handleTree.getViewport.getComponent(0);
        jTreeObjh = handle(jTreeObj,'CallbackProperties');
        set(jTreeObjh, 'MousePressedCallback', {@treeMousePressedCallback,tree_h});  % context (right-click) menu
        set(jTreeObjh, 'MouseMovedCallback',   @treeMouseMovedCallback);    % mouse hover tooltips

        % Pre-expand all rows
        expandNode(progressBar, jTreeObj, tree_h, root, 0);
        %jTreeObj.setVisible(1);

        % Hide the progressbar now that we've finished expanding all rows
        try
            hsplitPane.getLeftComponent.setTopComponent(treePane);
        catch
            % Probably not a vSplitPane on the left...
            hsplitPane.setLeftComponent(treePane);
        end
        hsplitPane.setDividerLocation(hsplitPaneLocation);  % need to do it again...

        % Update userdata
        userdata.inInit = false;
        try
            set(tree_h,'userdata',userdata);
        catch
            setappdata(tree_h,'userdata',userdata);
        end
        set(hTreeFig,'userdata',userdata);

        % Set keyboard focus on the tree
        jTreeObj.requestFocus;
        drawnow;

        % Check for a newer version
        checkVersion();

        % Reset the last error
        lasterr('');  %#ok
    end

Rresize image pane

    function resizeImg(varargin)  %#ok - unused (TODO: waiting for img placement fix...)
        try
            hPanel = varargin{1};
            hAx    = varargin{3};
            lowerPanel = get(hPanel,'UserData');
            newJPos = cell2mat(get(hPanel,{'X','Y','Width','Height'}));
            newMPos = [1,get(lowerPanel,'Height'),newJPos(3:4)];
            set(hAx, 'units','pixels', 'position',newMPos, 'Visible','on');
            uistack(hAx,'top');  % no good...
            set(hPanel,'Opaque','off');  % also no good...
        catch
            % Never mind...
            dispError
        end
        return;
    end

"dbstop if error" causes inspect.m to croak due to a bug - so workaround by temporarily disabling this dbstop

    function identifiers = disableDbstopError
        dbStat = dbstatus;
        idx = find(strcmp({dbStat.cond},'error'));
        identifiers = [dbStat(idx).identifier];
        if ~isempty(idx)
            dbclear if error;
            msgbox('''dbstop if error'' had to be disabled due to a Matlab bug that would have caused Matlab to crash.', 'FindJObj', 'warn');
        end
    end

Restore any previous "dbstop if error"

    function restoreDbstopError(identifiers)  %#ok
        for itemIdx = 1 : length(identifiers)
            eval(['dbstop if error ' identifiers{itemIdx}]);
        end
    end

Recursively expand all nodes (except toolbar/menubar) in startup

    function expandNode(progressBar, tree, tree_h, parentNode, parentRow)
        try
            if nargin < 5
                parentPath = javax.swing.tree.TreePath(parentNode.getPath);
                parentRow = tree.getRowForPath(parentPath);
            end
            tree.expandRow(parentRow);
            progressBar.setValue(progressBar.getValue+1);
            numChildren = parentNode.getChildCount;
            if (numChildren == 0)
                pause(0.0002);  % as short as possible...
                drawnow;
            end
            nodesToUnExpand = {'FigureMenuBar','MLMenuBar','MJToolBar','Box','uimenu','uitoolbar','ScrollBar'};
            numChildren = parentNode.getChildCount;
            for childIdx = 0 : numChildren-1
                childNode = parentNode.getChildAt(childIdx);

                % Pre-select the node based upon the user's FINDJOBJ filters
                try
                    nodedata = get(childNode, 'userdata');
                    try
                        userdata = get(tree_h, 'userdata');
                    catch
                        userdata = getappdata(tree_h, 'userdata');
                    end
                    %fprintf('%d - %s\n',nodedata.idx,char(nodedata.obj))
                    if ~ishghandle(nodedata.obj) && ~userdata.userSelected && any(userdata.initialIdx == nodedata.idx)
                        pause(0.0002);  % as short as possible...
                        drawnow;
                        if isempty(tree_h.getSelectedNodes)
                            tree_h.setSelectedNode(childNode);
                        else
                            newSelectedNodes = [tree_h.getSelectedNodes, childNode];
                            tree_h.setSelectedNodes(newSelectedNodes);
                        end
                    end
                catch
                    % never mind...
                    dispError
                end

                % Expand child node if not leaf & not toolbar/menubar
                if childNode.isLeafNode

                    % This is a leaf node, so simply update the progress-bar
                    progressBar.setValue(progressBar.getValue+1);

                else
                    % Expand all non-leaves
                    expandNode(progressBar, tree, tree_h, childNode);

                    % Re-collapse toolbar/menubar etc., and also invisible containers
                    % Note: if we simply did nothing, progressbar would not have been updated...
                    try
                        childHandle = getappdata(childNode,'childHandle');  %=childNode.getUserObject
                        visible = childHandle.isVisible;
                    catch
                        visible = 1;
                    end
                    visible = visible && isempty(findstr(get(childNode,'Name'),'color="gray"'));
                    %if any(strcmp(childNode.getName,nodesToUnExpand))
                    %name = char(childNode.getName);
                    if any(cellfun(@(s)~isempty(strmatch(s,char(childNode.getName))),nodesToUnExpand)) || ~visible
                        childPath = javax.swing.tree.TreePath(childNode.getPath);
                        childRow = tree.getRowForPath(childPath);
                        tree.collapseRow(childRow);
                    end
                end
            end
        catch
            % never mind...
            dispError
        end
    end

Create utility buttons

    function hButton = createJButton(nameStr, handler, toolTipText)
        try
            jButton = javax.swing.JButton(['<html><body><center>' nameStr]);
            jButton.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR));
            jButton.setToolTipText(toolTipText);
            minSize = jButton.getMinimumSize;
            jButton.setMinimumSize(java.awt.Dimension(minSize.getWidth,35));
            hButton = handle(jButton,'CallbackProperties');
            set(hButton,'ActionPerformedCallback',handler);
        catch
            % Never mind...
        end
    end

Flash a component off/on for the specified duration

note: starts with 'on'; if numTimes is odd then ends with 'on', otherwise with 'off'

    function flashComponent(jComps,delaySecs,numTimes)
        persistent redBorder redBorderPanels
        try
            % Handle callback data from right-click (context-menu)
            if iscell(numTimes)
                [jComps,delaySecs,numTimes] = deal(numTimes{:});
            end

            if isempty(redBorder)  % reuse if possible
                redBorder = javax.swing.border.LineBorder(java.awt.Color.red,2,0);
            end
            for compIdx = 1 : length(jComps)
                try
                    oldBorder{compIdx} = jComps(compIdx).getBorder;  %#ok grow
                catch
                    oldBorder{compIdx} = [];  %#ok grow
                end
                isSettable(compIdx) = ismethod(jComps(compIdx),'setBorder');  %#ok grow
                if isSettable(compIdx)
                    try
                        % some components prevent border modification:
                        oldBorderFlag = jComps(compIdx).isBorderPainted;
                        if ~oldBorderFlag
                            jComps(compIdx).setBorderPainted(1);
                            isSettable(compIdx) = jComps(compIdx).isBorderPainted;  %#ok grow
                            jComps(compIdx).setBorderPainted(oldBorderFlag);
                        end
                    catch
                        % do nothing...
                    end
                end
                if compIdx > length(redBorderPanels)
                    redBorderPanels{compIdx} = javax.swing.JPanel;
                    redBorderPanels{compIdx}.setBorder(redBorder);
                    redBorderPanels{compIdx}.setOpaque(0);  % transparent interior, red border
                end
                try
                    redBorderPanels{compIdx}.setBounds(jComps(compIdx).getBounds);
                catch
                    % never mind - might be an HG handle
                end
            end
            for idx = 1 : 2*numTimes
                if idx>1,  pause(delaySecs);  end  % don't pause at start
                visible = mod(idx,2);
                for compIdx = 1 : length(jComps)
                    try
                        jComp = jComps(compIdx);

                        % Prevent Matlab crash (java buffer overflow...)
                        if jComp.isa('com.mathworks.mwswing.desk.DTSplitPane') || ...
                           jComp.isa('com.mathworks.mwswing.MJSplitPane')
                            continue;
                        end

                        % HG handles are highlighted by setting their 'Selected' property
                        if isa(jComp,'uimenu')
                            if visible
                                oldColor = get(jComp,'ForegroundColor');
                                setappdata(jComp,'findjobj_oldColor',oldColor);
                                set(jComp,'ForegroundColor','red');
                            else
                                oldColor = getappdata(jComp,'findjobj_oldColor');
                                set(jComp,'ForegroundColor',oldColor);
                                rmappdata(jComp,'ForegroundColor');
                            end

                        elseif ishghandle(jComp)
                            if visible
                                set(jComp,'Selected','on');
                            else
                                set(jComp,'Selected','off');
                            end

                        else %if isjava(jComp)

                            jParent = jComps(compIdx).getParent;

                            % Most Java components allow modifying their borders
                            if isSettable(compIdx)
                                if visible
                                    jComp.setBorder(redBorder);
                                    try jComp.setBorderPainted(1); catch, end  %#ok
                                else %if ~isempty(oldBorder{compIdx})
                                    jComp.setBorder(oldBorder{compIdx});
                                end
                                jComp.repaint;

                            % The other Java components are highlighted by a transparent red-border
                            % panel that is placed on top of them in their parent's space
                            elseif ~isempty(jParent)
                                if visible
                                    jParent.add(redBorderPanels{compIdx});
                                    jParent.setComponentZOrder(redBorderPanels{compIdx},0);
                                else
                                    jParent.remove(redBorderPanels{compIdx});
                                end
                                jParent.repaint
                            end
                        end
                    catch
                        % never mind - try the next component (if any)
                    end
                end
                drawnow;
            end
        catch
            % never mind...
            dispError;
        end
        return;  % debug point
    end  % flashComponent

Select tree node

    function nodeSelected(src, evd, tree)  %#ok
        try
            if iscell(tree)
                [src,node] = deal(tree{:});
            else
                node = evd.getCurrentNode;
            end
            %nodeHandle = node.getUserObject;
            nodedata = get(node,'userdata');
            nodeHandle = nodedata.obj;
            try
                userdata = get(src,'userdata');
            catch
                userdata = getappdata(src.java,'userdata');
            end
            if ~isempty(nodeHandle) && ~isempty(userdata)
                numSelections  = userdata.jTree.getSelectionCount;
                selectionPaths = userdata.jTree.getSelectionPaths;
                if (numSelections == 1)
                    % Indicate that the user has modified the initial selection (except if this was an initial auto-selected node)
                    if ~userdata.inInit
                        userdata.userSelected = true;
                        try
                            set(src,'userdata',userdata);
                        catch
                            setappdata(src.java,'userdata',userdata);
                        end
                    end

                    % Update the fully-qualified class name label
                    numInitialIdx = length(userdata.initialIdx);
                    thisHandle = nodeHandle;
                    try
                        if ~ishghandle(thisHandle)
                            thisHandle = java(nodeHandle);
                        end
                    catch
                        % never mind...
                    end
                    if ~userdata.inInit || (numInitialIdx == 1)
                        userdata.classNameLabel.setText(['      ' char(thisHandle.class)]);
                    else
                        userdata.classNameLabel.setText([' ' num2str(numInitialIdx) 'x handles (some handles hidden by unexpanded tree nodes)']);
                    end
                    if ishghandle(thisHandle)
                        userdata.classNameLabel.setText(userdata.classNameLabel.getText.concat(' (HG handle)'));
                    end
                    userdata.inspectorPane.dispose;  % remove props listeners - doesn't work...
                    updateNodeTooltip(nodeHandle, userdata.classNameLabel);

                    % Update the data properties inspector pane
                    % Note: we can't simply use the evd nodeHandle, because this node might have been DE-selected with only one other node left selected...
                    %nodeHandle = selectionPaths(1).getLastPathComponent.getUserObject;
                    nodedata = get(selectionPaths(1).getLastPathComponent,'userdata');
                    nodeHandle = nodedata.obj;
                    %identifiers = disableDbstopError;  % "dbstop if error" causes inspect.m to croak due to a bug - so workaround
                    userdata.inspectorPane.setObject(thisHandle);

                    % Update the callbacks table
                    try
                        stripStdCbsFlag = getappdata(userdata.callbacksTable,'hideStdCbs');
                        [cbData, cbHeaders, cbTableEnabled] = getCbsData(nodeHandle, stripStdCbsFlag);  %#ok cbTableEnabled unused
                        callbacksTableModel = javax.swing.table.DefaultTableModel(cbData,cbHeaders);
                        set(handle(callbacksTableModel,'CallbackProperties'), 'TableChangedCallback',{@tbCallbacksChanged,nodeHandle});
                        %set(callbacksTableModel, 'UserData',nodeHandle);
                        userdata.callbacksTable.setModel(callbacksTableModel)
                        userdata.callbacksTable.setRowAutoResizes(true);
                        userdata.jideTableUtils.autoResizeAllColumns(userdata.callbacksTable);
                    catch
                        % never mind...
                        %dispError
                    end
                    pause(0.005);
                    drawnow;
                    %restoreDbstopError(identifiers);

                    % Highlight the selected object (if visible)
                    flashComponent(nodeHandle,0.2,3);

                elseif (numSelections > 1)  % Multiple selections

                    % Get the list of all selected nodes
                    jArray = javaArray('java.lang.Object', numSelections);
                    toolTipStr = '<html>';
                    sameClassFlag = true;
                    for idx = 1 : numSelections
                        %jArray(idx) = selectionPaths(idx).getLastPathComponent.getUserObject;
                        nodedata = get(selectionPaths(idx).getLastPathComponent,'userdata');
                        try
                            jArray(idx) = java(nodedata.obj);
                        catch
                            jArray(idx) = nodedata.obj;
                        end
                        toolTipStr = [toolTipStr '&nbsp;' jArray(idx).class '&nbsp;'];  %#ok grow
                        if (idx < numSelections),  toolTipStr = [toolTipStr '<br>'];  end  %#ok grow
                        if (idx > 1) && sameClassFlag && ~isequal(jArray(idx).getClass,jArray(1).getClass)
                            sameClassFlag = false;
                        end
                    end
                    toolTipStr = [toolTipStr '</html>'];

                    % Update the fully-qualified class name label
                    if sameClassFlag
                        classNameStr = jArray(1).class;
                    else
                        classNameStr = 'handle';
                    end
                    if all(ishghandle(jArray))
                        if strcmp(classNameStr,'handle')
                            classNameStr = 'HG handles';
                        else
                            classNameStr = [classNameStr ' (HG handles)'];
                        end
                    end
                    classNameStr = [' ' num2str(numSelections) 'x ' classNameStr];
                    userdata.classNameLabel.setText(classNameStr);
                    userdata.classNameLabel.setToolTipText(toolTipStr);

                    % Update the data properties inspector pane
                    %identifiers = disableDbstopError;  % "dbstop if error" causes inspect.m to croak due to a bug - so workaround
                    userdata.inspectorPane.getRegistry.setSelected(jArray, true);

                    % Update the callbacks table
                    try
                        % Get intersecting callback names & values
                        stripStdCbsFlag = getappdata(userdata.callbacksTable,'hideStdCbs');
                        [cbData, cbHeaders, cbTableEnabled] = getCbsData(jArray(1), stripStdCbsFlag);  %#ok cbHeaders & cbTableEnabled unused
                        if ~isempty(cbData)
                            cbNames = cbData(:,1);
                            for idx = 2 : length(jArray)
                                [cbData2, cbHeaders2] = getCbsData(jArray(idx), stripStdCbsFlag);  %#ok cbHeaders2 unused
                                if ~isempty(cbData2)
                                    newCbNames = cbData2(:,1);
                                    [cbNames, cbIdx, cb2Idx] = intersect(cbNames,newCbNames);  %#ok cb2Idx unused
                                    cbData = cbData(cbIdx,:);
                                    for cbIdx = 1 : length(cbNames)
                                        newIdx = find(strcmp(cbNames{cbIdx},newCbNames));
                                        if ~isequal(cbData2{newIdx,2}, cbData{cbIdx,2})
                                            cbData{cbIdx,2} = '<different values>';
                                        end
                                    end
                                else
                                    cbData = cbData([],:);  %=empty cell array
                                end
                                if isempty(cbData)
                                    break;
                                end
                            end
                        end
                        cbHeaders = {'Callback name','Callback value'};
                        callbacksTableModel = javax.swing.table.DefaultTableModel(cbData,cbHeaders);
                        set(handle(callbacksTableModel,'CallbackProperties'), 'TableChangedCallback',{@tbCallbacksChanged,jArray});
                        %set(callbacksTableModel, 'UserData',jArray);
                        userdata.callbacksTable.setModel(callbacksTableModel)
                        userdata.callbacksTable.setRowAutoResizes(true);
                        userdata.jideTableUtils.autoResizeAllColumns(userdata.callbacksTable);
                    catch
                        % never mind...
                        dispError
                    end

                    pause(0.005);
                    drawnow;
                    %restoreDbstopError(identifiers);

                    % Highlight the selected objects (if visible)
                    flashComponent(jArray,0.2,3);
                end

                % TODO: Auto-highlight selected object (?)
                %nodeHandle.requestFocus;
            end
        catch
            dispError
        end
    end

IFF utility function for annonymous cellfun funcs

    function result = iff(test,trueVal,falseVal)  %#ok
        try
            if test
                result = trueVal;
            else
                result = falseVal;
            end
        catch
            result = false;
        end
    end

Get an HTML representation of the object's properties

    function dataFieldsStr = getPropsHtml(nodeHandle, dataFields)
        try
            % Get a text representation of the fieldnames & values
            dataFieldsStr = '';  % just in case the following croaks...
            if isempty(dataFields)
                return;
            end
            dataFieldsStr = evalc('disp(dataFields)');
            if dataFieldsStr(end)==char(10),  dataFieldsStr=dataFieldsStr(1:end-1);  end

            % Strip out callbacks
            dataFieldsStr = regexprep(dataFieldsStr,'^\s*\w*Callback(Data)?:[^\n]*$','','lineanchors');
            dataFieldsStr = regexprep(dataFieldsStr,'\n\n','\n');

            % HTMLize tooltip data
            % First, set the fields' font based on its read-write status
            nodeHandle = handle(nodeHandle);  % ensure this is a Matlab handle, not a java object
            fieldNames = fieldnames(dataFields);
            undefinedStr = '';
            for fieldIdx = 1 : length(fieldNames)
                thisFieldName = fieldNames{fieldIdx};
                accessFlags = get(findprop(nodeHandle,thisFieldName),'AccessFlags');
                if isfield(accessFlags,'PublicSet') && strcmp(accessFlags.PublicSet,'on')
                    % Bolden read/write fields
                    thisFieldFormat = ['<b>' thisFieldName '<b>:$2'];
                elseif ~isfield(accessFlags,'PublicSet')
                    % Undefined - probably a Matlab-defined field of com.mathworks.hg.peer.FigureFrameProxy...
                    thisFieldFormat = ['<font color="blue">' thisFieldName '</font>:$2'];
                    undefinedStr = ', <font color="blue">undefined</font>';
                else % PublicSet=='off'
                    % Gray-out & italicize any read-only fields
                    thisFieldFormat = ['<font color="#C0C0C0"><i>' thisFieldName '</i></font>:<font color="#C0C0C0"><i>$2<i></font>'];
                end
                dataFieldsStr = regexprep(dataFieldsStr, ['([\s\n])' thisFieldName ':([^\n]*)'], ['$1' thisFieldFormat]);
            end
        catch
            % never mind... - probably an ambiguous property name
            %dispError
        end

        % Method 1: simple <br> list
        %dataFieldsStr = strrep(dataFieldsStr,char(10),'&nbsp;<br>&nbsp;&nbsp;');

        % Method 2: 2x2-column <table>
        dataFieldsStr = regexprep(dataFieldsStr, '^\s*([^:]+:)([^\n]*)\n^\s*([^:]+:)([^\n]*)$', '<tr><td>&nbsp;$1</td><td>&nbsp;$2</td><td>&nbsp;&nbsp;&nbsp;&nbsp;$3</td><td>&nbsp;$4&nbsp;</td></tr>', 'lineanchors');
        dataFieldsStr = regexprep(dataFieldsStr, '^[^<]\s*([^:]+:)([^\n]*)$', '<tr><td>&nbsp;$1</td><td>&nbsp;$2</td><td>&nbsp;</td><td>&nbsp;</td></tr>', 'lineanchors');
        dataFieldsStr = ['(<b>modifiable</b>' undefinedStr ' &amp; <font color="#C0C0C0"><i>read-only</i></font> fields)<p>&nbsp;&nbsp;<table cellpadding="0" cellspacing="0">' dataFieldsStr '</table>'];
    end

Update tooltip string with a node's data

    function updateNodeTooltip(nodeHandle, uiObject)
        try
            toolTipStr = nodeHandle.class;
            dataFieldsStr = '';

            % Add HG annotation if relevant
            if ishghandle(nodeHandle)
                hgStr = ' HG Handle';
            else
                hgStr = '';
            end

            % Note: don't bulk-get because (1) not all properties are returned & (2) some properties cause a Java exception
            % Note2: the classhandle approach does not enable access to user-defined schema.props
            ch = classhandle(handle(nodeHandle));
            dataFields = [];
            [sortedNames, sortedIdx] = sort(get(ch.Properties,'Name'));
            for idx = 1 : length(sortedIdx)
                sp = ch.Properties(sortedIdx(idx));
                % TODO: some fields (see EOL comment below) generate a Java Exception from: com.mathworks.mlwidgets.inspector.PropertyRootNode$PropertyListener$1$1.run
                if strcmp(sp.AccessFlags.PublicGet,'on') % && ~any(strcmp(sp.Name,{'FixedColors','ListboxTop','Extent'}))
                    try
                        dataFields.(sp.Name) = get(nodeHandle, sp.Name);
                    catch
                        dataFields.(sp.Name) = '<font color="red">Error!</font>';
                    end
                else
                    dataFields.(sp.Name) = '(no public getter method)';
                end
            end
            dataFieldsStr = getPropsHtml(nodeHandle, dataFields);
        catch
            % Probably a non-HG java object
            try
                % Note: the bulk-get approach enables access to user-defined schema-props, but not to some original classhandle Properties...
                dataFields = get(nodeHandle);
                dataFieldsStr = getPropsHtml(nodeHandle, dataFields);
            catch
                % Probably a missing property getter implementation
                try
                    % Inform the user - bail out on error
                    err = lasterror;  %#ok
                    dataFieldsStr = ['<p>' strrep(err.message, char(10), '<br>')];
                catch
                    % forget it...
                end
            end
        end

        % Set the object tooltip
        if ~isempty(dataFieldsStr)
            toolTipStr = ['<html>&nbsp;<b><font color="blue">' char(toolTipStr) '</font></b>' hgStr ':&nbsp;' dataFieldsStr '</html>'];
        end
        uiObject.setToolTipText(toolTipStr);
    end

Expand tree node

    function nodeExpanded(src, evd, tree)  %#ok
        % tree = handle(src);
        % evdsrc = evd.getSource;
        evdnode = evd.getCurrentNode;

        if ~tree.isLoaded(evdnode)

            % Get the list of children TreeNodes
            childnodes = getChildrenNodes(tree, evdnode);

            % Add the HG sub-tree (unless already included in the first tree)
            childHandle = getappdata(evdnode.handle,'childHandle');  %=evdnode.getUserObject
            if evdnode.isRoot && ~isempty(hg_handles) && ~isequal(hg_handles(1).java, childHandle)
                childnodes = [childnodes, getChildrenNodes(tree, evdnode, true)];
            end

            % If we have a single child handle, wrap it within a javaArray for tree.add() to "swallow"
            if (length(childnodes) == 1)
                chnodes = childnodes;
                childnodes = javaArray('com.mathworks.hg.peer.UITreeNode', 1);
                childnodes(1) = java(chnodes);
            end

            % Add child nodes to the current node
            tree.add(evdnode, childnodes);
            tree.setLoaded(evdnode, true);
        end
    end

Get an icon image no larger than 16x16 pixels

    function iconImage = setIconSize(iconImage)
        try
            iconWidth  = iconImage.getWidth;
            iconHeight = iconImage.getHeight;
            if iconWidth > 16
                newHeight = fix(iconHeight * 16 / iconWidth);
                iconImage = iconImage.getScaledInstance(16,newHeight,iconImage.SCALE_SMOOTH);
            elseif iconHeight > 16
                newWidth = fix(iconWidth * 16 / iconHeight);
                iconImage = iconImage.getScaledInstance(newWidth,16,iconImage.SCALE_SMOOTH);
            end
        catch
            % never mind... - return original icon
        end
    end  % setIconSize

Get list of children nodes

    function nodes = getChildrenNodes(tree, parentNode, isRootHGNode)
        try
            iconpath = [matlabroot, '/toolbox/matlab/icons/'];
            nodes = handle([]);
            try
                userdata = get(tree,'userdata');
            catch
                userdata = getappdata(tree,'userdata');
            end
            hdls = userdata.handles;
            nodedata = get(parentNode,'userdata');
            if nargin < 3
                %isJavaNode = ~ishghandle(parentNode.getUserObject);
                isJavaNode = ~ishghandle(nodedata.obj);
                isRootHGNode = false;
            else
                isJavaNode = ~isRootHGNode;
            end

            % Search for this parent node in the list of all nodes
            parents = userdata.parents;
            nodeIdx = nodedata.idx;

            if isJavaNode && isempty(nodeIdx)  % Failback, in case userdata doesn't work for some reason...
                for hIdx = 1 : length(hdls)
                    %if isequal(handle(parentNode.getUserObject), hdls(hIdx))
                    if isequal(handle(nodedata.obj), hdls(hIdx))
                        nodeIdx = hIdx;
                        break;
                    end
                end
            end
            if ~isJavaNode
                if isRootHGNode  % =root HG node
                    thisChildHandle = userdata.hg_handles(1);
                    childName = getNodeName(thisChildHandle);
                    hasGrandChildren = any(parents==1);
                    icon = [];
                    if hasGrandChildren && length(hg_handles)>1
                        childName = childName.concat(' - HG root container');
                        icon = [iconpath 'figureicon.gif'];
                    end
                    try
                        nodes = uitreenode('v0', thisChildHandle, childName, icon, ~hasGrandChildren);
                    catch  % old matlab version don't have the 'v0' option
                        try
                            nodes = uitreenode(thisChildHandle, childName, icon, ~hasGrandChildren);
                        catch
                            % probably an invalid handle - ignore...
                        end
                    end

                    % Add the handler to the node's internal data
                    % Note: could also use 'userdata', but setUserObject() is recommended for TreeNodes
                    % Note2: however, setUserObject() sets a java *BeenAdapter object for HG handles instead of the required original class, so use setappdata
                    %nodes.setUserObject(thisChildHandle);
                    setappdata(nodes,'childHandle',thisChildHandle);
                    nodedata.idx = 1;
                    nodedata.obj = thisChildHandle;
                    set(nodes,'userdata',nodedata);
                    return;
                else  % non-root HG node
                    parents = userdata.hg_parents;
                    hdls    = userdata.hg_handles;
                end  % if isRootHGNode
            end  % if ~isJavaNode

            % If this node was found, get the list of its children
            if ~isempty(nodeIdx)
                %childIdx = setdiff(find(parents==nodeIdx),nodeIdx);
                childIdx = find(parents==nodeIdx);
                childIdx(childIdx==nodeIdx) = [];  % faster...
                numChildren = length(childIdx);
                for cIdx = 1 : numChildren
                    thisChildIdx = childIdx(cIdx);
                    thisChildHandle = hdls(thisChildIdx);
                    childName = getNodeName(thisChildHandle);
                    try
                        visible = thisChildHandle.Visible;
                        if visible
                            try visible = thisChildHandle.Width > 0; catch, end  %#ok
                        end
                        if ~visible
                            childName = ['<HTML><i><font color="gray">' char(childName) '</font></i></html>'];  %#ok grow
                        end
                    catch
                        % never mind...
                    end
                    hasGrandChildren = any(parents==thisChildIdx);
                    try
                        isaLabel = isa(thisChildHandle.java,'javax.swing.JLabel');
                    catch
                        isaLabel = 0;
                    end
                    if hasGrandChildren && ~any(strcmp(thisChildHandle.class,{'axes'}))
                        icon = [iconpath 'foldericon.gif'];
                    elseif isaLabel
                        icon = [iconpath 'tool_text.gif'];
                    else
                        icon = [];
                    end
                    try
                        nodes(cIdx) = uitreenode('v0', thisChildHandle, childName, icon, ~hasGrandChildren);
                    catch  % old matlab version don't have the 'v0' option
                        try
                            nodes(cIdx) = uitreenode(thisChildHandle, childName, icon, ~hasGrandChildren);
                        catch
                            % probably an invalid handle - ignore...
                        end
                    end

                    % Use existing object icon, if available
                    try
                        setTreeNodeIcon(nodes(cIdx),thisChildHandle);
                    catch
                        % probably an invalid handle - ignore...
                    end

                    % Pre-select the node based upon the user's FINDJOBJ filters
                    try
                        if isJavaNode && ~userdata.userSelected && any(userdata.initialIdx == thisChildIdx)
                            pause(0.0002);  % as short as possible...
                            drawnow;
                            if isempty(tree.getSelectedNodes)
                                tree.setSelectedNode(nodes(cIdx));
                            else
                                newSelectedNodes = [tree.getSelectedNodes, nodes(cIdx).java];
                                tree.setSelectedNodes(newSelectedNodes);
                            end
                        end
                    catch
                        % never mind...
                    end

                    % Add the handler to the node's internal data
                    % Note: could also use 'userdata', but setUserObject() is recommended for TreeNodes
                    % Note2: however, setUserObject() sets a java *BeenAdapter object for HG handles instead of the required original class, so use setappdata
                    % Note3: the following will error if invalid handle - ignore
                    try
                        if isJavaNode
                            thisChildHandle = thisChildHandle.java;
                        end
                        %nodes(cIdx).setUserObject(thisChildHandle);
                        setappdata(nodes(cIdx),'childHandle',thisChildHandle);
                        nodedata.idx = thisChildIdx;
                        nodedata.obj = thisChildHandle;
                        set(nodes(cIdx),'userdata',nodedata);
                    catch
                        % never mind (probably an invalid handle) - leave unchanged (like a leaf)
                    end
                end
            end
        catch
            % Never mind - leave unchanged (like a leaf)
            %error('YMA:findjobj:UnknownNodeType', 'Error expanding component tree node');
            dispError
        end
    end

Get a node's name

    function [nodeName, nodeTitle] = getNodeName(hndl,charsLimit)
        try
            % Initialize (just in case one of the succeding lines croaks)
            nodeName = '';
            nodeTitle = '';
            if ~ismethod(hndl,'getClass')
                try
                    nodeName = hndl.class;
                catch
                    nodeName = hndl.type;  % last-ditch try...
                end
            else
                nodeName = hndl.getClass.getSimpleName;
            end

            % Strip away the package name, leaving only the regular classname
            if ~isempty(nodeName) && ischar(nodeName)
                nodeName = java.lang.String(nodeName);
                nodeName = nodeName.substring(nodeName.lastIndexOf('.')+1);
            end
            if (nodeName.length == 0)
                % fix case of anonymous internal classes, that do not have SimpleNames
                try
                    nodeName = hndl.getClass.getName;
                    nodeName = nodeName.substring(nodeName.lastIndexOf('.')+1);
                catch
                    % never mind - leave unchanged...
                end
            end

            % Get any unique identifying string (if available in one of several fields)
            labelsToCheck = {'label','title','text','string','displayname','toolTipText','TooltipString','actionCommand','name','Tag','style'}; %,'UIClassID'};
            nodeTitle = '';
            strField = '';  %#ok - used for debugging
            while ((~isa(nodeTitle,'java.lang.String') && ~ischar(nodeTitle)) || isempty(nodeTitle)) && ~isempty(labelsToCheck)
                try
                    nodeTitle = get(hndl,labelsToCheck{1});
                    strField = labelsToCheck{1};  %#ok - used for debugging
                catch
                    % never mind - probably missing prop, so skip to next one
                end
                labelsToCheck(1) = [];
            end
            if length(nodeTitle) ~= numel(nodeTitle)
                % Multi-line - convert to a long single line
                nodeTitle = nodeTitle';
                nodeTitle = nodeTitle(:)';
            end
            if isempty(char(nodeTitle))
                % No title - check whether this is an HG label whose text is gettable
                try
                    location = hndl.getLocationOnScreen;
                    pos = [location.getX, location.getY, hndl.getWidth, hndl.getHeight];
                    dist = sum((labelPositions-repmat(pos,size(labelPositions,1),[1,1,1,1])).^2, 2);
                    [minVal,minIdx] = min(dist);
                    % Allow max distance of 8 = 2^2+2^2 (i.e. X&Y off by up to 2 pixels, W&H exact)
                    if minVal <= 8  % 8=2^2+2^2
                        nodeTitle = get(hg_labels(minIdx),'string');
                        % Preserve the label handles & position for the tooltip & context-menu
                        %hg_labels(minIdx) = [];
                        %labelPositions(minIdx,:) = [];
                    end
                catch
                    % never mind...
                end
            end
            if nargin<2,  charsLimit = 25;  end
            extraStr = regexprep(nodeTitle,{sprintf('(.{%d,%d}).*',charsLimit,min(charsLimit,length(nodeTitle)-1)),' +'},{'$1...',' '},'once');
            if ~isempty(extraStr)
                if ischar(extraStr)
                    nodeName = nodeName.concat(' (''').concat(extraStr).concat(''')');
                else
                    nodeName = nodeName.concat(' (').concat(num2str(extraStr)).concat(')');
                end
                %nodeName = nodeName.concat(strField);
            end
        catch
            % Never mind - use whatever we have so far
            %dispError
        end
    end

Strip standard Swing callbacks from a list of events

    function evNames = stripStdCbs(evNames)
        try
            stdEvents = {'AncestorAdded', 'AncestorMoved', 'AncestorRemoved', 'AncestorResized', ...
                         'CaretPositionChanged', 'ComponentAdded', 'ComponentRemoved', ...
                         'ComponentHidden', 'ComponentMoved', 'ComponentResized', 'ComponentShown', ...
                         'PropertyChange', 'FocusGained', 'FocusLost', ...
                         'HierarchyChanged', 'InputMethodTextChanged', ...
                         'KeyPressed', 'KeyReleased', 'KeyTyped', ...
                         'MouseClicked', 'MouseDragged', 'MouseEntered', 'MouseExited', ...
                         'MouseMoved', 'MousePressed', 'MouseReleased', 'MouseWheelMoved', ...
                         'VetoableChange'};
            stdEvents = [stdEvents, strcat(stdEvents,'Callback'), strcat(stdEvents,'Fcn')];
            evNames = setdiff(evNames,stdEvents)';
        catch
            % Never mind...
            dispError
        end
    end

Callback function for standard callbacks checkbox

    function cbHideStdCbs_Callback(src, evd, callbacksTable, varargin)  %#ok
        try
            % Store the current checkbox value for later use
            if nargin < 3
                try
                    callbacksTable = get(src,'userdata');
                catch
                    callbacksTable = getappdata(src,'userdata');
                end
            end
            if evd.getSource.isSelected
                setappdata(callbacksTable,'hideStdCbs',1);
            else
                setappdata(callbacksTable,'hideStdCbs',[]);
            end

            % Rescan the current node
            try
                userdata = get(callbacksTable,'userdata');
            catch
                userdata = getappdata(callbacksTable,'userdata');
            end
            nodepath = userdata.jTree.getSelectionModel.getSelectionPath;
            try
                ed.getCurrentNode = nodepath.getLastPathComponent;
                nodeSelected(handle(userdata.jTreePeer),ed,[]);
            catch
                % ignore - probably no node selected
            end
        catch
            % Never mind...
            dispError
        end
    end

Callback function for UndocumentedMatlab.com button

    function btWebsite_Callback(src, evd, varargin)  %#ok
        try
            web('http://UndocumentedMatlab.com/');
        catch
            % Never mind...
            dispError
        end
    end

Callback function for data button

    function btRefresh_Callback(src, evd, varargin)  %#ok
        try
            % Set cursor shape to hourglass until we're done
            hTreeFig = varargin{2};
            set(hTreeFig,'Pointer','watch');
            drawnow;
            object = varargin{1};

            % Re-invoke this utility to re-scan the container for all children
            findjobj(object);
        catch
            % Never mind...
        end

        % Restore default cursor shape
        set(hTreeFig,'Pointer','arrow');
    end

Callback function for Export button

    function btExport_Callback(src, evd, varargin)  %#ok
        try
            % Get the list of all selected nodes
            if length(varargin) > 1
                jTree = varargin{1};
                numSelections  = jTree.getSelectionCount;
                selectionPaths = jTree.getSelectionPaths;
                hdls = handle([]);
                for idx = 1 : numSelections
                    %hdls(idx) = handle(selectionPaths(idx).getLastPathComponent.getUserObject);
                    nodedata = get(selectionPaths(idx).getLastPathComponent,'userdata');
                    hdls(idx) = handle(nodedata.obj,'CallbackProperties');
                end

                % Assign the handles in the base workspace & inform user
                assignin('base','findjobj_hdls',hdls);
                classNameLabel = varargin{2};
                msg = ['Exported ' char(classNameLabel.getText.trim) ' to base workspace variable findjobj_hdls'];
            else
                % Right-click (context-menu) callback
                data = varargin{1};
                obj = data{1};
                varName = data{2};
                if isempty(varName)
                    varName = inputdlg('Enter workspace variable name','FindJObj');
                    if isempty(varName),  return;  end  % bail out on <Cancel>
                    varName = varName{1};
                    if isempty(varName) || ~ischar(varName),  return;  end  % bail out on empty/null
                    varName = genvarname(varName);
                end
                assignin('base',varName,handle(obj,'CallbackProperties'));
                msg = ['Exported object to base workspace variable ' varName];
            end
            msgbox(msg,'FindJObj','help');
        catch
            % Never mind...
            dispError
        end
    end

Callback function for focus button

    function btFocus_Callback(src, evd, varargin)  %#ok
        try
            % Request focus for the specified object
            object = getTopSelectedObject(varargin{:});
            object.requestFocus;
        catch
            try
                object = object.java.getPeer.requestFocus;
                object.requestFocus;
            catch
                % Never mind...
                %dispError
            end
        end
    end

Callback function for Inspect button

    function btInspect_Callback(src, evd, varargin)  %#ok
        try
            % Inspect the specified object
            if length(varargin) == 1
                object = varargin{1};
            else
                object = getTopSelectedObject(varargin{:});
            end
            if isempty(which('uiinspect'))

                % If the user has not indicated NOT to be informed about UIInspect
                if ~ispref('FindJObj','dontCheckUIInspect')

                    % Ask the user whether to download UIINSPECT (YES, no, no & don't ask again)
                    answer = questdlg({'The object inspector requires UIINSPECT from the MathWorks File Exchange. UIINSPECT was created by Yair Altman, like this FindJObj utility.','','Download & install UIINSPECT?'},'UIInspect','Yes','No','No & never ask again','Yes');
                    switch answer
                        case 'Yes'  % => Yes: download & install
                            try
                                % Download UIINSPECT
                                baseUrl = 'http://www.mathworks.com/matlabcentral/fileexchange/17935';
                                fileUrl = [baseUrl '?controller=file_infos&download=true'];
                                %file = urlread(fileUrl);
                                %file = regexprep(file,[char(13),char(10)],'\n');  %convert to OS-dependent EOL

                                % Install...
                                %newPath = fullfile(fileparts(which(mfilename)),'uiinspect.m');
                                %fid = fopen(newPath,'wt');
                                %fprintf(fid,'%s',file);
                                %fclose(fid);
                                [fpath,fname,fext] = fileparts(which(mfilename));
                                zipFileName = fullfile(fpath,'uiinspect.zip');
                                urlwrite(fileUrl,zipFileName);
                                unzip(zipFileName,fpath);
                                rehash;
                            catch
                                % Error downloading: inform the user
                                msgbox(['Error in downloading: ' lasterr], 'UIInspect', 'warn');  %#ok
                                web(baseUrl);
                            end

                            % ...and now run it...
                            %pause(0.1);
                            drawnow;
                            dummy = which('uiinspect');  %#ok used only to load into memory
                            uiinspect(object);
                            return;

                        case 'No & never ask again'   % => No & don't ask again
                            setpref('FindJObj','dontCheckUIInspect',1);

                        otherwise
                            % forget it...
                    end
                end
                drawnow;

                % No UIINSPECT available - run the good-ol' METHODSVIEW()...
                methodsview(object);
            else
                uiinspect(object);
            end
        catch
            try
                if isjava(object)
                    methodsview(object)
                else
                    methodsview(object.java);
                end
            catch
                % Never mind...
                dispError
            end
        end
    end

Callback function for for updates button

    function btCheckFex_Callback(src, evd, varargin)  %#ok
        try
            % Check the FileExchange for the latest version
            web('http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=14317');
        catch
            % Never mind...
            dispError
        end
    end

Check for existence of a newer version

    function checkVersion()
        try
            % If the user has not indicated NOT to be informed
            if ~ispref('FindJObj','dontCheckNewerVersion')

                % Get the latest version date from the File Exchange webpage
                baseUrl = 'http://www.mathworks.com/matlabcentral/fileexchange/';
                webUrl = [baseUrl '14317'];  % 'loadFile.do?objectId=14317'];
                webPage = urlread(webUrl);
                modIdx = strfind(webPage,'>Updates<');
                if ~isempty(modIdx)
                    webPage = webPage(modIdx:end);
                    % Note: regexp hangs if substr not found, so use strfind instead...
                    %latestWebVersion = regexprep(webPage,'.*?>(20[\d-]+)</td>.*','$1');
                    dateIdx = strfind(webPage,'class="date">');
                    if ~isempty(dateIdx)
                        latestDate = webPage(dateIdx(end)+13 : dateIdx(end)+23);
                        try
                            startIdx = dateIdx(end)+27;
                            descStartIdx = startIdx + strfind(webPage(startIdx:startIdx+999),'<td>');
                            descEndIdx   = startIdx + strfind(webPage(startIdx:startIdx+999),'</td>');
                            descStr = webPage(descStartIdx(1)+3 : descEndIdx(1)-2);
                            descStr = regexprep(descStr,'</?[pP]>','');
                        catch
                            descStr = '';
                        end

                        % Get this file's latest date
                        thisFileName = which(mfilename);  %#ok
                        try
                            thisFileData = dir(thisFileName);
                            try
                                thisFileDatenum = thisFileData.datenum;
                            catch  % old ML versions...
                                thisFileDatenum = datenum(thisFileData.date);
                            end
                        catch
                            thisFileText = evalc('type(thisFileName)');
                            thisFileLatestDate = regexprep(thisFileText,'.*Change log:[\s%]+([\d-]+).*','$1');
                            thisFileDatenum = datenum(thisFileLatestDate,'yyyy-mm-dd');
                        end

                        % If there's a newer version on the File Exchange webpage (allow 2 days grace period)
                        if (thisFileDatenum < datenum(latestDate,'dd mmm yyyy')-2)

                            % Ask the user whether to download the newer version (YES, no, no & don't ask again)
                            msg = {['A newer version (' latestDate ') of FindJObj is available on the MathWorks File Exchange:'], '', ...
                                   ['\color{blue}' descStr '\color{black}'], '', ...
                                   'Download & install the new version?'};
                            createStruct.Interpreter = 'tex';
                            createStruct.Default = 'Yes';
                            answer = questdlg(msg,'FindJObj','Yes','No','No & never ask again',createStruct);
                            switch answer
                                case 'Yes'  % => Yes: download & install newer file
                                    try
                                        %fileUrl = [baseUrl '/download.do?objectId=14317&fn=findjobj&fe=.m'];
                                        fileUrl = [baseUrl '/14317?controller=file_infos&download=true'];
                                        %file = urlread(fileUrl);
                                        %file = regexprep(file,[char(13),char(10)],'\n');  %convert to OS-dependent EOL
                                        %fid = fopen(thisFileName,'wt');
                                        %fprintf(fid,'%s',file);
                                        %fclose(fid);
                                        [fpath,fname,fext] = fileparts(thisFileName);
                                        zipFileName = fullfile(fpath,[fname '.zip']);
                                        urlwrite(fileUrl,zipFileName);
                                        unzip(zipFileName,fpath);
                                        rehash;
                                    catch
                                        % Error downloading: inform the user
                                        msgbox(['Error in downloading: ' lasterr], 'FindJObj', 'warn');  %#ok
                                        web(webUrl);
                                    end
                                case 'No & never ask again'   % => No & don't ask again
                                    setpref('FindJObj','dontCheckNewerVersion',1);
                                otherwise
                                    % forget it...
                            end
                        end
                    end
                else
                    % Maybe webpage not fully loaded or changed format - bail out...
                end
            end
        catch
            % Never mind...
        end
    end

Get the first selected object (might not be the top one - depends on selection order)

    function object = getTopSelectedObject(jTree, root)
        try
            object = [];
            numSelections  = jTree.getSelectionCount;
            if numSelections > 0
                % Get the first object specified
                %object = jTree.getSelectionPath.getLastPathComponent.getUserObject;
                nodedata = get(jTree.getSelectionPath.getLastPathComponent,'userdata');
            else
                % Get the root object (container)
                %object = root.getUserObject;
                nodedata = get(root,'userdata');
            end
            object = nodedata.obj;
        catch
            % Never mind...
            dispError
        end
    end

Update component callback upon callbacksTable data change

    function tbCallbacksChanged(src, evd, object)
        persistent hash
        try
            % exit if invalid handle or already in Callback
            %if ~ishandle(src) || ~isempty(getappdata(src,'inCallback')) % || length(dbstack)>1  %exit also if not called from user action
            if isempty(hash), hash = java.util.Hashtable;  end
            if ~ishandle(src) || ~isempty(hash.get(src)) % || length(dbstack)>1  %exit also if not called from user action
                return;
            end
            %setappdata(src,'inCallback',1);  % used to prevent endless recursion   % can't use getappdata(src,...) because it fails on R2010b!!!
            hash.put(src,1);

            % Update the object's callback with the modified value
            modifiedColIdx = evd.getColumn;
            modifiedRowIdx = evd.getFirstRow;
            if modifiedColIdx==1 && modifiedRowIdx>=0  %sanity check - should always be true
                table = evd.getSource;
                %object = get(src,'userdata');
                cbName = strtrim(table.getValueAt(modifiedRowIdx,0));
                try
                    cbValue = strtrim(char(table.getValueAt(modifiedRowIdx,1)));
                    if ~isempty(cbValue) && ismember(cbValue(1),'{[@''')
                        cbValue = eval(cbValue);
                    end
                    if (~ischar(cbValue) && ~isa(cbValue, 'function_handle') && (iscom(object(1)) || iscell(cbValue)))
                        revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, '');
                    else
                        for objIdx = 1 : length(object)
                            if ~iscom(object(objIdx))
                                set(object(objIdx), cbName, cbValue);
                            else
                                cbs = object(objIdx).eventlisteners;
                                if ~isempty(cbs)
                                    cbs = cbs(strcmpi(cbs(:,1),cbName),:);
                                    object(objIdx).unregisterevent(cbs);
                                end
                                if ~isempty(cbValue)
                                    object(objIdx).registerevent({cbName, cbValue});
                                end
                            end
                        end
                    end
                catch
                    revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, lasterr)  %#ok
                end
            end
        catch
            % never mind...
        end
        %setappdata(src,'inCallback',[]);  % used to prevent endless recursion   % can't use setappdata(src,...) because it fails on R2010b!!!
        hash.remove(src);
    end

Revert Callback table modification

    function revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, errMsg)  %#ok
        try
            % Display a notification MsgBox
            msg = 'Callbacks must be a ''string'', or a @function handle';
            if ~iscom(object(1)),  msg = [msg ' or a {@func,args...} construct'];  end
            if ~isempty(errMsg),  msg = {errMsg, '', msg};  end
            msgbox(msg, ['Error setting ' cbName ' value'], 'warn');

            % Revert to the current value
            curValue = '';
            try
                if ~iscom(object(1))
                    curValue = charizeData(get(object(1),cbName));
                else
                    cbs = object(1).eventlisteners;
                    if ~isempty(cbs)
                        cbs = cbs(strcmpi(cbs(:,1),cbName),:);
                        curValue = charizeData(cbs(1,2));
                    end
                end
            catch
                % never mind... - clear the current value
            end
            table.setValueAt(curValue, modifiedRowIdx, modifiedColIdx);
        catch
            % never mind...
        end
    end  % revertCbTableModification

Get the Java positions of all HG text labels

    function labelPositions = getLabelsJavaPos(container)
        try
            labelPositions = [];

            % Ensure we have a figure handle
            try
                h = hFig;  %#ok unused
            catch
                hFig = getCurrentFigure;
            end

            % Get the figure's margins from the Matlab properties
            hg_labels = findall(hFig, 'Style','text');
            units = get(hFig,'units');
            set(hFig,'units','pixels');
            outerPos = get(hFig,'OuterPosition');
            innerPos = get(hFig,'Position');
            set(hFig,'units',units);
            margins = abs(innerPos-outerPos);

            figX = container.getX;        % =outerPos(1)
            figY = container.getY;        % =outerPos(2)
            %figW = container.getWidth;   % =outerPos(3)
            figH = container.getHeight;   % =outerPos(4)

            % Get the relevant screen pixel size
            %monitorPositions = get(0,'MonitorPositions');
            %for monitorIdx = size(monitorPositions,1) : -1 : 1
            %    screenSize = monitorPositions(monitorIdx,:);
            %    if (outerPos(1) >= screenSize(1)) % && (outerPos(1)+outerPos(3) <= screenSize(3))
            %        break;
            %    end
            %end
            %monitorBaseY = screenSize(4) - monitorPositions(1,4);

            % Compute the labels' screen pixel position in Java units ([0,0] = top left)
            for idx = 1 : length(hg_labels)
                matlabPos = getpixelposition(hg_labels(idx),1);
                javaX = figX + matlabPos(1) + margins(1);
                javaY = figY + figH - matlabPos(2) - matlabPos(4) - margins(2);
                labelPositions(idx,:) = [javaX, javaY, matlabPos(3:4)];  %#ok grow
            end
        catch
            % never mind...
            err = lasterror;  %#ok debug point
        end
    end

Traverse an HG container hierarchy and extract the HG elements within

    function traverseHGContainer(hcontainer,level,parent)
        try
            % Record the data for this node
            thisIdx = length(hg_levels) + 1;
            hg_levels(thisIdx) = level;
            hg_parentIdx(thisIdx) = parent;
            hg_handles(thisIdx) = handle(hcontainer);
            parentId = length(hg_parentIdx);

            % Now recursively process all this node's children (if any)
            %if ishghandle(hcontainer)
            try  % try-catch is faster than checking ishghandle(hcontainer)...
                allChildren = allchild(handle(hcontainer));
                for childIdx = 1 : length(allChildren)
                    traverseHGContainer(allChildren(childIdx),level+1,parentId);
                end
            catch
                % do nothing - probably not a container
                %dispError
            end

            % TODO: Add axis (plot) component handles
        catch
            % forget it...
        end
    end

Debuggable "quiet" error-handling

    function dispError
        err = lasterror;  %#ok
        msg = err.message;
        for idx = 1 : length(err.stack)
            filename = err.stack(idx).file;
            if ~isempty(regexpi(filename,mfilename))
                funcname = err.stack(idx).name;
                line = num2str(err.stack(idx).line);
                msg = [msg ' at <a href="matlab:opentoline(''' filename ''',' line ');">' funcname ' line #' line '</a>']; %#ok grow
                break;
            end
        end
        disp(msg);
        return;  % debug point
    end

ML 7.0 - compatible ischar() function

    function flag = ischar(data)
        try
            flag = builtin('ischar',data);
        catch
            flag = isa(data,'char');
        end
    end

Set up the uitree context (right-click) menu

    function jmenu = setTreeContextMenu(obj,node,tree_h)
          % Prepare the context menu (note the use of HTML labels)
          import javax.swing.*
          titleStr = getNodeTitleStr(obj,node);
          titleStr = regexprep(titleStr,'<hr>.*','');
          menuItem0 = JMenuItem(titleStr);
          menuItem0.setEnabled(false);
          menuItem0.setArmed(false);
          menuItem1 = JMenuItem('Export handle to findjobj_hdls');
          menuItem2 = JMenuItem('Export handle to...');
          menuItem3 = JMenuItem('Request focus (bring to front)');
          menuItem4 = JMenuItem('Flash component borders');
          menuItem5 = JMenuItem('Display properties & callbacks');
          menuItem6 = JMenuItem('Inspect object');

          % Set the menu items' callbacks
          set(handle(menuItem1,'CallbackProperties'), 'ActionPerformedCallback', {@btExport_Callback,{obj,'findjobj_hdls'}});
          set(handle(menuItem2,'CallbackProperties'), 'ActionPerformedCallback', {@btExport_Callback,{obj,[]}});
          set(handle(menuItem3,'CallbackProperties'), 'ActionPerformedCallback', {@requestFocus,obj});
          set(handle(menuItem4,'CallbackProperties'), 'ActionPerformedCallback', {@flashComponent,{obj,0.2,3}});
          set(handle(menuItem5,'CallbackProperties'), 'ActionPerformedCallback', {@nodeSelected,{tree_h,node}});
          set(handle(menuItem6,'CallbackProperties'), 'ActionPerformedCallback', {@btInspect_Callback,obj});

          % Add all menu items to the context menu (with internal separator)
          jmenu = JPopupMenu;
          jmenu.add(menuItem0);
          jmenu.addSeparator;
          handleValue=[];  try handleValue = double(obj); catch, end;  %#ok
          if ~isempty(handleValue)
              % For valid HG handles only
              menuItem0a = JMenuItem('Copy handle value to clipboard');
              set(handle(menuItem0a,'CallbackProperties'), 'ActionPerformedCallback', sprintf('clipboard(''copy'',%.99g)',handleValue));
              jmenu.add(menuItem0a);
          end
          jmenu.add(menuItem1);
          jmenu.add(menuItem2);
          jmenu.addSeparator;
          jmenu.add(menuItem3);
          jmenu.add(menuItem4);
          jmenu.add(menuItem5);
          jmenu.add(menuItem6);
    end  % setTreeContextMenu

Set the mouse-press callback

    function treeMousePressedCallback(hTree, eventData, tree_h)  %#ok hTree is unused
        if eventData.isMetaDown  % right-click is like a Meta-button
            % Get the clicked node
            clickX = eventData.getX;
            clickY = eventData.getY;
            jtree = eventData.getSource;
            treePath = jtree.getPathForLocation(clickX, clickY);
            try
                % Modify the context menu based on the clicked node
                node = treePath.getLastPathComponent;
                userdata = get(node,'userdata');
                obj = userdata.obj;
                jmenu = setTreeContextMenu(obj,node,tree_h);

                % TODO: remember to call jmenu.remove(item) in item callback
                % or use the timer hack shown here to remove the item:
                %    timerFcn = {@menuRemoveItem,jmenu,item};
                %    start(timer('TimerFcn',timerFcn,'StartDelay',0.2));

                % Display the (possibly-modified) context menu
                jmenu.show(jtree, clickX, clickY);
                jmenu.repaint;

                % This is for debugging:
                userdata.tree = jtree;
                setappdata(gcf,'findjobj_hgtree',userdata)
            catch
                % clicked location is NOT on top of any node
                % Note: can also be tested by isempty(treePath)
            end
        end
    end  % treeMousePressedCallback

Remove the extra context menu item after display

    function menuRemoveItem(hObj,eventData,jmenu,item) %#ok unused
        jmenu.remove(item);
    end  % menuRemoveItem

Get the title for the tooltip and context (right-click) menu

    function nodeTitleStr = getNodeTitleStr(obj,node)
        try
            % Display the full classname and object name in the tooltip
            %nodeName = char(node.getName);
            %nodeName = strrep(nodeName, '<HTML><i><font color="gray">','');
            %nodeName = strrep(nodeName, '</font></i></html>','');
            nodeName = char(getNodeName(obj,99));
            [objClass,objName] = strtok(nodeName);
            objName = objName(3:end-1);  % strip leading ( and trailing )
            if isempty(objName),  objName = '(none found)';  end
            nodeName = char(node.getName);
            objClass = char(obj.getClass.getName);
            nodeTitleStr = sprintf('<html>Class name: <font color="blue">%s</font><br>Text/title: %s',objClass,objName);

            % If the component is invisible, state this in the tooltip
            if ~isempty(strfind(nodeName,'color="gray"'))
                nodeTitleStr = [nodeTitleStr '<br><font color="gray"><i><b>*** Invisible ***</b></i></font>'];
            end
            nodeTitleStr = [nodeTitleStr '<hr>Right-click for context-menu'];
        catch
            % Possible not a Java object - try treating as an HG handle
            try
                handleValueStr = sprintf('#: <font color="blue"><b>%.99g<b></font>',double(obj));
                try
                    type = '';
                    type = get(obj,'type');
                    type(1) = upper(type(1));
                catch
                    if ~ishandle(obj)
                        type = ['<font color="red"><b>Invalid <i>' char(node.getName) '</i>'];
                        handleValueStr = '!!!</b></font><br>Perhaps this handle was deleted after this UIInspect tree was<br>already drawn. Try to refresh by selecting any valid node handle';
                    end
                end
                nodeTitleStr = sprintf('<html>%s handle %s',type,handleValueStr);
                try
                    % If the component is invisible, state this in the tooltip
                    if strcmp(get(obj,'Visible'),'off')
                        nodeTitleStr = [nodeTitleStr '<br><center><font color="gray"><i>Invisible</i></font>'];
                    end
                catch
                    % never mind...
                end
            catch
                % never mind... - ignore
            end
        end
    end  % getNodeTitleStr

Handle tree mouse movement callback - used to set the tooltip & context-menu

    function treeMouseMovedCallback(hTree, eventData)
          try
              x = eventData.getX;
              y = eventData.getY;
              jtree = eventData.getSource;
              treePath = jtree.getPathForLocation(x, y);
              try
                  % Set the tooltip string based on the hovered node
                  node = treePath.getLastPathComponent;
                  userdata = get(node,'userdata');
                  obj = userdata.obj;
                  tooltipStr = getNodeTitleStr(obj,node);
                  set(hTree,'ToolTipText',tooltipStr)
              catch
                  % clicked location is NOT on top of any node
                  % Note: can also be tested by isempty(treePath)
              end
          catch
              dispError;
          end
          return;  % denug breakpoint
    end  % treeMouseMovedCallback

Request focus for a specific object handle

    function requestFocus(hTree, eventData, obj)  %#ok hTree & eventData are unused
        % Ensure the object handle is valid
        if isjava(obj)
            obj.requestFocus;
            return;
        elseif ~ishandle(obj)
            msgbox('The selected object does not appear to be a valid handle as defined by the ishandle() function. Perhaps this object was deleted after this hierarchy tree was already drawn. Refresh this tree by selecting a valid node handle and then retry.','FindJObj','warn');
            beep;
            return;
        end

        try
            foundFlag = 0;
            while ~foundFlag
                if isempty(obj),  return;  end  % sanity check
                type = get(obj,'type');
                obj = double(obj);
                foundFlag = any(strcmp(type,{'figure','axes','uicontrol'}));
                if ~foundFlag
                    obj = get(obj,'Parent');
                end
            end
            feval(type,obj);
        catch
            % never mind...
            dispError;
        end
    end  % requestFocus
end  % FINDJOBJ

TODO TODO TODO

%{
- Enh: Improve performance - esp. expandNode() (performance solved in non-nteractive mode)
- Enh: Add property listeners - same problem in MathWork's inspect.m
- Enh: Display additional properties - same problem in MathWork's inspect.m
- Enh: Add axis (plot, Graphics) component handles
- Enh: Group callbacks according to the first word (up to 2nd cap letter)
- Enh: Add figure thumbnail image below the java tree (& indicate corresponding jObject when selected)
- Enh: scroll initially-selected node into view (problem because treenode has no pixel location)
- Fix: java exceptions when getting some fields (com.mathworks.mlwidgets.inspector.PropertyRootNode$PropertyListener$1$1.run)
- Fix: use EDT if available (especially in flashComponent)
%}
janis_gui_RespondToCycle
% Eventlistener for waitbar
classdef janis_gui_RespondToCycle < handle

    properties (SetAccess = protected)
        % Starting time
        Start;
        % the waitbar object
        h;
    end

   methods
      % Constructor, takes validator as input
      function obj = janis_gui_RespondToCycle(validator_obj)
         obj.h = waitbar(0, 'Please Wait', 'Name', 'Validation');
         posi = get(obj.h,'Position');
         posi(4) = posi(4) + 100;
         set(obj.h,'Position',posi);
         addlistener(validator_obj, 'cycle',@(src,evtdata)handleEvnt(obj,src,evtdata));
         obj.Start = tic;
      end

      % delete will delete this object and the waitbar.
      function delete(obj)
         delete(obj.h);
      end

      % updates the progressbar, computes remaining time and so on...
      function handleEvnt(obj,src,evtdata)
           timer = toc(obj.Start);
           obj.h = waitbar(src.actualRound/src.getRoundCount, obj.h, ...
           sprintf('Validation Round %d (%d)\n\n Performance:\nThis iteration: %3.1f  %% - Mean: %3.1f %%\n\nTime: \nRemaining: %4.0f sec. - Elapsed: %4.0f sec.\n\n',...
           src.actualRound, src.getRoundCount, ...
           src.classPerformance.LastCorrectRate*100, src.classPerformance.CorrectRate*100, ...
           (timer/src.actualRound)*(src.getRoundCount-src.actualRound), timer));
      end
   end
end
janis_gui_RespondToEnsemble
% extended waitbar Eventlistener for classifier ensemble
classdef janis_gui_RespondToEnsemble < janis_gui_RespondToCycle

    methods

        % just calls the original Eventlistener
        function obj = janis_gui_RespondToEnsemble(validator_obj)
            obj = obj@janis_gui_RespondToCycle(validator_obj);
        end

        % events treated like before, but a new line will be added,
        % displayed informationen of all the classifiers.
        function handleEvnt(obj,src,evtdata)
            if isa(src.patternParser.patternMat{1},'janis_validator')
                handleEvnt@janis_gui_RespondToCycle(obj,src,evtdata);
                hAxes = findobj(obj.h,'type','axes');
                hTitle = get(hAxes,'title');
                currentStr = get(hTitle,'String');
                currentStr = currentStr(1:end-1,:);
                singlePredictions = 'This iteration: ';
                for k=1:size(src.patternParser.patternMat,2)
                    singlePredictions = sprintf('%s %3d%% |', ...
                        singlePredictions, ...
                        src.patternParser.patternMat{k}.classPerformance.LastCorrectRate*100);
                end
                currentStr = strvcat(currentStr,singlePredictions);
                obj.h = waitbar(src.actualRound/src.getRoundCount, obj.h, ...
                    currentStr);
            end

        end
    end
end
janis_gui_RespondToPermutation
% Eventlistener, similar to normal waitbar, but for permutation test
classdef janis_gui_RespondToPermutation< handle

    properties (SetAccess = protected)
        Start;          % Starting time
        h;              % waitbar objet
        Round = 2;      % start with round 2
        Permutations;   % number of permutations
        pval = 1;       % current p-val (which starts with 1)
        lastPerformance;% last predicted perfomance
        appendStr = '';
    end

   methods
       % Constructor
      function obj = janis_gui_RespondToPermutation(permutations, initVal)
         obj.h = waitbar(0, 'Please Wait', 'Name', 'PermutationTest');
         posi = get(obj.h,'Position');
         posi(4) = posi(4) + 100;
         set(obj.h,'Position',posi);
         obj.Start = tic;
         obj.Permutations = permutations;
         obj.lastPerformance = initVal;
      end

      % add eventlistener
      function obj = listenTo(obj,validator_obj)
          addlistener(validator_obj, 'cycle',@(src,evtdata)handleEvnt(obj,src,evtdata));
      end

      % delete object and progressbar
      function delete(obj)
         delete(obj.h);
      end

      % handle the event = update progressbar
      function handleEvnt(obj,src,evtdata)
           timer = toc(obj.Start);
           obj.h = waitbar(obj.Round/obj.Permutations, obj.h, ...
           sprintf('%s\nPermutation Round %d (%d)\n\n p-Val: %1.4f  %% - last performance: %3.1f %%\n\nTime: \nRemaining: %5.1f min. - Elapsed: %5.1f min.\n\n',...
           obj.appendStr, ...
           obj.Round, obj.Permutations, ...
           obj.pval, obj.lastPerformance*100, ...
           ((timer/(obj.Round+src.actualRound/src.getRoundCount)))*(obj.Permutations-(obj.Round+(src.actualRound/src.getRoundCount)))/60, timer/60), obj.appendStr);
           obj.appendStr = [obj.appendStr '.'];
      end

      % update round
      function updateRound(obj, pval, lastPerformance)
           obj.appendStr = '';
           obj.Round = obj.Round + 1;
           obj.pval = pval;
           obj.lastPerformance = lastPerformance;
      end
   end
end
janis_gui_feature_options
function varargout = janis_gui_feature_options(varargin)
% JANIS_GUI_FEATURE_OPTIONS M-file for janis_gui_feature_options.fig
%      JANIS_GUI_FEATURE_OPTIONS, by itself, creates a new JANIS_GUI_FEATURE_OPTIONS or raises the existing
%      singleton*.
%
%      H = JANIS_GUI_FEATURE_OPTIONS returns the handle to a new JANIS_GUI_FEATURE_OPTIONS or the handle to
%      the existing singleton*.
%
%      JANIS_GUI_FEATURE_OPTIONS('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in JANIS_GUI_FEATURE_OPTIONS.M with the given input arguments.
%
%      JANIS_GUI_FEATURE_OPTIONS('Property','Value',...) creates a new JANIS_GUI_FEATURE_OPTIONS or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before janis_gui_feature_options_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to janis_gui_feature_options_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

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

% Last Modified by GUIDE v2.5 28-Feb-2013 12:15:59

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @janis_gui_feature_options_OpeningFcn, ...
                   'gui_OutputFcn',  @janis_gui_feature_options_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 janis_gui_feature_options is made visible.
function janis_gui_feature_options_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 janis_gui_feature_options (see VARARGIN)

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

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes janis_gui_feature_options wait for user response (see UIRESUME)
% uiwait(handles.figure1);
ud.preMap = varargin{3};
ud.proMap = varargin{4};
active = varargin{1};
default = varargin{2};
ud.default = default;
ud.active = active;
set(handles.listbox_pre_active,'String',active.preprocessors);
set(handles.listbox_pre_inactive,'String',active.available_pps);
set(handles.listbox_intra_active,'String',active.intravalidationPreprocessors);
set(handles.listbox_intra_inactive,'String',active.available_ips);
set(handles.figure1,'UserData',ud);
listbox_pre_active_Callback(handles.listbox_pre_active, eventdata, handles);
listbox_intra_active_Callback(handles.listbox_intra_active, eventdata, handles);
uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = janis_gui_feature_options_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
try
    ud = get(handles.figure1,'UserData');
    out{1} = get(handles.listbox_pre_active,'String');
    out{2} = get(handles.listbox_intra_active,'String');
    out{3} = 'never used';
    out{4} = get(handles.listbox_pre_inactive,'String');
    out{5} = get(handles.listbox_intra_inactive,'String');
    out{6} = ud.preMap;
    out{7} = ud.proMap;
    out{8} = handles;
    handles.output = out;
    varargout{1} = handles.output;
catch
    msgbox({'Selection has been canceled. ';'Will leave data unchanged.'});
    varargout{1} = [];
end


% --- Executes on button press in pushbutton_cancel.
function pushbutton_cancel_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_cancel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
set(handles.listbox_pre_active,'String',ud.active.preprocessors);
set(handles.listbox_pre_inactive,'String',ud.active.available_pps);
set(handles.listbox_intra_active,'String',ud.active.intravalidationPreprocessors);
set(handles.listbox_intra_inactive,'String',ud.active.available_ips);
uiresume;
return

% --- Executes on button press in pushbutton_appy.
function pushbutton_appy_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_appy (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
uiresume;
return


% --- Executes on button press in pushbutton_restore.
function pushbutton_restore_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_restore (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
set(handles.listbox_pre_active,'String',ud.default.preprocessors);
set(handles.listbox_pre_inactive,'String',ud.default.available_pps);
set(handles.listbox_intra_active,'String',ud.default.intravalidationPreprocessors);
set(handles.listbox_intra_inactive,'String',ud.default.available_ips);


function edit2_Callback(hObject, eventdata, handles)
% hObject    handle to edit2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit2 as text
%        str2double(get(hObject,'String')) returns contents of edit2 as a double


% --- Executes during object creation, after setting all properties.
function edit2_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox_intra_inactive.
function listbox_intra_inactive_Callback(hObject, eventdata, handles)
% hObject    handle to listbox_intra_inactive (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox_intra_inactive contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox_intra_inactive


% --- Executes during object creation, after setting all properties.
function listbox_intra_inactive_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox_intra_inactive (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_add_intra.
function pushbutton_add_intra_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_add_intra (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
toAdd_Idx = get(handles.listbox_intra_inactive,'Value');
inactive = get(handles.listbox_intra_inactive,'String');
if size(inactive) > 0
    toAdd_Str = inactive{toAdd_Idx};
    if size(toAdd_Str,1) > 0 && ~isempty(toAdd_Str)
        active = get(handles.listbox_intra_active,'String');
        active{size(active,1)+1} = toAdd_Str;
        inactive(toAdd_Idx) = [];
        set(handles.listbox_intra_active,'String',active);
        set(handles.listbox_intra_inactive,'String',inactive);
        set(handles.listbox_intra_active,'Value',1);
        set(handles.listbox_intra_inactive,'Value',1);
    end
end
listbox_intra_active_Callback(handles.listbox_intra_active, eventdata, handles);

% --- Executes on button press in pushbutton_rem_intra.
function pushbutton_rem_intra_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_rem_intra (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
toRem_Idx = get(handles.listbox_intra_active,'Value');
inactive = get(handles.listbox_intra_inactive,'String');
active = get(handles.listbox_intra_active,'String');
if (size(active) > 0)
toRem_Str = active{toRem_Idx};
    if size(toRem_Str,1) > 0 && ~isempty(toRem_Str)
        inactive{size(inactive,1)+1} = toRem_Str;
        active(toRem_Idx) = [];
        set(handles.listbox_intra_active,'String',active);
        set(handles.listbox_intra_inactive,'String',inactive);
        set(handles.listbox_intra_active,'Value',1);
        set(handles.listbox_intra_inactive,'Value',1);
    end
end
listbox_intra_active_Callback(handles.listbox_intra_active, eventdata, handles);

function listbox_pre_active_Callback(hObject, eventdata, handles)
% hObject    handle to listbox_pre_active (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of listbox_pre_active as text
%        str2double(get(hObject,'String')) returns contents of listbox_pre_active as a double
ud = get(handles.figure1,'UserData');
contents = get(hObject,'String');
if get(hObject,'Value') <= size(contents,1)
    marked = contents(get(hObject,'Value'));
    preObj = ud.preMap(marked{:});
    if preObj.isConfigurable
       set(handles.pushbutton_settings_pre,'Enable','on');
    else
       set(handles.pushbutton_settings_pre,'Enable','off');
    end
else
    set(handles.pushbutton_settings_pre,'Enable','off')
end

% --- Executes during object creation, after setting all properties.
function listbox_pre_active_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox_pre_active (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox_pre_inactive.
function listbox_pre_inactive_Callback(hObject, eventdata, handles)
% hObject    handle to listbox_pre_inactive (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox_pre_inactive contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox_pre_inactive



% --- Executes during object creation, after setting all properties.
function listbox_pre_inactive_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox_pre_inactive (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_add_pre.
function pushbutton_add_pre_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_add_pre (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
toAdd_Idx = get(handles.listbox_pre_inactive,'Value');
inactive = get(handles.listbox_pre_inactive,'String');
if size(inactive) > 0
toAdd_Str = inactive{toAdd_Idx};
    if size(toAdd_Str,1) > 0 && ~isempty(toAdd_Str)
        active = get(handles.listbox_pre_active,'String');
        active{size(active,1)+1} = toAdd_Str;
        inactive(toAdd_Idx) = [];
        set(handles.listbox_pre_active,'String',active);
        set(handles.listbox_pre_inactive,'String',inactive);
        set(handles.listbox_pre_active,'Value',1);
        set(handles.listbox_pre_inactive,'Value',1);
    end
end
listbox_pre_active_Callback(handles.listbox_pre_active, eventdata, handles);

% --- Executes on button press in pushbutton_rem_pre.
function pushbutton_rem_pre_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_rem_pre (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
toRem_Idx = get(handles.listbox_pre_active,'Value');
inactive = get(handles.listbox_pre_inactive,'String');
active = get(handles.listbox_pre_active,'String');
if (size(active)) > 0
    toRem_Str = active{toRem_Idx};
    if size(toRem_Str,1) > 0 && ~isempty(toRem_Str)
        inactive{size(inactive,1)+1} = toRem_Str;
        active(toRem_Idx) = [];
        set(handles.listbox_pre_active,'String',active);
        set(handles.listbox_pre_inactive,'String',inactive);
        set(handles.listbox_pre_active,'Value',1);
        set(handles.listbox_pre_inactive,'Value',1);
    end
end
listbox_pre_active_Callback(handles.listbox_pre_active, eventdata, handles);


function edit1_Callback(hObject, eventdata, handles)
% hObject    handle to edit1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit1 as text
%        str2double(get(hObject,'String')) returns contents of edit1 as a double


% --- Executes during object creation, after setting all properties.
function edit1_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox6.
function listbox6_Callback(hObject, eventdata, handles)
% hObject    handle to listbox6 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox6 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox6


% --- Executes during object creation, after setting all properties.
function listbox6_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox6 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- 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)


% --- 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)


% --- Executes on selection change in listbox_intra_active.
function listbox_intra_active_Callback(hObject, eventdata, handles)
% hObject    handle to listbox_intra_active (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox_intra_active contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox_intra_active
ud = get(handles.figure1,'UserData');
contents = get(hObject,'String');
if get(hObject,'Value') <= size(contents,1)
    marked = contents(get(hObject,'Value'));
    preObj = ud.proMap(marked{:});
    if preObj.isConfigurable
       set(handles.pushbutton_settings_pro,'Enable','on');
    else
       set(handles.pushbutton_settings_pro,'Enable','off');
    end
else
    set(handles.pushbutton_settings_pro,'Enable','off')
end

% --- Executes during object creation, after setting all properties.
function listbox_intra_active_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox_intra_active (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on key press with focus on pushbutton_add_pre and none of its controls.
function pushbutton_add_pre_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to pushbutton_add_pre (see GCBO)
% eventdata  structure with the following fields (see UICONTROL)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)
disp('bla')


% --- Executes on button press in pushbutton_settings_pro.
function pushbutton_settings_pro_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_settings_pro (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
contents = get(handles.listbox_intra_active,'String');
marked = contents(get(handles.listbox_intra_active,'Value'));
proObj = ud.proMap(marked{:});
proObj.uiconfigure;

% --- If Enable == 'on', executes on mouse press in 5 pixel border.
% --- Otherwise, executes on mouse press in 5 pixel border or over listbox_pre_active.
function listbox_pre_active_ButtonDownFcn(hObject, eventdata, handles)
% hObject    handle to listbox_pre_active (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
disp('Button Down');


% --- Executes on button press in pushbutton_settings_pre.
function pushbutton_settings_pre_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_settings_pre (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
contents = get(handles.listbox_pre_active,'String');
marked = contents(get(handles.listbox_pre_active,'Value'));
preObj = ud.preMap(marked{:});
preObj.uiconfigure;


% --- Executes on key press with focus on listbox_pre_active and none of its controls.
function listbox_pre_active_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to listbox_pre_active (see GCBO)
% eventdata  structure with the following fields (see UICONTROL)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)
disp('key press');


% --- Executes on button press in pushbutton_view_pro.
function pushbutton_view_pro_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_view_pro (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
contents = get(handles.listbox_intra_active,'String');
marked = contents(get(handles.listbox_intra_active,'Value'));
proObj = ud.proMap(marked{:});
msg = evalc('proObj.viewSettings');
msgbox(msg, ['type: ' class(proObj)]);


% --- Executes on button press in pushbutton_view_pre.
function pushbutton_view_pre_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_view_pre (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
contents = get(handles.listbox_pre_active,'String');
marked = contents(get(handles.listbox_pre_active,'Value'));
preObj = ud.preMap(marked{:});
msg = evalc('preObj.viewSettings');
msgbox(msg, ['type: ' class(preObj)]);
janis_gui_regExp
function varargout = janis_gui_regExp(varargin)
% JANIS_GUI_REGEXP M-file for janis_gui_regExp.fig
%      JANIS_GUI_REGEXP, by itself, creates a new JANIS_GUI_REGEXP or raises the existing
%      singleton*.
%
%      H = JANIS_GUI_REGEXP returns the handle to a new JANIS_GUI_REGEXP or the handle to
%      the existing singleton*.
%
%      JANIS_GUI_REGEXP('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in JANIS_GUI_REGEXP.M with the given input arguments.
%
%      JANIS_GUI_REGEXP('Property','Value',...) creates a new JANIS_GUI_REGEXP or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before janis_gui_regExp_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to janis_gui_regExp_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

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

% Last Modified by GUIDE v2.5 14-Feb-2011 15:31:17

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @janis_gui_regExp_OpeningFcn, ...
                   'gui_OutputFcn',  @janis_gui_regExp_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 janis_gui_regExp is made visible.
function janis_gui_regExp_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 janis_gui_regExp (see VARARGIN)

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

% Update handles structure
guidata(hObject, handles);
ud.firstSub = varargin{1};
set(handles.figure1,'UserData',ud);
% UIWAIT makes janis_gui_regExp wait for user response (see UIRESUME)
uiwait(handles.figure1);


% --- Outputs from this function are returned to the command line.
function varargout = janis_gui_regExp_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
out{1} = get(handles.edit_subDir,'String'); % subDir
out{2} = get(handles.edit_regExp,'String');; % regExp
out{3} = handles;
handles.output = out;
varargout{1} = handles.output;


% --- Executes on button press in pushbutton_subDir.
function pushbutton_subDir_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_subDir (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.figure1,'UserData');
firstSub = ud.firstSub;
subDir = uigetdir(firstSub);
if ischar(subDir)
   set(handles.edit_subDir,'String',subDir);
end



function edit_subDir_Callback(hObject, eventdata, handles)
% hObject    handle to edit_subDir (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_subDir as text
%        str2double(get(hObject,'String')) returns contents of edit_subDir as a double


% --- Executes during object creation, after setting all properties.
function edit_subDir_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_subDir (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



function edit_regExp_Callback(hObject, eventdata, handles)
% hObject    handle to edit_regExp (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_regExp as text
%        str2double(get(hObject,'String')) returns contents of edit_regExp as a double


% --- Executes during object creation, after setting all properties.
function edit_regExp_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_regExp (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_apply.
function pushbutton_apply_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_apply (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
uiresume;
return
janis_permutation
function varargout = janis_permutation(varargin)
% JANIS_PERMUTATION M-file for janis_permutation.fig
%      JANIS_PERMUTATION, by itself, creates a new JANIS_PERMUTATION or raises the existing
%      singleton*.
%
%      H = JANIS_PERMUTATION returns the handle to a new JANIS_PERMUTATION or the handle to
%      the existing singleton*.
%
%      JANIS_PERMUTATION('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in JANIS_PERMUTATION.M with the given input arguments.
%
%      JANIS_PERMUTATION('Property','Value',...) creates a new JANIS_PERMUTATION or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before janis_permutation_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to janis_permutation_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

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

% Last Modified by GUIDE v2.5 23-Feb-2011 12:50:52

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @janis_permutation_OpeningFcn, ...
                   'gui_OutputFcn',  @janis_permutation_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

function janis_permutation_OpeningFcn(hObject, eventdata, handles, varargin)
handles.output = hObject;
guidata(hObject, handles);
uiwait(handles.figure1);

function varargout = janis_permutation_OutputFcn(hObject, eventdata, handles)
out{1} = str2num(get(handles.edit_count,'String'));
out{2} = handles;
handles.output = out;
varargout{1} = handles.output;

function slider1_Callback(hObject, eventdata, handles)
set(handles.edit_count,'String',num2str(round(get(handles.slider1,'Value'))));

function slider1_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function pushbutton_go_Callback(hObject, eventdata, handles)
uiresume;
return

function edit_count_Callback(hObject, eventdata, handles)
set(handles.slider1,'Value',str2num(get(handles.edit_count,'String')));


% --- Executes during object creation, after setting all properties.
function edit_count_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end
pathchooser
function varargout = pathchooser(varargin)
% PATHCHOOSER M-file for pathchooser.fig
%      PATHCHOOSER, by itself, creates a new PATHCHOOSER or raises the existing
%      singleton*.
%
%      H = PATHCHOOSER returns the handle to a new PATHCHOOSER or the handle to
%      the existing singleton*.
%
%      PATHCHOOSER('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in PATHCHOOSER.M with the given input arguments.
%
%      PATHCHOOSER('Property','Value',...) creates a new PATHCHOOSER or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before pathchooser_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to pathchooser_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

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

% Last Modified by GUIDE v2.5 18-Jan-2013 13:54:10

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @pathchooser_OpeningFcn, ...
                   'gui_OutputFcn',  @pathchooser_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

% func. to switch selected entries
function switchEntries(lb_source, lb_dest)
content = get(lb_source, 'String');
inds = get(lb_source, 'Value');
if ~isempty(content)
    old = get(lb_dest, 'String');
    new = [old; content(inds)];
    set(lb_dest,'Value',[]);
    set(lb_dest,'String',new);
    content(inds) = [];
    set(lb_source,'Value',[]);
    set(lb_source,'String',content);
end

function [list inds] = upEntries(list,inds)
if inds(1)>1
    for i=inds
        MoveDown=list(i-1);
        list(i-1)=list(i);
        list(i)=MoveDown;
    end
    inds = inds -1;
end

function [list inds] = downEntries(list,inds)
if inds(end)<size(list,1)
    for i=fliplr(inds)
        MoveDown=list(i+1);
        list(i+1)=list(i);
        list(i)=MoveDown;
    end
    inds = inds+1;
end

% --- Executes just before pathchooser is made visible.
function pathchooser_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 pathchooser (see VARARGIN)

% Choose default command line output for pathchooser


%handles.output = hObject;

% Update handles structure
%guidata(hObject, handles);

% UIWAIT makes pathchooser wait for user response (see UIRESUME)
tmpdir =  varargin{1};
ud.baseDir = tmpdir{:};
ud.modus = varargin{2};
set(handles.pathchooser,'UserData',ud);
ud = get(handles.pathchooser,'UserData');
ud.pattern = cellstr([repmat('class ',26,1), num2str([1:26]')]);
ud.folders = cell(1,26);
baseDir = ud.baseDir;
dirContent = dir(baseDir);
contentNames = {dirContent.name};
if strcmp(ud.modus,'dirs')
    contentNames = contentNames(vertcat(dirContent.isdir));
elseif strcmp(ud.modus,'files')
    pat = {'.*.(img|nii|gz)'};
    content = regexp(contentNames,pat,'match');
    imgIdx = ~cellfun(@isempty,content);
    contentNames = vertcat(content{imgIdx});
end
set(handles.listbox4,'String', contentNames);
set(handles.pathchooser,'UserData', ud);
if size(varargin,2) > 3
    ud.pattern(1:length(varargin{3})) = varargin{4};
    ud.folders(1:length(varargin{3})) = varargin{3};
    set(handles.pathchooser,'UserData', ud);
    set(handles.slider1,'Value',length(varargin{3}));
    slider1_Callback(hObject, eventdata, handles);
    pulldown_c1_Callback(hObject, eventdata, handles);
    pulldown_c2_Callback(hObject, eventdata, handles);
end
% if no reset was made
if size(varargin,2) ~= 3
    uiwait(handles.pathchooser);
end
updateMemberCount(handles);

% --- Outputs from this function are returned to the command line.
function varargout = pathchooser_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;
%global baseDir;
try
ud = get(handles.pathchooser,'UserData');
baseDir = ud.baseDir;
classCount = get(handles.slider1,'Value');
subjects = {ud.folders{1:classCount}};
classNames = ud.pattern(1:classCount);
out{1} = subjects;
out{2} = classNames;
out{3} = baseDir;
out{4} = ud.modus;
out{5} = handles;
handles.output = out;
varargout{1} = handles.output;
catch
    handles.output = 0;
    varargout{1} = handles.output;
end


% --- Executes on selection change in listbox1.
function listbox1_Callback(hObject, eventdata, handles)
% hObject    handle to listbox1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hints: contents = cellstr(get(hObject,'String')) returns listbox1 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox1


% --- Executes during object creation, after setting all properties.
function listbox4_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
%global baseDir;
%baseDir = uigetdir(pwd, 'Select Basedirectory of study');

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


% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
switchEntries(handles.listbox4, handles.listbox5);
% 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)
updateUserData(handles,2);

% --- Executes on button press in pushbutton2.
function pushbutton2_Callback(hObject, eventdata, handles)
switchEntries(handles.listbox5, handles.listbox4);
% 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)
updateUserData(handles,2);

% --- Executes on selection change in listbox2.
function listbox2_Callback(hObject, eventdata, handles)
% hObject    handle to listbox2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox2 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox2


% --- Executes during object creation, after setting all properties.
function listbox2_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox3.
function listbox3_Callback(hObject, eventdata, handles)
% hObject    handle to listbox3 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox3 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox3


% --- Executes during object creation, after setting all properties.
function listbox3_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox3 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox4.
function listbox4_Callback(hObject, eventdata, handles)
% hObject    handle to listbox4 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox4 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox4


% --- Executes during object creation, after setting all properties.
function listbox5_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox4 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in listbox5.
function listbox5_Callback(hObject, eventdata, handles)
% hObject    handle to listbox5 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns listbox5 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from listbox5


% --- Executes during object creation, after setting all properties.
function listbox1_CreateFcn(hObject, eventdata, handles)
% hObject    handle to listbox5 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: listbox controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton3.
function pushbutton3_Callback(hObject, eventdata, handles)
switchEntries(handles.listbox3, handles.listbox4);
% 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)
updateUserData(handles,1);

% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
switchEntries(handles.listbox4, handles.listbox3);
% 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)
updateUserData(handles,1);

% --- Executes on key press with focus on pushbutton4 and none of its controls.
function pushbutton4_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to pushbutton4 (see GCBO)
% eventdata  structure with the following fields (see UICONTROL)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)


% --- Executes on button press in pushbutton5.
function pushbutton5_Callback(hObject, eventdata, handles)
%global baseDir;
ud = get(handles.pathchooser,'UserData');
baseDir = ud.baseDir;
classCount = get(handles.slider1,'Value');
subjects = {ud.folders{1:classCount}};
classNames = ud.pattern(1:classCount);
out{1} = subjects; %get(handles.listbox3, 'String');
out{2} = classNames; %get(handles.listbox5, 'String');
out{3} = baseDir;
out{4} = ud.modus;
out{5} = handles;
if size(unique(cellfun(@(x) size(x,1),out{1})),2) > 1
    answer = questdlg(['The classes are unbalanced! This means the amount', ...
    ' of subjects in class 1 is not equal to the amount of class2. This could ', ...
    ' lead to unfeasible classifications. Nevertheless it could make sense, if you', ...
    ' aware of this fact and you know what you are doing. Do you want to continue?'], ...
    'Attention!', 'Yes', 'No','No');
    if strcmp(answer,'No')
        return
    end
end
if ~isempty(find((cellfun(@(x) size(x,1),out{1}))==0))
    helpdlg('You should choose some subjects, otherwise it will not work.','No subjects selected');
    return
end
handles.output = out;
pathchooser_OutputFcn(hObject, eventdata, handles);
uiresume;
return
% hObject    handle to pushbutton5 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


% --- Executes on button press in pushbutton7.
function pushbutton7_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton7 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
funPath = mfilename('fullpath');
helpFile = [funPath(1:end - size(mfilename,2)), 'help.html'];
web(helpFile);

% --- Executes on button press in pushbuttonReset.
function pushbuttonReset_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonReset (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
%global baseDir;
%baseDir = uigetdir(pwd, 'Select Basedirectory of study');
ud = get(handles.pathchooser,'UserData');
oldBaseDir = ud.baseDir;
baseDir = {uigetdir(oldBaseDir, 'Select New Directory')};
set(handles.listbox3,'String', []);
set(handles.listbox5,'String', []);
pathchooser_OpeningFcn(hObject, eventdata, handles, baseDir, ud.modus,'reset');

% --- Executes on button press in pushbuttonQuit.
function pushbuttonQuit_Callback(hObject, eventdata, handles)
% hObject    handle to pushbuttonQuit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
%try
%figure1_CloseRequestFcn(handles.pathchooser, eventdata, handles)
%catch
close;
%end
return


% --- Executes when user attempts to close pathchooser.
function pathchooser_CloseRequestFcn(hObject, eventdata, handles)
% hObject    handle to pathchooser (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: delete(hObject) closes the figure
delete(hObject);


% --- Executes on button press in up1.
function up1_Callback(hObject, eventdata, handles)
% hObject    handle to up1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
vals = get(handles.listbox3,'Value');
list = get(handles.listbox3,'String');
if ~isempty(list) && ~isempty(vals)
[list vals] = upEntries(list, vals);
set(handles.listbox3,'String',list);
set(handles.listbox3,'Value', vals);
end
updateUserData(handles,1);


% --- Executes on button press in down1.
function down1_Callback(hObject, eventdata, handles)
% hObject    handle to down1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
vals = get(handles.listbox3,'Value');
list = get(handles.listbox3,'String');
if ~isempty(list) && ~isempty(vals)
[list vals] = downEntries(list, vals);
set(handles.listbox3,'String',list);
set(handles.listbox3,'Value', vals);
end
updateUserData(handles,1);

% --- Executes on button press in up2.
function up2_Callback(hObject, eventdata, handles)
% hObject    handle to up2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
vals = get(handles.listbox5,'Value');
list = get(handles.listbox5,'String');
if ~isempty(list) && ~isempty(vals)
[list vals] = upEntries(list, vals);
set(handles.listbox5,'String',list);
set(handles.listbox5,'Value', vals);
end
updateUserData(handles,2);

% --- Executes on button press in down2.
function down2_Callback(hObject, eventdata, handles)
% hObject    handle to down2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
vals = get(handles.listbox5,'Value');
list = get(handles.listbox5,'String');
if ~isempty(list) && ~isempty(vals)
[list vals] = downEntries(list, vals);
set(handles.listbox5,'String',list);
set(handles.listbox5,'Value', vals);
end
updateUserData(handles,2);


% --- Executes on button press in selected.
function selected_Callback(hObject, eventdata, handles)
% hObject    handle to selected (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.pushbutton1,'Enable', 'off');
set(handles.pushbutton2,'Enable', 'off');
set(handles.pushbutton3,'Enable', 'off');
set(handles.pushbutton4,'Enable', 'off');
set(handles.selected,'Enable', 'off');
set(handles.pushbutton5,'Enable', 'on');
set(handles.up1,'Enable', 'on');
set(handles.up2,'Enable', 'on');
set(handles.down1,'Enable', 'on');
set(handles.down2,'Enable', 'on');


% --- Executes on button press in pushbutton_xls.
function pushbutton_xls_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_xls (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
 ud = get(handles.pathchooser,'UserData');
 [filename, pathname, filterindex] = uigetfile({'*.xls;*.csv'}, 'Pick an Excel or CSV File');
 if strcmp(filename(end-2:end),'csv')
    data = textread([pathname filename],'%s','delimiter','\n');
    lines = cellfun(@(x) strsplit(',',x),data,'UniformOutput',false);
    classNames =  vertcat(lines{1})';
    cols = vertcat(lines{2:end});
    for k=1:size(cols,2)
       classes{k} = cols(~cellfun(@isempty,cols(:,k)),k);
    end
 elseif strcmp(filename(end-2:end),'xls')
    [a b] = xlsread([pathname filename]);
    classNames = b(1,:)';
    for k=1:size(b,2)
        classes{k} = b(2:end,k);
    end
 else
     error('Unknown Fileformat');
 end
 ud.pattern(1:length(classNames)) = classNames;
 ud.folders(1:length(classes)) = classes;
 set(handles.pathchooser,'UserData', ud);
 set(handles.slider1,'Value',length(classes));
 slider1_Callback(hObject, eventdata, handles);
 pulldown_c1_Callback(hObject, eventdata, handles);
 updateUserData(handles,3);




% --- Executes on button press in save.
function save_Callback(hObject, eventdata, handles)
% hObject    handle to save (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.pathchooser,'UserData');
ccount = get(handles.slider1,'Value');
members = ud.folders(1:ccount);
[filename, pathname] = uiputfile('*.csv', 'Save Groups as csv');
fn = [pathname filename];
fid = fopen(fn,'wt');
k = repmat({''},max(cellfun(@(x) size(x,1),members))+1,length(members));
k(1,:) = ud.pattern(1:length(members))';
for counter=1:length(members)
  k(2:length(members{counter})+1,counter) = members{counter};
end
try
    fopen(fn,'wt');
    for c=1:size(k,1)
        fprintf(fid, '%s\n',[sprintf('%s,',k{c,1:end-1}),k{c,end}]);
    end
    fclose(fid);
catch ex
    fclose(fid);
    error('Could not write file');
end


% --- Executes on slider movement.
function slider1_Callback(hObject, eventdata, handles)
% hObject    handle to slider1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.pathchooser,'UserData');
set(handles.edit_classCount,'String',num2str(round(get(handles.slider1,'Value'))));
set(handles.pulldown_c1,'Value',min(get(handles.slider1,'Value'),get(handles.pulldown_c1,'Value')));
set(handles.pulldown_c2,'Value',min(get(handles.slider1,'Value'),get(handles.pulldown_c2,'Value')));
set(handles.pulldown_c1,'String',ud.pattern(1:get(handles.slider1,'Value'),:));
set(handles.pulldown_c2,'String',ud.pattern(1:get(handles.slider1,'Value'),:));
pulldown_c1_Callback(hObject, eventdata, handles)
pulldown_c2_Callback(hObject, eventdata, handles)
% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider


% --- Executes during object creation, after setting all properties.
function slider1_CreateFcn(hObject, eventdata, handles)
% hObject    handle to slider1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end


% --- Executes on selection change in pulldown_c2.
function pulldown_c2_Callback(hObject, eventdata, handles)
% hObject    handle to pulldown_c2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.pathchooser,'UserData');
currentVal = get(handles.pulldown_c2,'Value');
currentStr = ud.folders{1,currentVal};
if ~isempty(currentStr)
    set(handles.listbox5, 'String',currentStr);
else
    set(handles.listbox5, 'String','');
end
updateMemberCount(handles);
set(handles.listbox5, 'Value',1);
% Hints: contents = cellstr(get(hObject,'String')) returns pulldown_c2 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from pulldown_c2


% --- Executes during object creation, after setting all properties.
function pulldown_c2_CreateFcn(hObject, eventdata, handles)
% hObject    handle to pulldown_c2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in pulldown_c1.
function pulldown_c1_Callback(hObject, eventdata, handles)
% hObject    handle to pulldown_c1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
ud = get(handles.pathchooser,'UserData');
currentVal = get(handles.pulldown_c1,'Value');
currentStr = ud.folders{1,currentVal};
if ~isempty(currentStr)
    set(handles.listbox3, 'String',currentStr);
else
    set(handles.listbox3, 'String','');
end
updateMemberCount(handles);
set(handles.listbox3, 'Value',1);
% Hints: contents = cellstr(get(hObject,'String')) returns pulldown_c1 contents as cell array
%        contents{get(hObject,'Value')} returns selected item from pulldown_c1


% --- Executes during object creation, after setting all properties.
function pulldown_c1_CreateFcn(hObject, eventdata, handles)
% hObject    handle to pulldown_c1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



function edit_classCount_Callback(hObject, eventdata, handles)
% hObject    handle to edit_classCount (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
set(handles.slider1,'Value',str2num(get(handles.edit_classCount,'String')));
% Hints: get(hObject,'String') returns contents of edit_classCount as text
%        str2double(get(hObject,'String')) returns contents of edit_classCount as a double


% --- Executes during object creation, after setting all properties.
function edit_classCount_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_classCount (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function updateUserData(handles,boxNumber)
ud = get(handles.pathchooser,'UserData');
currentVal1 = get(handles.pulldown_c1,'Value');
currentVal2 = get(handles.pulldown_c2,'Value');
str1 = get(handles.listbox3, 'String');
str2 = get(handles.listbox5, 'String');
if boxNumber == 1
    ud.folders{1,currentVal1} = str1;
    set(handles.listbox5, 'String',ud.folders{1,currentVal2});
    set(handles.listbox5,'Value',min(size(ud.folders{1,currentVal2},1),get(handles.listbox5, 'Value')));
elseif boxNumber == 2
    ud.folders{1,currentVal2} = str2;
    set(handles.listbox3,'Value',min(size(ud.folders{1,currentVal1},1),get(handles.listbox3, 'Value')));
    set(handles.listbox3, 'String',ud.folders{1,currentVal1});
else
    ud.folders{1,currentVal1} = str1;
    ud.folders{1,currentVal2} = str2;
end
set(handles.pathchooser,'UserData',ud);
updateMemberCount(handles);


% updates text under listbox, which shows actual amount of members
function updateMemberCount(handles)
try
ud = get(handles.pathchooser,'UserData');
currentVal1 = get(handles.pulldown_c1,'Value');
currentVal2 = get(handles.pulldown_c2,'Value');
subjects = ud.folders;
set(handles.text_members1,'String',[num2str(length(subjects{currentVal1})) ' members']);
set(handles.text_members2,'String',[num2str(length(subjects{currentVal2})) ' members']);
set(handles.pathchooser,'UserData',ud);
catch
    msgbox({'Selection has been canceled. ';'Will leave data unchanged.'});
end

% --- Executes on button press in pushbutton_rename1.
function pushbutton_rename1_Callback(hObject, eventdata, handles)
ud = get(handles.pathchooser,'UserData');
ud.pattern = cellstr(ud.pattern);
currentVal1 = get(handles.pulldown_c1,'Value');
newName = inputdlg('Rename','Enter new Name',1,{ud.pattern{currentVal1}});
if ~isempty(newName)
    ud.pattern{currentVal1} = newName{:};
    set(handles.pathchooser,'UserData',ud);
    slider1_Callback(hObject, eventdata, handles)
end
% hObject    handle to pushbutton_rename1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)


% --- Executes on button press in pushbutton_rename2.
function pushbutton_rename2_Callback(hObject, eventdata, handles)
ud = get(handles.pathchooser,'UserData');
ud.pattern = cellstr(ud.pattern);
currentVal1 = get(handles.pulldown_c2,'Value');
newName = inputdlg('Rename','Enter new Name',1,{ud.pattern{currentVal1}});
if ~isempty(newName)
    ud.pattern{currentVal1} = newName{:};
    set(handles.pathchooser,'UserData',ud);
    slider1_Callback(hObject, eventdata, handles)
end
% hObject    handle to pushbutton_rename2 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
janis
% JANIS - Just another neuroimaging software
% author: Dominik Grotegerd
% Neuroimaging Group @University Hospital Münster
% License: BSD
%
% Commands:
%   janis                  - starts JANIS Graphical User Interface
%   janis('version')       - returns actual version number
%   janis('license')       - returns licence
%
% Version Changes          -> changelog.txt
% Documentation            -> Directory: doc
% third party software     -> Directory: extlib
function varargout = janis(varargin)

% Constants
versionNr = '2.2';
versionYear = '2013';
author = 'Dominik Grotegerd';
institute = 'Neuroimaging Group, Dep. of Psychiatry, University Hospital Muenster, Germany';
groupURL = 'http://www.uni-muenster.de/neuroimage';


[janis_path ign] = fileparts(which('janis'));
% setup path & launch GUI
if isempty(varargin)
    addpath(janis_path);
    addpath([janis_path filesep 'oo']);
    addpath([janis_path filesep 'gui']);
    addpath([janis_path filesep 'util']);
    addpath(genpath([janis_path filesep 'extlib']));
    janis_gui_main
% return infos
elseif isstr(varargin{1})
    switch varargin{1}
        case 'version'
            fprintf([janis('getVersionString') '\n']);
        case 'getVersionString'
            varargout{1} = ['JANIS version ' versionNr ' (' versionYear ')'];
        case 'license'
            lic = janis('getLicenseString');
            cellfun(@(x) fprintf([x '\n']),lic{:});
        case 'getLicenseString'
            fid = fopen([janis_path filesep 'license.txt']);
            varargout{1} = textscan(fid, '%s','delimiter', '\n');
            fclose(fid);
        case 'getAuthor'
            varargout{1} = author;
        case 'group'
            varargout{1} = institute;
        case 'www'
            varargout{1} = groupURL;
        otherwise
            error('Unkown option');
    end
else
    error('Unkown parameter');
end
janis_class
% A Pattern-Class which holds Members of a class
%  and works as a simple interface for all class Members
%
%
classdef janis_class < handle

    properties (SetAccess = public)
        % Human readable name for this class
        name
        testGroup;
    end

    properties (SetAccess = protected)
        % cell array of members
        members;
        sourceDimension = {};
    end

    methods

        % constructur with class name & optional the members of the class
        function obj = janis_class(className,varargin)
            obj.name = className;
            obj.members = {};
            obj.testGroup = false;
            if nargin > 1
                for memberCount=1:size(varargin,2)
                    obj = addMember(obj,varargin{memberCount});
                end
            end
        end

        % adds a new member to the class
        function this = addMember(this,newMember)
            if isa(newMember,'janis_pattern')
                this.members{size(this.members,1)+1,1} = newMember;
                if isempty(this.sourceDimension)
                    this.sourceDimension = newMember.sourceDimension;
                elseif ~isequal(newMember.sourceDimension,this.sourceDimension)
                    warning(['Image dimension of Member "' newMember.description '" does not match dimension of other members!']);
                end
            else
                throw(MException('janis:WrongPatternClass','This is not a janis pattern class file'));
            end
        end

        % returns the member with index "memberIndex"
        function member = getMember(this,memberIndex)
            if this.size > 0 && this.size >= memberIndex
                member = this.members{memberIndex};
            else
                throw(MException('janis:MemberIndexOutOfBounds','Index does not match class size'));
            end
        end

        % simply returns all Members
        function members = getAllMembers(this)
            members = this.members;
        end

        % returns amount of Members
        function s = size(this)
            s = size(this.members,1);
        end

        % if there are is one object not loaded or no subject at all
        % return false
        function loaded = allLoaded(this)
            if this.size > 0
                loaded = true;
                for mem=1:size(this.members,1)
                    loaded = this.members{mem}.isLoaded;
                end
            else
                loaded = false;
            end
        end


        % sets for all members the same reduction method "fsmethod"
        function this = setDimensionalitiyReduction(this,fsmethod)
            try
                for mem=1:size(this.members,1)
                    this.members{mem} = this.members{mem}.setFeatureSelection(fsmethod);
                end
            catch excep
                error('Seems not to be a correct dim. reduction method!')
                rethrow(excep);
            end
        end

        % returns true, if all reduction methods were set
        function tf = reductionMethodsSet(this)
            tf = true;
            try
                for mem=1:size(this.members,1)
                    this.members{mem}.getFeatureSelection;
                end
            catch
                tf = false;
            end
        end

        % Load all Pattern (and reload already loaded patterns)
        function this = loadAll(this)
            for mem=1:size(this.members,1)
                this.members{mem} = this.members{mem}.generatePattern;
            end
        end

        % returns the voxels of all subjects (by looking at the first
        % subject - there is NO validation if alle have the same Voxel
        % amount!!! therefore look at "isValid")
        function v = voxelsPerSub(this)
            if this.allLoaded
                v = this.members{1}.getVoxelCount;
            else
                v = 0;
            end
        end

        % should return true if everything is ready for computing:
        %   * feature reduction methods should be set
        %   * images should be loaded
        %   * there should be at least ONE subject.
        function valid = isValid(this)
            if this.allLoaded && this.reductionMethodsSet && this.size > 0
                valid = true;
                v = this.voxelsPerSub;
                for mem=1:this.size
                    if this.members{1}.getVoxelCount ~= v
                        valid = false;
                    end
                end

            else
                valid = false;
            end
        end

        % prints out a human readable Description of the Class
        function show(this)
            fprintf('\nClass:  ::: "%s" :::\n',this.name);
            fprintf('   Members:               %d\n',this.size);
            if this.reductionMethodsSet; r = 'Yes'; else r = 'No';
            end;
            if this.allLoaded; l = 'Yes'; else l = 'No';
            end;
            fprintf('   Reduction Methods set: %s\n', r);
            if l
                fprintf('   Images Loaded:         %s\n', l);
                fprintf('   Voxels per Subject:    %d\n', this.voxelsPerSub);
            end
        end

        % prints out a human readable list of class members
        function showMembers(this)
            fprintf('\nMembers of:  ::: "%s" :::\n',this.name);
            fprintf('   Index        Description\n')
            fprintf('   ---------------------------------------\n');
            for mem=1:size(this.members,1)
                fprintf('    %3d.:       %s\n', mem, this.members{mem}.description);
            end
            fprintf('   ---------------------------------------\n');
        end

        function markerList = getUniqueMarkers(this)
            markerList = cell(1,this.size);
            for mem=1:size(this.members,1)
                markers = struct;
                markerList{mem} = this.members{mem}.getUniqueMarkers(markers);
            end
        end
    end

end
janis_classCollection
% Collection of classes with simple interface for common functions to the
% patterns in the classes
%
classdef janis_classCollection < handle

    properties (SetAccess = protected)
        % cell of classes, which should be janis_class or a subclass
        classes = {};
        sourceDimension = {};
    end

    properties
        % human readable name or description
        description = 'untitled';
    end


    methods
        % adds a Class, throws exception if not a janis_class
        function this = addClass(this, newClass)
            if isa(newClass,'janis_class')
                this.classes{size(this.classes,1)+1,1} = newClass;
                if isempty(this.sourceDimension)
                    this.sourceDimension = newClass.sourceDimension;
                elseif ~isequal(newClass.sourceDimension,this.sourceDimension)
                    warning(['Image dimension of Member "' newMember.description '" does not match dimension of other members!']);
                end
            else
                throw(MException('janis:WrongClassClass','This is not a janis class type'));
            end
        end

        % returns class with index "idx"
        function jclass = get(this,idx)
            jclass = this.classes{idx};
        end

        % just inits the patterns
        function this = init(this)
            for count=1:size(this.classes,1)
                this.classes{count} = this.classes{count}.loadAll;
            end
        end

        % returns amount of Classes
        function s = size(this)
            s = size(this.classes,1);
        end

        % returns amount of all subjects over all classes.
        function p = patternCount(this)
            p = 0;
            for count=1:size(this.classes,1)
                p = p + this.classes{count}.size;
            end
        end

        % returns  true, if:
        %    * feature reduction methods should be set
        %    * images should be loaded
        %    * there should be at least ONE subject in every class
        %    * tehre should be at least two classes
        function valid = isValid(this)
            if this.size > 1
                valid = this.classes{1}.isValid;
                voxels = this.classes{1}.voxelsPerSub;
                for count=2:size(this.classes,1)
                    valid = valid && this.classes{count}.isValid && voxels == this.classes{count}.voxelsPerSub;
                end
            else
                valid = false;
            end
        end

        % returns  true, if:
        %    * all sizes of classes are equal
        % false, if
        %    * one class has a different amount of subjects
        function balanced = isBalanced(this)
            balanced = numel(unique(cellfun(@(x) x.size,this.classes))) == 1;
        end

        % Function, which switches the class labels of all members by random
        %     This is a nice helper method for a permutation test
        %     This returns a copy and does not modify the original the
        %     oringinal class collection!
        function thisCopy = shuffledCopy(this)
            thisCopy = janis_classCollection;
            thisCopy.description = this.description;
            allMembers = {};
            for c=1:size(this.classes,1)
                allMembers = {allMembers{:} this.classes{c}.members{:}};
            end
            indices = crossvalind('Kfold', size(allMembers,2), size(allMembers,2));
            counter = 0;
            for c=1:size(this.classes,1)
                permClass = janis_class([this.classes{c}.name ' (permuted)']);
                for m=1:size(this.classes{c}.members)
                    tmp = allMembers(1,indices((m + counter)));
                    permClass.addMember(tmp{:});
                end
                thisCopy = thisCopy.addClass(permClass);
                counter = counter + this.classes{c}.size;
            end

        end

        function thisCopy = copy(this)
            thisCopy = janis_classCollection;
            thisCopy.description = this.description;
            for c=1:size(this.classes,1)
                newClass = janis_class(this.classes{c}.name);
                for m=1:size(this.classes{c}.members)
                    tmp = this.classes{c}.getMember(m).copy;
                    newClass.addMember(tmp);
                end
                thisCopy = thisCopy.addClass(newClass);
            end
        end

        function this = setTestGroups(this,testIdx)
            for k=1:this.size
                this.classes{k}.testGroup = testIdx(k);
            end
        end

        % A human readable representation of whats goin' on.
        function show(this)
            fprintf('\nClass Collection:  ::: "%s" :::\n',this.description);
            if this.isValid; l = 'Yes'; else l = 'No';
            end;
            fprintf('   Valid structure:   %s\n', l);
            fprintf('   Classes:           %d\n', this.size);
            fprintf('   Subjects overall:  %d\n', this.patternCount);
            fprintf('\n');
            fprintf('   Index        Description     Subjects     Voxels per subject\n')
            fprintf('   ----------------------------------------------------------------\n');
            for cl=1:size(this.classes,1)
                fprintf('    %3d.:       %12s      %3d             %d\n', cl, this.classes{cl}.name, this.classes{cl}.size, this.classes{cl}.voxelsPerSub);
            end
            fprintf('   ----------------------------------------------------------------\n');
        end

        function clearData(this)
            for k=1:this.size
                for m=1:size(this.classes{k}.members)
                    this.classes{k}.getMember(m).freePattern;
                end
            end
        end

        function duplicates = checkDuplicates(this)
            vals = struct;
            for k=1:this.size
                markers = this.classes{k}.getUniqueMarkers;
                for mem=1:length(markers)
                    fields=fieldnames(markers{mem});
                    for f=1:length(fields)
                        singleMarker = getfield(markers{mem},fields{f});
                        if ~isfield(vals,fields{f})
                            vals = setfield(vals,fields{f},[]);
                        end
                        vals = setfield(vals,fields{f}, [getfield(vals,fields{f}); {singleMarker}]);
                    end
                end
            end
            fields=fieldnames(vals);
            duplicates = {};
            for k = 1:length(fields)
                s = getfield(vals,fields{k});
                if iscell(s{1})
                    b = horzcat(s{:});
                    c = b(:);
                    t = strvcat(c{:});
                else
                    t = strvcat(s{:});
                end
                [U I] = unique(t,'first','rows');
                dups = t;
                dups(I,:) = [];
                if ~isempty(dups)
                    duplicates =  [duplicates; {dups}];
                end
            end
        end

    end

end
janis_ensembleParser
% EnsembleParser does not parse the patterns, but classifiers (which act
% in an ensemble as a kind of pattern)%
classdef janis_ensembleParser < janis_patternParser

    methods
        % Constructor
        function pp = janis_ensembleParser(classCollection)
            pp = pp@janis_patternParser(classCollection);
            if ~isa(classCollection,'janis_classCollection')
                throw(MException('janis:WrongClass','This is not a janis_classCollection!'));
            end
        end

        % gets all Patterns from all subjects and generates a Matrix
        % which is used to return the a matrix by specified subjects
        function pp = init(pp)
            % I think I will nothing do here and provide instead a function
            % to add new classifiers
%            % init helper Matrices
%            % Patternsize: Voxels X Subjects
            pp.patternLabels = zeros(pp.classCollection.patternCount,1);
            actualIndex = 1;
%            % go through all classes
            for cl=1:pp.classCollection.size
               % for each class
               actualClass = pp.classCollection.get(cl);
               pp.patternLabels(actualIndex:actualIndex+actualClass.size-1) = cl -1;
               actualIndex = actualIndex + actualClass.size;
            end
            for k=1:size(pp.patternMat,2)
               pp.patternMat{k}.patternParser.init;
            end
        end

        % get patterns according to indices specified.
        function [trainPattern, trainLabels, testPattern, testLabels] = get(pp,indices)
             trainPattern = pp.patternMat;
             trainLabels = indices;
             testLabels  = ~indices;
             testPattern = testLabels;
        end

        % add new classifier/validator to ensemble.
        function pp = addValidatorToEnsemble(pp,validatorNode)
            % TODO: check Node for classCollection similar.
            if ~isa(validatorNode,'janis_validator')
                throw(MException('janis:WrongClass','This is not a validator-Class!'));
            else
                 pp.patternMat{size(pp.patternMat,2)+1} = validatorNode;
            end
        end

        % This is not (and probably will never) be implemented.
        function pattern = reconstructFromProcessors(pp,pattern)
            error('This function is not implemented!');
        end

        % returns labels of patterns
        function labelsAll = getLabels(pp)
            labelsAll = pp.patternLabels;
        end
    end
end
janis_ensemble_majorityVote
% Classifier, which takes binary Classifiers as input.
% Note: The patterns of different classifiers must(!) exactly be the same,
% there will bin no check.
classdef janis_ensemble_majorityVote < janis_classifier


    methods (Static)
        function shortName = name
            shortName = 'Classifier Ensemble: Majority Vote';
        end
        % special classifier, needs special setup, should not appear in
        % default list of classifiers
        function beVisbile = shouldBeVisible
            beVisbile = false;
        end
    end

    methods
        % For ensemble: trainPatterns are validators (any kind)
        function this = train(this,trainPatterns,trainLabels)
            classifierStruct.trainPatterns = trainPatterns;
            classifierStruct.trainLabels = trainLabels';
            this.classifierStruct = classifierStruct;
        end
        % testPatterns: indices of Patterns to test (which MUST be the same
        % in each classifier.

        function [predictions decFactors] = test(this,testPatterns)
            performances = cell(size(this.classifierStruct.trainPatterns,2));
            predictions = [];
            % for every classifier compute predicitions of specified
            % testPatterns.
            for valis=1:size(this.classifierStruct.trainPatterns,2)
                performances{valis} = this.classifierStruct.trainPatterns{valis}.computeForSubjects(testPatterns);
                predictions = [predictions; this.classifierStruct.trainPatterns{valis}.predicted(end,:)];
            end
            %resTemp = sum(predictions)/size(predictions,1);
            for sub=1:size(predictions,2)
                observed = unique(predictions(:,sub));     % observed predictions, unique
                [n,bins] = hist(double(predictions(:,sub)),double(observed));
                maxObs = max(n);               % maximal observation
                maxVals = bins(n==maxObs);
                % stupid fix for hist, which will not do anything for only
                % one value - grml hmpf
                if isempty(maxVals) && length(observed) == 1
                    maxVals = observed;
                end
                if (size(maxVals,1)>1)
                    meanDec = mean(performances{end,1}.decFactors(end).decVals);
                    if length(meanDec) < 3
                        resTemp(1,sub) = int32(mean(performances{end,1}.decFactors(end).decVals) > 0);
                    else
                        [val loc] = max(meanDec);
                        resTemp(1,sub) = loc;
                        warning('Patt situation in Multiclass - decision handling extremely experimental!');
                    end
                    warning('Patt situation - will decide on decision values instead of labels');
                else
                    resTemp(1,sub) = maxVals;
                end
                % decVals (fake, not for multiple stuff!)
                decFactors.decVals(sub) = mean((predictions(:,sub)-0.5)*2);
            end
            predictions = resTemp';
        end

        % return the classifierStruct (cell of validators)
        function classifierStruct  = getClassifier(this,trainPatterns,trainLabels)
            classifierStruct.trainPatterns = trainPatterns;
            classifierStruct.trainLabels = trainLabels;
        end

        % nothing to configure here
        function jc = uiconfigure(this)
            msgbox('Nothing to configure here.');
        end


        % show setup
        function show(this)
            show@janis_classifier(this);
        end
    end
end
janis_fs_append
% Dimensionality Reduction: Appending multiple images
%   varargin: List of images to append. Default "append"-dimension is 4
%   in other words: 3 dimensional images will be cat to a 4 Dimensional
%   image; two 4 Dimensional images will also be cat to a 4 Dimensional
%   image.
%
% Behavior can be changed be changing dimToCat
classdef janis_fs_append < janis_fs

   properties
      % human readable name
      name = 'Append';
      % Dimension, which is the reference for the append
      dimToCat = 4
   end

   methods
       % constructs from multiple images a single one, were the immages
       % were appended according to the specified dimension
       function img = reduce(fs,varargin)
           if nargin > 1
               if ~isnumeric(varargin{1})
                   throw(MException('janis:WrongImageFormat','Wrong image format for this method'));
               else
                   img = varargin{1};
                   if nargin > 2
                        for count = 2:size(varargin,2)
                           img = cat(fs.dimToCat, img, varargin{count});
                        end
                   end
               end
           else
              img = [];
              warning('janis:emptyImg','Image was empty!');
           end
       end
   end

end
janis_patternBeta
% Pattern Object of SPM-Beta-Images
%
%    images will be defined by using SPM.mat
%    pattern will be generated from specification of Beta-Images find in the
%    SPM.mat
classdef janis_patternBeta < janis_patternAbstractImg

   properties (SetAccess = public)
      ImgType = 'beta';
   end

    methods

        % Constructor
        %    takes
        %    * first location of the SPM.mat
        %    * second a list of Conditions as input, Strings which must match the
        %    specifications in the SPM.mat
        function obj = janis_patternBeta(SPMfile,listOfConditions)
            obj = obj@janis_patternAbstractImg;
            if nargin > 0
                obj.SPMfile = SPMfile;
                obj = addCondition(obj,SPMfile,listOfConditions);
            end
        end

        function obj = addCondition(obj,SPMfile,listOfConditions)
            obj.SPMfile = SPMfile;
            try
                load(SPMfile);
            catch excep
             fprintf('Unable to access spm.mat: %s\n', SPMfile);
             rethrow(excep);
            end
            try
                betaNames = {SPM.Vbeta.descrip};
                for condCount =1:numel(listOfConditions)
                    % find Index of Condition
                    condIdx = strcmpi(betaNames,listOfConditions{condCount});
                    if exist ( SPM.Vbeta(condIdx).fname)
                        obj = obj.addImage(SPM.Vbeta(condIdx).fname);
                    elseif exist([SPM.swd filesep SPM.Vbeta(condIdx).fname])
                        obj = obj.addImage([SPM.swd filesep SPM.Vbeta(condIdx).fname]);
                    else
                        throw(MException('janis:betaFileNotFound'));
                    end
                end
            catch excep
                % exception handling if no betas found
                    error('Unable to find all beta-images for subject: %s',SPMfile);
                    rethrow(excep);
            end
        end
    end
end
janis_patternCon
% Pattern Object: Contrast-Images (according to specification of SPM.mat)
%   pattern init by extracting pathes of con-Files
classdef janis_patternCon < janis_patternAbstractImg

   properties (SetAccess = public)
      ImgType = 'contrast';
   end

    methods
      %Constructor:
      % gets an SPM file and a list of conditions, which must match the
      % condition of con files in the SPM.mat
      function obj = janis_patternCon(SPMfile,listOfConditions)
            obj = obj@janis_patternAbstractImg;
            if nargin > 0
              obj.SPMfile = SPMfile;
              obj = addCondition(obj,SPMfile,listOfConditions);
            end
      end

        function obj = addCondition(obj,SPMfile,listOfConditions)
            obj.SPMfile = SPMfile;
            try
                load(SPMfile);
            catch excep
             fprintf('Unable to access spm.mat: %s\n', SPMfile);
             rethrow(excep);
            end
            try
                conNames = {SPM.xCon.Vcon};
                conNames = cell2mat(conNames);
                conNames = {conNames.descrip};
                for condCount =1:size(listOfConditions,1)
                    % find Index of Condition
                    conIdx = strcmpi(conNames,listOfConditions{condCount});
                    if exist(SPM.xCon(conIdx).Vcon.fname)
                        obj = obj.addImage(SPM.xCon(conIdx).Vcon.fname);
                    elseif exist([SPM.swd filesep SPM.xCon(conIdx).Vcon.fname])
                        obj = obj.addImage([SPM.swd filesep SPM.xCon(conIdx).Vcon.fname]);
                    elseif exist(SPM.xCon(conIdx).Vcon.descrip)
                        obj = obj.addImage(SPM.xCon(conIdx).Vcon.descrip);
                    else
                        throw(MException('janis:ConFileNotFound','Could not parse confile from SPM.mat'));
                    end
                end
            catch excep
                % exception handlig if no con found
                error('Unable to find all con-images for subject: %s',SPMfile);
                rethrow(excep);
            end
        end
    end

end
janis_patternMat
classdef janis_patternMat < janis_pattern

   properties (SetAccess = public)
       ImgType = 'image simple';
   end

  properties (SetAccess = protected)
      imgFiles;             % list of image files (mat-files!)
  end

  methods

       function obj = janis_patternMat(listOfImages)
            for imgCount =1:numel(listOfImages)
                try
                    obj = obj.addImage(listOfImages{imgCount});
                catch excep
                % exception handlig if no image found
                    error('Unable to find image: %s',listOfImages{imgCount});
                    rethrow(excep);
                end
            end
       end

           % adds a new image
        function obj = addImage(obj,image)
            obj.imgFiles{size(obj.imgFiles,2)+1} = image;
        end

        function jp = generatePattern(jp)
            for imgCount=1:numel(jp.imgFiles)
                if ~isempty(jp.imgFiles{imgCount})
                    try
                        tmp = load(jp.imgFiles{imgCount},'Y');
                        images{imgCount} = tmp.Y;
                    catch excep
                        error('Loading image failed: %s', jp.imgFiles{imgCount});
                        rethrow(excep);
                    end
                end
            end
            fs = getFeatureSelection(jp);
            images = fs.reduce(images{:});
            if ~isempty(images)
                jp.VoxelValues = images;
                jp.isLoaded = true;
            else
                warning('janis:emptyImg','Image was empty, something went wrong');
                jp.isLoaded = false;
            end
        end

  end


end
janis_patternSimple
% A pattern which gets image locations for initialization
% e.g. for T1 images or for beta-images if no spm-file is available.
%
% create new pattern with:
%       janis_patternSimple(listOfImages)
% ... where ´listOfImages´ is a cellarray containing pathes to files:
%       *.img,
%       *.nii, or
%       *.hdr
%
classdef janis_patternSimple < janis_pattern

   properties (SetAccess = public)
       ImgType = 'image simple';
   end

   properties (SetAccess = protected)
      imgFiles;             % list of image files
   end

  methods


    %Constructor:
    function obj = janis_patternSimple(listOfImages)
        obj = obj@janis_pattern;
        if nargin>0
            for imgCount =1:numel(listOfImages)
                try
                    obj = obj.addImage(listOfImages{imgCount});
                catch excep
                % exception handlig if no image found
                    error('Unable to find image: %s',listOfImages{imgCount});
                    rethrow(excep);
                end
            end
        end
    end

    function newJp = copy(this)
       newJp = copy@janis_pattern(this);
       newJp.imgFiles = this.imgFiles;
    end

    % adds a new image
    function this = addImage(this,image)
        this.imgFiles{size(this.imgFiles,2)+1} = image;
    end

    % generate pattern, i.e. load the images using
    %       spm_read_vol()  and
    %       spm_vol()
    function this = generatePattern(this)
        for imgCount=1:numel(this.imgFiles)
            if ~isempty(this.imgFiles{imgCount})
                try
                    images{imgCount} = spm_read_vols(spm_vol(this.imgFiles{imgCount}));
                catch excep
                    error('Loading image failed: %s', this.imgFiles{imgCount});
                    rethrow(excep);
                end
            end
        end
        this.sourceDimension = cellfun(@(x) size(x),images,'UniformOutput',false);
        fs = getFeatureSelection(this);
        images = fs.reduce(images{:});
        if ~isempty(images)
            this.VoxelValues = images;
            this.isLoaded = true;
        else
            warning('janis:emptyImg','Image was empty, something went wrong');
            this.isLoaded = false;
        end
    end

    function this = freePattern(this)
       this.VoxelValues = [];
       this.isLoaded = false;
    end

    function markers = getUniqueMarkers(this,markers)
        markers.imgFiles = this.imgFiles;
    end
  end
end
janis_pro_randomMask
classdef janis_pro_randomMask < janis_pre_roi & janis_processor

    properties (SetAccess = protected)
        nFeatures;                       % mask that schould be applied
        randomStream;
    end

    methods (Static)
        function desc = description
            desc = 'Generates random subspace ensemble for ensemble classification';
        end
        function shortName = name
            shortName = 'Random Mask';
        end
        function beVisible = shouldBeVisible
            beVisible = false;
        end
    end

    methods
        function bool = isConfigurable(this)
            bool = true;
        end

        function [patternTrain, patternTest] = operate(this, patternTrain, patternTest, trainLabels)
            this.randomStream.reset;
            mask = zeros(size(patternTrain,1),1);
            permIdx = randperm(this.randomStream,size(mask,1));
            randIdx = permIdx(1:this.nFeatures);
            mask(randIdx) = 1;
            this.mask = mask;
            patternTrain = patternTrain(logical(reshape(mask,numel(mask),1)),:);
            patternTest = patternTest(logical(reshape(mask,numel(mask),1)),:);
        end

        function this = setRandomStream(this,s)
            this.randomStream = s;
        end

        function this = reset(this)
            this.randomStream.reset;
        end

        function this = setNFeatures(this,nFeat)
            this.nFeatures = nFeat;
        end

        function new = copy(this)
            new = copy@janis_pre_roi(this);
            new.randomStream = this.randomStream;
            new.nFeatures = this.nFeatures;
        end

        function viewSettings(this)
            viewSettings@janis_processor(this);
            fprintf('  Random Seed:\t%i\n',this.randomStream.Seed);
            fprintf('  Number of Features:\t%i\n',this.nFeatures);
        end

    end

end
janis_simpleParser
% Default parser for ordinary patterns
classdef janis_simpleParser < janis_patternParser

    methods

        % Constructor
        function pp = janis_simpleParser(classCollection)
            pp = pp@janis_patternParser(classCollection);
        end

        % gets all Patterns from all subjects and generates a Matrix
        % which is used to return the a matrix by specified subjects
        function pp = init(pp)
           % init helper Matrices
           % Patternsize: Voxels X Subjects
           pp.patternMat = zeros(pp.classCollection.get(1).voxelsPerSub,pp.classCollection.patternCount);
           pp.patternLabels = zeros(pp.classCollection.patternCount,1);
           actualIndex = 1;
           % go through all classes
           for cl=1:pp.classCollection.size
              % for each class
              actualClass = pp.classCollection.get(cl);
              pp.patternLabels(actualIndex:actualIndex+actualClass.size-1) = cl -1;
              % go through all subjects
              for subject=1:actualClass.size
                  actualSubject = actualClass.getMember(subject);
                  vector = actualSubject.get;
                  try
                  pp.patternMat(:,actualIndex-1+subject) = vector;
                  catch
                      error(['Dimension missmatch in subject ' actualSubject.description]);
                  end
              end
              actualIndex = actualIndex + actualClass.size;
           end
           pp.patternMat = pp.applyProcessors(pp.patternMat);
        end

        % return specific pattern
        function [trainPattern, trainLabels, testPattern, testLabels] = get(pp,indices)
            trainPattern = pp.patternMat(:,indices);
            testPattern  = pp.patternMat(:,~indices);
            trainLabels = pp.patternLabels(indices);
            testLabels  = pp.patternLabels(~indices);
            for ivp=1:size(pp.intraValidationProcessors,2)
                pp.intraValidationProcessors{ivp}.preprocessorHistory = pp.preprocessors;
                [trainPattern, testPattern] = pp.intraValidationProcessors{ivp}.operate(trainPattern,testPattern,trainLabels);
            end
        end

        % call reconstruct Functions from Feature Selection methods in
        % reverse order they were applied
        function pattern = reconstructFromProcessors(pp,pattern)
            % go through reverse
            for ivp=size(pp.intraValidationProcessors,2):-1:1
                pattern = pp.intraValidationProcessors{ivp}.reconstruct(pattern);
            end
            for ivp=size(pp.preprocessors,2):-1:1
                pattern = pp.preprocessors{ivp}.reconstruct(pattern);
            end
            origDim = size(pp.classCollection.classes{1}.members{1}.VoxelValues);
            pattern = reshape(pattern,origDim);
        end

        % return all the labels
        function labelsAll = getLabels(pp)
            labelsAll = pp.patternLabels;
        end

        function new = copy(this)
           new =  copy@janis_patternParser(this);
        end
    end
end
janis_validatorLeaveOneOut
% Simple Leave One Out Crossvalidation
classdef janis_validatorLeaveOneOut < janis_validator

    methods
        % Constructor
        function obj = janis_validatorLeaveOneOut(classCollection, classifier,patternParser)
           obj = obj@janis_validator(classCollection, classifier,patternParser);
           if ~isa(classCollection,'janis_classCollection')
               throw(MException('janis:WrongClass','This is not a classCollection-Class!'));
           elseif  ~isa(classifier,'janis_classifier')
               throw(MException('janis:WrongClass','This is not a classifier-Class!'));
           else
               % simple: the number of rounds is the amount of subjects
               obj.rounds = classCollection.patternCount;
           end
        end

        % leave one out cross validation
        function results = runValidation(jv)
            Indices = 1:jv.rounds;
            jv.classPerformance = classperf(jv.patternParser.getLabels);
            for r=1:jv.rounds
                testIdx = (Indices == r);
                jv = jv.computeForSubjects(testIdx);
                jv.actualRound = r;
                notify(jv,'cycle');
                jv.roundPerformances = [jv.roundPerformances; jv.classPerformance.LastCorrectRate];
            end
            results = jv.classPerformance;
        end

        function clStruct = getClassifierForSubject(jv,subject)
            Indices = 1:jv.rounds;
            testIdx = (Indices == subject);
            [patternMat, trainLabels] = jv.patternParser.get(testIdx == 0);
            clStruct = jv.classifier.getClassifier(patternMat, trainLabels);
        end

        function jv = computeForSubjects(jv, testIdx)
               [patternMat, trainLabels, testMat, testLabels] = jv.patternParser.get(testIdx == 0);
               jv.classifier = jv.classifier.train(patternMat, trainLabels);
               [prediction decFactors] = jv.classifier.test(testMat);
               jv.classPerformance = classperf(jv.classPerformance,prediction,testIdx);
               jv.predicted = [jv.predicted; prediction'];
               decFactors.label = testLabels;
               jv.decFactors = [jv.decFactors decFactors];
        end


    end


end
janis_validatorMatched
% Leave One Out PER GROUP Cross Validation, Matched:
%    Assumes, that n-th subject of a group is paired with the n-th subject
%    of another group. (Also assumes, the groups are about the same size).
%    In every round n-th subject of each group is used as test set, the
%    remaining subjects are the training set.
classdef janis_validatorMatched < janis_validator

    methods

        % Constructor
        function obj = janis_validatorMatched(classCollection, classifier,patternParser)
           obj = obj@janis_validator(classCollection, classifier,patternParser);
           if ~isa(classCollection,'janis_classCollection')
               throw(MException('janis:WrongClass','This is not a classCollection-Class!'));
           %elseif (classCollection.size ~= 2)
           %    throw(MException('janis:WrongClassification','Can only binary classify'));
           elseif (classCollection.get(1).size ~= classCollection.get(2).size)
               throw(MException('janis:WrongMatching','class should be matched! But cannot, because subject count differs'));
           elseif  ~isa(classifier,'janis_classifier')
               throw(MException('janis:WrongClass','This is not a classifier-Class!'));
           elseif mod(classCollection.patternCount,classCollection.size) ~= 0
               throw(MException('janis:WrongMatching','Uhm, there seems to be something wrong with the number of subjects...'));
           else
               % rounds should be half of the amount of patterns, i.e. the
               % amount of subjects in each group.
               obj.rounds = classCollection.patternCount/classCollection.size;
           end
        end

        % runs the validation
        function results = runValidation(jv)
            Indices = 1:jv.rounds;
            jv.classPerformance = classperf(jv.patternParser.getLabels);
            for r=1:jv.rounds
                %testIdx = [(Indices == r) (Indices == r)];
                testIdx = repmat((Indices == r),1,jv.patternParser.classCollection.size);
                jv = jv.computeForSubjects(testIdx);
                jv.actualRound = r;
                notify(jv,'cycle');
                jv.roundPerformances = [jv.roundPerformances; jv.classPerformance.LastCorrectRate];
            end
            results = jv.classPerformance;
        end

        function clStruct = getClassifierForSubject(jv,subject)
            Indices = 1:jv.rounds;
            testIdx = [(Indices == subject) (Indices == subject)];
            [patternMat, trainLabels] = jv.patternParser.get(testIdx == 0);
            clStruct = jv.classifier.getClassifier(patternMat, trainLabels);
        end

        function jv = computeForSubjects(jv, testIdx)
                   leaveOuts = {};
                   for k=1:sum(testIdx)
                       leaveOuts{size(leaveOuts,1)+1,1} = jv.patternParser.classCollection.classes{k}.members{testIdx(1:length(testIdx)/sum(testIdx))}.description;
                   end
                   for h=1:size(jv.patternParser.intraValidationProcessors)
                      if isa(jv.patternParser.intraValidationProcessors{h},'janis_pro_2ndLevel')
                          jv.patternParser.intraValidationProcessors{h}.setLeaveOut(leaveOuts);
                      end
                   end
                   [patternMat, trainLabels, testMat, testLabels] = jv.patternParser.get(~testIdx);
                   jv.classifier = jv.classifier.train(patternMat, trainLabels);
                   [prediction decFactors] = jv.classifier.test(testMat);
                   jv.classPerformance = classperf(jv.classPerformance,prediction,testIdx);
                   jv.predicted = [jv.predicted; prediction'];
                   decFactors.label = testLabels;
                   jv.decFactors = [jv.decFactors decFactors];
        end

        function jv = exportCSV(jv,destFolder)
                [patternMat, trainLabels, testMat, testLabels] = jv.patternParser.get([1:(jv.patternParser.size)]);
                for k=1:size(patternMat,2)
                    toSave = patternMat(:,k);
                    save([destFolder filesep 'subject' num2str(k) '.txt'],'toSave','-ASCII');
                end

        end

        function new = copy(this)
            new = copy@janis_validator(this);
        end

    end
end
janis_validatorTestGroup
classdef janis_validatorTestGroup < janis_validator

   methods

        function obj = janis_validatorTestGroup(classCollection, classifier,patternParser)
           obj = obj@janis_validator(classCollection, classifier,patternParser);
           if ~isa(classCollection,'janis_classCollection')
               throw(MException('janis:WrongClass','This is not a classCollection-Class!'));
           elseif  ~isa(classifier,'janis_classifier')
               throw(MException('janis:WrongClass','This is not a classifier-Class!'));
           else
               % rounds should be half of the amount of patterns, i.e. the
               % amount of subjects in each group.
               obj.rounds = classCollection.patternCount/classCollection.size;
           end
        end


               % runs the validation
        function results = runValidation(this)
            % test all subjects in specified groups
            testIdx = cell2mat(cellfun(@(x) repmat(x.testGroup,x.size,1) ,this.patternParser.classCollection.classes,'UniformOutput',false));
            this.classPerformance = classperf(this.patternParser.getLabels);
            this = this.computeForSubjects(testIdx);
            this.actualRound = 1;
            notify(this,'cycle');
            this.roundPerformances = this.classPerformance.LastCorrectRate;
            results = this.classPerformance;
        end


       function this = computeForSubjects(this, testIdx)
                   for h=1:size(this.patternParser.intraValidationProcessors)
                      if isa(this.patternParser.intraValidationProcessors{h},'janis_pro_2ndLevel')
                          this.patternParser.intraValidationProcessors{h}.setLeaveOut(leaveOuts);
                      end
                   end
                   [patternMat, trainLabels, testMat, testLabels] = this.patternParser.get(~testIdx);
                   this.classifier = this.classifier.train(patternMat, trainLabels);
                   [prediction decFactors] = this.classifier.test(testMat);
                   groupCount = 1;
                   memberCount = 1;
                   for k=1:size(prediction,1)
                       if memberCount > this.patternParser.classCollection.classes{groupCount}.size
                           groupCount = groupCount + 1;
                           memberCount = 1;
                       end
                       while ~this.patternParser.classCollection.classes{groupCount}.testGroup
                          groupCount = groupCount + 1;
                          memberCount = 1;
                       end
                       subject = regexp(this.patternParser.classCollection.classes{groupCount}.members{memberCount}.description, filesep, 'split');
                       subject = subject{end}(end);
                       fprintf('subject %s predicted as: %d\n', subject{:}, prediction(k,1));
                       memberCount = memberCount + 1;
                   end
                   fprintf('-------------------------\n');
                   obs = unique(prediction);
                   for k=1:length(obs)
                        fprintf('%d as class %d\n',sum(prediction==obs(k)),obs(k));
                   end
                   testIdx = testIdx.*[1:length(testIdx)]';
                   this.classPerformance = classperf(this.classPerformance,prediction,testIdx(testIdx>0));
                   %jv.predicted = [jv.predicted; prediction'];
                   %decFactors.label = testLabels;
                   %jv.decFactors = [jv.decFactors decFactors];
        end


        function new = copy(this)
            new = copy@janis_validator(this);
        end

   end

end
extractDiscriminativeMap
function img = extractDiscriminativeMap (model, parser)

scaleFactor = 100;
%origSpace = info.patternSize;

%for class=0:1
    if isfield(model,'SupportVectors')
        svecs = model.SupportVectors; %(inds==class,:);
        coeffs = model.Alpha; %(inds==class,:);
    %img = cell(1,2);%(size(svecs, 1), 2);
    else
        svecs = model.SVs;
        coeffs = model.sv_coef;
    end
    res = zeros(1,size(svecs,2));
    for k=1:size(svecs, 1)

        vec = svecs(k,:);
        %vec(abs(vec) < max(abs(vec))*1/3) = 0;
        %res(~mask) = res(~mask) + (vec * coeffs(k))'; % + rho;
        res = res + vec * coeffs(k);
    end
    img = parser.reconstructFromProcessors(res);

img = img*scaleFactor/sum(max(abs(img(:))));
janisSubclasses
            function [subclassFiles, subclassNames] = janisSubclasses(ooDir,superclassName)
            subclassNames = {};
            subclassFiles = {};


            content = dir(ooDir);
            directories =  [content.isdir];
            fileList = {content(~directories).name};
            for k=1:numel(fileList)
                if ~isempty(strfind(fileList{k},'janis_')) && ~isempty(strfind(fileList{k},'.m')) && strfind(fileList{k},'.m') == length(fileList{k})-1
                    [ign, fName, ign2] = fileparts(fileList{k});
                    try
                        if ismember(superclassName,superclasses(fName))
                            tmp = eval(['?' fName]);
                            try
                            if ~tmp.Abstract
                                subclassFiles{length(subclassFiles)+1} = fName;
                                subclassNames{length(subclassNames)+1} = eval([fName '.name']);
                            end
                            catch exp2
                                if strcmp(exp2.identifier,'MATLAB:noSuchMethodOrField')
                                    if isempty(findobj([tmp.Methods{:}],'Abstract',true))
                                        subclassFiles{length(subclassFiles)+1} = fName;
                                        subclassNames{length(subclassNames)+1} = eval([fName '.name']);
                                    end
                                else
                                    rethrow(exp2)
                                end
                            end
                        end
                    catch exp
                        warning('newer Matlab version recommended!')
                    end
                end
            end
janis_util_updateHelp
function janis_util_updateHelp(src,event,jhelpBox)
currentObj = get(overobj2('flat','visible','on'));
if ~isempty(currentObj)
    htmlStr = getHelpText(currentObj.Tag);
    if ~isempty(htmlStr)
        jhelpBox.setText(htmlStr);
    end
end

function formated = formatStr(name,desc)
formated = ['<html><b>' name ':</b> ' desc '</html>'];

function desc = getHelpText(tag)
switch tag
%
%       % Workflow Toggle Buttons
%
    case 'toggle_members'
        desc = formatStr('Class & Members','Opens panel to setup and review classes and it''s members');
    case 'toggle_modality'
        desc = formatStr('Data Modalities','Options here are depending on the previous setup. For instance, if a directory structure was specified, it is possible to use data extracted from the SPM.mat or find images by regular expressions<br>In any case, you will have to push <i>Apply</i> in order to load the data.');
    case 'toggle_training'
        desc = formatStr('Training Options','Opens panel to setup pattern classification algorithm and Feature Selection');
    case 'toggle_ensembles'
        desc = formatStr('Ensembles','Managing classifier ensembles. This panel becomes only active, when a classifier ensembles has been crated.');
    case 'toggle_validationOptions'
        desc = formatStr('Validation Options','In this panel you are able to define groups which are exclusivly used for training or testing. It is only active, when ''Test Groups''-Options in the Trainig Options panel is selected.');
    case 'toggle_validation'
        desc = formatStr('Validation & Results','In this panel validation can be started. It contains also results as well as a confusion matrix.');
%
%      % class members panel
%
    case {'edit_classname1','edit_classname2'}
        desc = formatStr('Select class to view','This is just for reviewing your setup. Use <i>setup</i> to change this setup');
    case 'pushbutton_editClassMembers'
        desc = formatStr('Edit Class and Members','Edit a prevouisly defined setup');
    case {'listbox_members_c1','listbox_members_c2'}
        desc = formatStr('Members of a class','This is just for reviewing your setup. Use <i>setup</i> to change this setup');
%
%       % Data Modalities Panel
%
    case 'popupmenu_imgType'
        desc = formatStr('Select Image Type','Different options to select images are available<br><ul><li><b>Beta-Images</b> as found in SPM.mat files</li><li><b>Con-Images</b> as found in SPM.mat files</li><li><b>by RegExps:</b> Find images in subfolders, specified by a Regular Expression Pattern</li></ul>');
    case 'pushbutton_regExp'
        desc = formatStr('Sepcify','Click to specify a regular expression and subfolders');
    case 'listbox_regExp'
        desc = formatStr('Image List for Regular Expression','Shows images found by a regular expression for the first subject - so that you can check, if your regular expression works...');
    case 'pushbutton_specify_spm'
        desc = formatStr('Secify SPM.mat','In the following file selection dialog the SPM.mat-file for the first subject has to be selected. For the others subjects, the programm will know the location of the SPM.mat (if the structure of the directories is the same).');
    case 'edit_spm_file'
        desc = formatStr('Selected SPM.mat file','Shows the location of the SPM.mat of the first subject, for review purposes.');
    case 'listbox_trials'
        desc = formatStr('Conditions/Contrasts','Shows the available contrasts. Please mark the desired contrast by mouse click and click <i>Apply</i> afterwards. You can also mark multiple contrasts by using <i>CTRL</i> in combination with a mouse button press!');
    case 'pushbutton_apply'
        desc = formatStr('Apply','Will load images as specified and assign them to classes and members. <br><b>If something is changed about the prevouis setup, you have to reload the data by pushing apply again!</b>');
%
%       % Training Options
%
    case 'popupmenu_validator'
        desc = formatStr('Validation Menu','Select type of validation<br><ul><li><b>Leave one out per group cross validation</b> Simple cross validation, in each iteration one subject is used for testing, the others for training</li><li><b>Leave one out per group</b> like loocv, but for testing one subject of each group will be used (pairs). Of course this option requires, that in each class is the same amount of class members.</li><li><b>Test Groups</b> provides a training with specific groups and a simple testing / computation with remaining groups. On the panel <i>validation options</i> these groups can be specified - it will be enabled, if you select this option. For instance:<ul><li>Do a simple hold out, by splitting a group in two groups, say split groups A and B in A<sub>1</sub>, A<sub>2</sub>, B<sub>1</sub> and B<sub>2</sub> then train with <sub>1</sub>-groups and test with <sub>2</sub>-groups </li><li>If you have three groups (A,B,C): Train with A and B, and test with C to find out, if group C might look very similar to A or B.</li><li>You have two groups A and B and a single subject, whos class membership is unknown. Just setup a third group C and assign the single subject to this group. Then train with A and B, test with C.</li></ul></li></ul>');
    case 'popupmenu_classifiers'
        desc = formatStr('Classifier','Select a classifier. You can edit (or have edit, depending on the classifier) the settings of the classifier using the menu edit -> classifier settings. For a detailed description of each classifier, please have a look a the documentation.');
    case 'pushbutton_fs'
        desc = formatStr('Setup Feature Selection','Opens window to setup data modifiers.');
    case 'listbox_fs'
        desc = formatStr('Selected data modifiers','These data modifiers apply in the order shown here.');
    case 'pushbutton_viewClassifierSettings'
        desc = formatStr('View Classifier Settings','Displays actual classifier settings.');
    case 'pushbutton_editClassifierSettings'
        desc = formatStr('Edit Classifier Settings','Change settings of the actual classifier.');
%
%        % Expert options
%
    case 'listbox_pre_active'
        desc = formatStr('Selected preprocessing options','These data modifiers apply before cross validation (by selected order!). Use the <i>Settings</i>-button, to configure selected data modifiers! For detailed description of the available options, please have a look at the documentation. ');
    case 'listbox_pre_inactive'
        desc = formatStr('Avaiable preprocessing options','These data modifiers apply (when selected) before cross validation. For detailed description of the available options, please have a look at the documentation.');
    case 'listbox_intra_active'
        desc = formatStr('Selected data modifier options','These data modifiers are <b>embedded</b> in the cross validation. Use the <i>Settings</i>-button, to configure selected data modifiers! For detailed description of the available options, please have a look at the documentation. ');
    case 'listbox_intra_inactive'
        desc = formatStr('Avaiable data modifier options','These data modifiers are (when selected) <b>embedded</b> in the  cross validation (by specified order!). For detailed description of the available options, please have a look at the documentation.');
    case {'pushbutton_add_intra','pushbutton_rem_intra','pushbutton_add_pre','pushbutton_rem_pre'}
        desc = formatStr('Select/Deselect modifier','slected modifiers appear on the left side, deselected on the right side.');
    case {'pushbutton_settings_pro','pushbutton_settings_pre'}
        desc = formatStr('Modifier settings','mark a slected modifier (left side) and setup it options by using the <i>Settings</i>-button.');
    case {'pushbutton_view_pro','pushbutton_view_pre'}
        desc = formatStr('View settings','mark a slected modifier (left side) and view its actual Settings.');
%
%        % Validation Panel
%
    case 'table_confusion'
        desc = formatStr('Confusion Matrix','Shows results of last validation process. <a href="http://en.wikipedia.org/wiki/Confusion_matrix">Wikipedia article</a> shows how it works.');
    case 'accGraphic'
        desc = formatStr('Accuracy rates','Visual representation of the results.');
    case 'pushbutton_validation'
        desc = formatStr('Start Validation','starts validation and presents results above afterwards');
%
%      % Ensembles
%
    case 'listbox_ensembleList'
        desc = formatStr('Classifier','Pool of generated classifiers');
    case 'listbox_ensPerf'
        desc = formatStr('Performances','List of individual computed performances for each classifier');
    case 'pushbutton_fromComputed'
        desc = formatStr('add last computed classifier','will add the actually computed classifier to the pool of classifiers');
    case 'pushbutton_addFromFile'
        desc = formatStr('add from saved classifier','load a previously saved classifier to the pool of classifiers');
    case 'pushbutton_removeFromEnsemble'
        desc = formatStr('Remove','removes the marked classifer from the pool of classifiers');
    case 'pushbutton_resetEnsemble'
        desc = formatStr('Reset','remove the pool of classifiers and start over');
    case 'pushbutton_ensembleRun'
        desc = formatStr('Majority Vote Start','Starts leave one out cross validation for all classifiers and computes a Majority Vote for each tested subject.');
%
%       % Validation Setup
%
    case 'valOpt_train'
        desc = formatStr('train','this class will be used for training only.');
    case 'valOpt_test'
        desc = formatStr('test','predicts members of this class, will not be used for training.');
%
%       % Pathchooser
%
    case {'pushbutton_rename1','pushbutton_rename2'}
        desc = formatStr('rename','rename the displayed class. For instance ''patients'' or ''healty controls''');
    case {'pulldown_c1','pulldown_c2'}
        desc = formatStr('Class pulldown menu','Switch the actual displayed class');
    case {'listbox3','listbox5'}
        desc = formatStr('class members','Each line represents one member of the selected class');
    case {'pushbutton1','pushbutton4'}
        desc = formatStr('Add','adds subject from available subjects to a specific class');
    case {'pushbutton2','pushbutton3'}
        desc = formatStr('Remove','removes subject from class and throws it back into pool of available subjects');
    case 'listbox4'
        desc = formatStr('Directory list','list of available images or direcotries respectively - which represent subjects that are not assigned to a class, yet');
    case {'edit_classCount'}
        desc = formatStr('Current number of classes','Change to the desired amount of classes. Use pulldown menus above, to switch and setup between displayed classes');
    case 'pushbuttonReset'
        desc = formatStr('Reset','Clears the complete setup and asks again for a folder to specify');
    case 'save'
        desc = formatStr('Save','save the current setup to a text-file.');
    case 'pushbutton_xls'
        desc = formatStr('Load','Load setup from a previoulsy saved setup - or a setup which was manual written in a txt-file or xls-file (format of such files can be found in the documentation.');
    case 'pushbuttonQuit'
        desc = formatStr('Cancel','cancel this dialog and leave everything unchanged.');
    case 'pushbutton5'
        desc = formatStr('Ok','apply the current setup and go back to main GUI');
%
%        % ignore
%
    case {'text_classes','slider1','text19','text18','text22','text24','text23','valOpt_string','text_members1','text_members2'}
        desc = [];
    otherwise
        desc = ['<html><font color="red">Help Description not found for this item:</font> ' tag '<br>Sorry! Feel free to report this Bug to the author.</html>'];
end
strsplit
function parts = strsplit(splitstr, str, option)
%STRSPLIT Split string into pieces.
%
%   STRSPLIT(SPLITSTR, STR, OPTION) splits the string STR at every occurrence
%   of SPLITSTR and returns the result as a cell array of strings.  By default,
%   SPLITSTR is not included in the output.
%
%   STRSPLIT(SPLITSTR, STR, OPTION) can be used to control how SPLITSTR is
%   included in the output.  If OPTION is 'include', SPLITSTR will be included
%   as a separate string.  If OPTION is 'append', SPLITSTR will be appended to
%   each output string, as if the input string was split at the position right
%   after the occurrence SPLITSTR.  If OPTION is 'omit', SPLITSTR will not be
%   included in the output.

%   Author:      Peter J. Acklam
%   Time-stamp:  2004-09-22 08:48:01 +0200
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   nargsin = nargin;
   error(nargchk(2, 3, nargsin));
   if nargsin < 3
      option = 'omit';
   else
      option = lower(option);
   end

   splitlen = length(splitstr);
   parts = {};

   while 1

      k = strfind(str, splitstr);
      if isempty(k)
         parts{end+1} = str;
         break
      end

      switch option
         case 'include'
            parts(end+1:end+2) = {str(1:k(1)-1), splitstr};
         case 'append'
            parts{end+1} = str(1 : k(1)+splitlen-1);
         case 'omit'
            parts{end+1} = str(1 : k(1)-1);
         otherwise
            error(['Invalid option string -- ', option]);
      end


      str = str(k(1)+splitlen : end);

   end
spm
function varargout=spm(varargin)
% SPM: Statistical Parametric Mapping (startup function)
%_______________________________________________________________________
%  ___  ____  __  __
% / __)(  _ \(  \/  )
% \__ \ )___/ )    (   Statistical Parametric Mapping
% (___/(__)  (_/\/\_)  SPM - http://www.fil.ion.ucl.ac.uk/spm/
%_______________________________________________________________________
%
% SPM (Statistical Parametric Mapping) is a package for the analysis
% functional brain mapping experiments. It is the in-house package of
% the Wellcome Trust Centre for Neuroimaging, and is available to the
% scientific community as copyright freeware under the terms of the
% GNU General Public Licence.
%
% Theoretical, computational and other details of the package are
% available in SPM's "Help" facility. This can be launched from the
% main SPM Menu window using the "Help" button, or directly from the
% command line using the command `spm_help`.
%
% Details of this release are available via the "About SPM" help topic
% (file spm.man), accessible from the SPM splash screen.  (Or type
% `spm_help spm.man` in the MATLAB command window)
%
% This spm function initialises the default parameters, and displays a
% splash screen with buttons leading to the PET, fMRI and M/EEG
% modalities. Alternatively, `spm('pet')`, `spm('fmri')`, `spm('eeg')`
% (equivalently `spm pet`, `spm fmri` and `spm eeg`) lead directly to
% the respective modality interfaces.
%
% Once the modality is chosen, (and it can be toggled mid-session) the
% SPM user interface is displayed. This provides a constant visual
% environment in which data analysis is implemented. The layout has
% been designed to be simple and at the same time show all the
% facilities that are available. The interface consists of three
% windows: A menu window with pushbuttons for the SPM routines (each
% button has a 'CallBack' string which launches the appropriate
% function/script); A blank panel used for interaction with the user;
% And a graphics figure with various editing and print facilities (see
% spm_figure.m). (These windows are 'Tag'ged 'Menu', 'Interactive', and
% 'Graphics' respectively, and should be referred to by their tags
% rather than their figure numbers.)
%
% Further interaction with the user is (mainly) via questioning in the
% 'Interactive' window (managed by spm_input), and file selection
% (managed by spm_select). See the help on spm_input.m and spm_select.m for
% details on using these functions.
%
% If a "message of the day" file named spm_motd.man exists in the SPM
% directory (alongside spm.m) then it is displayed in the Graphics
% window on startup.
%
% Arguments to this routine (spm.m) lead to various setup facilities,
% mainly of use to SPM power users and programmers. See programmers
% FORMAT & help in the main body of spm.m
%
%_______________________________________________________________________
% SPM is developed by members and collaborators of the
% Wellcome Trust Centre for Neuroimaging

%-SVN ID and authorship of this program...
%-----------------------------------------------------------------------
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Andrew Holmes
% $Id: spm.m 4178 2011-01-27 15:12:53Z guillaume $


%=======================================================================
% - FORMAT specifications for embedded CallBack functions
%=======================================================================
%( This is a multi function function, the first argument is an action  )
%( string, specifying the particular action function to take. Recall   )
%( MATLAB's command-function duality: `spm Welcome` is equivalent to   )
%( `spm('Welcome')`.                                                   )
%
% FORMAT spm
% Defaults to spm('Welcome')
%
% FORMAT spm('Welcome')
% Clears command window, deletes all figures, prints welcome banner and
% splash screen, sets window defaults.
%
% FORMAT spm('AsciiWelcome')
% Prints ASCII welcome banner in MATLAB command window.
%
% FORMAT spm('PET') spm('FMRI') spm('EEG')
% Closes all windows and draws new Menu, Interactive, and Graphics
% windows for an SPM session. The buttons in the Menu window launch the
% main analysis routines.
%
% FORMAT spm('ChMod',Modality)
% Changes modality of SPM: Currently SPM supports PET & MRI modalities,
% each of which have a slightly different Menu window and different
% defaults. This function switches to the specified modality, setting
% defaults and displaying the relevant buttons.
%
% FORMAT spm('defaults',Modality)
% Sets default global variables for the specified modality.
%
% FORMAT [Modality,ModNum]=spm('CheckModality',Modality)
% Checks the specified modality against those supported, returns
% upper(Modality) and the Modality number, it's position in the list of
% supported Modalities.
%
% FORMAT Fmenu = spm('CreateMenuWin',Vis)
% Creates SPM menu window, 'Tag'ged 'Menu'
% F   - handle of figure created
% Vis - Visibility, 'on' or 'off'
%
% Finter = FORMAT spm('CreateIntWin',Vis)
% Creates an SPM Interactive window, 'Tag'ged 'Interactive'
% F   - handle of figure created
% Vis - Visibility, 'on' or 'off'
%
% FORMAT [Finter,Fgraph,CmdLine] = spm('FnUIsetup',Iname,bGX,CmdLine)
% Robust UIsetup procedure for functions:
%   Returns handles of 'Interactive' and 'Graphics' figures.
%   Creates 'Interactive' figure if ~CmdLine, creates 'Graphics' figure if bGX.
% Iname   - Name for 'Interactive' window
% bGX     - Need a Graphics window? [default 1]
% CmdLine - CommandLine usage? [default spm('CmdLine')]
% Finter  - handle of 'Interactive' figure
% Fgraph  - handle of 'Graphics' figure
% CmdLine - CommandLine usage?
%
% FORMAT WS=spm('WinScale')
% Returns ratios of current display dimensions to that of a 1152 x 900
% Sun display. WS=[Xratio,Yratio,Xratio,Yratio]. Used for scaling other
% GUI elements.
% (Function duplicated in spm_figure.m, repeated to reduce inter-dependencies.)
%
% FORMAT [FS,sf] = spm('FontSize',FS)
% FORMAT [FS,sf] = spm('FontSizes',FS)
% Returns fontsizes FS scaled for the current display.
% FORMAT sf = spm('FontScale')
% Returns font scaling factor
% FS     - (vector of) Font sizes to scale [default [1:36]]
% sf     - font scaling factor (FS(out) = floor(FS(in)*sf)
%
% Rect = spm('WinSize',Win,raw)
% Returns sizes and positions for SPM windows.
% Win  - 'Menu', 'Interactive', 'Graphics', or '0'
%      -  Window whose position is required. Only first character is
%         examined. '0' returns size of root workspace.
% raw  - If specified, then positions are for a 1152 x 900 Sun display.
%        Otherwise the positions are scaled for the current display.
%
% FORMAT [c,cName] = spm('Colour')
% Returns the RGB triple and a description for the current en-vogue SPM
% colour, the background colour for the Menu and Help windows.
%
% FORMAT F = spm('FigName',Iname,F,CmdLine)
% Set name of figure F to "SPMver (User): Iname" if ~CmdLine
% Robust to absence of figure.
% Iname      - Name for figure
% F (input)  - Handle (or 'Tag') of figure to name [default 'Interactive']
% CmdLine    - CommandLine usage? [default spm('CmdLine')]
% F (output) - Handle of figure named
%
% FORMAT Fs = spm('Show')
% Opens all SPM figure windows (with HandleVisibility) using `figure`.
%   Maintains current figure.
% Fs - vector containing all HandleVisible figures (i.e. get(0,'Children'))
%
% FORMAT spm('Clear',Finter, Fgraph)
% Clears and resets SPM-GUI, clears and timestamps MATLAB command window.
% Finter  - handle or 'Tag' of 'Interactive' figure [default 'Interactive']
% Fgraph  - handle or 'Tag' of 'Graphics' figure [default 'Graphics']
%
% FORMAT SPMid = spm('FnBanner', Fn,FnV)
% Prints a function start banner, for version FnV of function Fn, & datestamps
% FORMAT SPMid = spm('SFnBanner',Fn,FnV)
% Prints a sub-function start banner
% FORMAT SPMid = spm('SSFnBanner',Fn,FnV)
% Prints a sub-sub-function start banner
% Fn    - Function name (string)
% FnV   - Function version (string)
% SPMid - ID string: [SPMver: Fn (FnV)]
%
% FORMAT SPMdir = spm('Dir',Mfile)
% Returns the directory containing the version of spm in use,
% identified as the first in MATLABPATH containing the Mfile spm (this
% file) (or Mfile if specified).
%
% FORMAT [v,r] = spm('Ver',Mfile,ReDo)
% Returns the current version (v) and release (r) of file Mfile. This
% corresponds to the Last changed Revision number extracted from the
% Subversion Id tag.
% If Mfile is absent or empty then it returns the current SPM version (v)
% and release (r), extracted from the file Contents.m in the SPM directory
% (these information are cached in a persistent variable to enable repeat
% use without recomputation).
% If Redo [default false] is true, then the cached current SPM information
% are not used but recomputed (and recached).
%
% FORMAT v = spm('MLver')
% Returns MATLAB version, truncated to major & minor revision numbers
%
% FORMAT xTB = spm('TBs')
% Identifies installed SPM toolboxes: SPM toolboxes are defined as the
% contents of sub-directories of fullfile(spm('Dir'),'toolbox') - the
% SPM toolbox installation directory. For SPM to pick a toolbox up,
% there must be a single mfile in the directory whose name ends with
% the toolbox directory name. (I.e. A toolbox called "test" would be in
% the "test" subdirectory of spm('Dir'), with a single file named
% *test.m.) This M-file is regarded as the launch file for the
% toolbox.
% xTB      - structure array containing toolbox definitions
% xTB.name - name of toolbox (taken as toolbox directory name)
% xTB.prog - launch program for toolbox
% xTB.dir  - toolbox directory
%
% FORMAT spm('TBlaunch',xTB,i)
% Launch a toolbox, prepending TBdir to path if necessary
% xTB      - toolbox definition structure (i.e. from spm('TBs')
% xTB.name - name of toolbox
% xTB.prog - name of program to launch toolbox
% xTB.dir  - toolbox directory (prepended to path if not on path)
%
% FORMAT [v1,v2,...] = spm('GetGlobal',name1,name2,...)
% Returns values of global variables (without declaring them global)
% name1, name2,... - name strings of desired globals
% a1, a2,...       - corresponding values of global variables with given names
%                    ([] is returned as value if global variable doesn't exist)
%
% FORMAT CmdLine = spm('CmdLine',CmdLine)
% Command line SPM usage?
% CmdLine (input)  - CmdLine preference
%                    [defaults (missing or empty) to global defaults.cmdline,]
%                    [if it exists, or 0 (GUI) otherwise.                    ]
% CmdLine (output) - true if global CmdLine if true,
%                    or if on a terminal with no support for graphics windows.
%
% FORMAT spm('PopUpCB',h)
% Callback handler for PopUp UI menus with multiple callbacks as cellstr UserData
%
% FORMAT str = spm('GetUser',fmt)
% Returns current users login name, extracted from the hosting environment
% fmt   - format string: If USER is defined then sprintf(fmt,USER) is returned
%
% FORMAT spm('Beep')
% Plays the keyboard beep!
%
% FORMAT spm('time')
% Returns the current time and date as hh:mm dd/mm/yyyy
%
% FORMAT spm('Pointer',Pointer)
% Changes pointer on all SPM (HandleVisible) windows to type Pointer
% Pointer defaults to 'Arrow'. Robust to absence of windows
%
% FORMAT h = spm('alert',Message,Title,CmdLine,wait)
% FORMAT h = spm('alert"',Message,Title,CmdLine,wait)
% FORMAT h = spm('alert*',Message,Title,CmdLine,wait)
% FORMAT h = spm('alert!',Message,Title,CmdLine,wait)
% Displays an alert, either in a GUI msgbox, or as text in the command window.
%  ( 'alert"' uses the 'help' msgbox icon, 'alert*' the )
%  ( 'error' icon, 'alert!' the 'warn' icon             )
% Message - string (or cellstr) containing message to print
% Title   - title string for alert
% CmdLine - CmdLine preference [default spm('CmdLine')]
%         - If CmdLine is complex, then a CmdLine alert is always used,
%           possibly in addition to a msgbox (the latter according
%           to spm('CmdLine').)
% wait    - if true, waits until user dismisses GUI / confirms text alert
%           [default 0] (if doing both GUI & text, waits on GUI alert)
% h       - handle of msgbox created, empty if CmdLine used
%
% FORMAT spm('GUI_FileDelete')
% CallBack for GUI for file deletion, using spm_select and confirmation dialogs
%
% FORMAT spm('Clean')
% Clear all variables, globals, functions, MEX links and class definitions.
%
% FORMAT spm('Help',varargin)
% Merely a gateway to spm_help(varargin) - so you can type "spm help"
%
% FORMAT spm('Quit')
% Quit SPM, delete all windows and clear the command window.
%
%_______________________________________________________________________


%-Parameters
%-----------------------------------------------------------------------
Modalities = {'PET','FMRI','EEG'};

%-Format arguments
%-----------------------------------------------------------------------
if nargin == 0, Action = 'Welcome'; else Action = varargin{1}; end


%=======================================================================
switch lower(Action), case 'welcome'             %-Welcome splash screen
%=======================================================================
% spm('Welcome')
spm_check_installation('basic');
defaults = spm('GetGlobal','defaults');
if isfield(defaults,'modality')
    spm(defaults.modality);
    return
end

%-Open startup window, set window defaults
%-----------------------------------------------------------------------
Fwelcome = openfig(fullfile(spm('Dir'),'spm_Welcome.fig'),'new','invisible');
set(Fwelcome,'name',sprintf('%s%s',spm('ver'),spm('GetUser',' (%s)')));
set(get(findobj(Fwelcome,'Type','axes'),'children'),'FontName',spm_platform('Font','Times'));
set(findobj(Fwelcome,'Tag','SPM_VER'),'String',spm('Ver'));
RectW = spm('WinSize','W',1); Rect0 = spm('WinSize','0',1);
set(Fwelcome,'Units','pixels', 'Position',...
    [Rect0(1)+(Rect0(3)-RectW(3))/2, Rect0(2)+(Rect0(4)-RectW(4))/2, RectW(3), RectW(4)]);
set(Fwelcome,'Visible','on');

%=======================================================================
case 'asciiwelcome'                           %-ASCII SPM banner welcome
%=======================================================================
% spm('AsciiWelcome')
disp( ' ___  ____  __  __                                            ');
disp( '/ __)(  _ \(  \/  )                                           ');
disp( '\__ \ )___/ )    (   Statistical Parametric Mapping           ');
disp(['(___/(__)  (_/\/\_)  ',spm('Ver'),' - http://www.fil.ion.ucl.ac.uk/spm/']);
fprintf('\n');


%=======================================================================
case lower(Modalities)       %-Initialise SPM in PET, fMRI, EEG modality
%=======================================================================
% spm(Modality)
spm_check_installation('basic');
try, feature('JavaFigures',0); end

%-Initialisation and workspace canonicalisation
%-----------------------------------------------------------------------
local_clc;
spm('AsciiWelcome');                    fprintf('\n\nInitialising SPM');
Modality = upper(Action);                                  fprintf('.');
spm_figure('close',allchild(0));                           fprintf('.');

%-Load startup global defaults
%-----------------------------------------------------------------------
spm_defaults;                                              fprintf('.');

%-Setup for batch system
%-----------------------------------------------------------------------
spm_jobman('initcfg');
spm_select('prevdirs',[spm('Dir') filesep]);

%-Draw SPM windows
%-----------------------------------------------------------------------
if ~spm('CmdLine')
    Fmenu  = spm('CreateMenuWin','off');                   fprintf('.');
    Finter = spm('CreateIntWin','off');                    fprintf('.');
else
    Fmenu  = [];
    Finter = [];
end
Fgraph = spm_figure('Create','Graphics','Graphics','off'); fprintf('.');

spm_figure('WaterMark',Finter,spm('Ver'),'',45);           fprintf('.');

Fmotd  = fullfile(spm('Dir'),'spm_motd.man');
if exist(Fmotd,'file'), spm_help('!Disp',Fmotd,'',Fgraph,spm('Ver')); end
                                                           fprintf('.');

%-Setup for current modality
%-----------------------------------------------------------------------
spm('ChMod',Modality);                                     fprintf('.');

%-Reveal windows
%-----------------------------------------------------------------------
set([Fmenu,Finter,Fgraph],'Visible','on');          fprintf('done\n\n');

%-Print present working directory
%-----------------------------------------------------------------------
fprintf('SPM present working directory:\n\t%s\n',pwd)


%=======================================================================
case 'chmod'                      %-Change SPM modality PET<->fMRI<->EEG
%=======================================================================
% spm('ChMod',Modality)
%-----------------------------------------------------------------------

%-Sort out arguments
%-----------------------------------------------------------------------
if nargin<2, Modality = ''; else Modality = varargin{2}; end
[Modality,ModNum] = spm('CheckModality',Modality);

%-Sort out global defaults
%-----------------------------------------------------------------------
spm('defaults',Modality);

%-Sort out visability of appropriate controls on Menu window
%-----------------------------------------------------------------------
Fmenu = spm_figure('FindWin','Menu');
if ~isempty(Fmenu)

    if strcmpi(Modality,'PET')
        set(findobj(Fmenu, 'Tag', 'FMRI'),    'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'EEG'),     'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'PETFMRI'), 'Visible', 'on' );
        set(findobj(Fmenu, 'Tag', 'PET'),     'Visible', 'on' );
    elseif strcmpi(Modality,'FMRI')
        set(findobj(Fmenu, 'Tag', 'EEG'),     'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'PET'),     'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'PETFMRI'), 'Visible', 'on' );
        set(findobj(Fmenu, 'Tag', 'FMRI'),    'Visible', 'on' );
    else
        set(findobj(Fmenu, 'Tag', 'PETFMRI'), 'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'PET'),     'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'FMRI'),    'Visible', 'off');
        set(findobj(Fmenu, 'Tag', 'EEG'),     'Visible', 'on' );
    end
    set(findobj(Fmenu,'Tag','Modality'),'Value',ModNum,'UserData',ModNum);
else
    warning('SPM Menu window not found');
end

%=======================================================================
case 'defaults'                  %-Set SPM defaults (as global variable)
%=======================================================================
% spm('defaults',Modality)
%-----------------------------------------------------------------------
if nargin<2, Modality=''; else Modality=varargin{2}; end
Modality = spm('CheckModality',Modality);

%-Re-initialise, load defaults (from spm_defaults.m) and store modality
%-----------------------------------------------------------------------
clear global defaults
spm_get_defaults('modality',Modality);

%-Addpath modality-specific toolboxes
%-----------------------------------------------------------------------
if strcmpi(Modality,'EEG') && ~isdeployed
    addpath(fullfile(spm('Dir'),'external','fieldtrip'));
    ft_defaults;
    addpath(fullfile(spm('Dir'),'external','bemcp'));
    addpath(fullfile(spm('Dir'),'external','ctf'));
    addpath(fullfile(spm('Dir'),'external','eeprobe'));
    addpath(fullfile(spm('Dir'),'external','yokogawa'));
    addpath(fullfile(spm('Dir'),'toolbox', 'dcm_meeg'));
    addpath(fullfile(spm('Dir'),'toolbox', 'spectral'));
    addpath(fullfile(spm('Dir'),'toolbox', 'Neural_Models'));
    addpath(fullfile(spm('Dir'),'toolbox', 'Beamforming'));
    addpath(fullfile(spm('Dir'),'toolbox', 'MEEGtools'));
end

%-Turn output pagination off in Octave
%-----------------------------------------------------------------------
if strcmpi(spm_check_version,'octave')
    try
        more('off');
        page_screen_output(false);
        page_output_immediately(true);
    end
end

%-Return defaults variable if asked
%-----------------------------------------------------------------------
if nargout, varargout = {spm_get_defaults}; end


%=======================================================================
case 'checkmodality'              %-Check & canonicalise modality string
%=======================================================================
% [Modality,ModNum] = spm('CheckModality',Modality)
%-----------------------------------------------------------------------
if nargin<2, Modality=''; else Modality=upper(varargin{2}); end
if isempty(Modality)
    try
        Modality = spm_get_defaults('modality');
    end
end
if ischar(Modality)
    ModNum = find(ismember(Modalities,Modality));
else
    if ~any(Modality == 1:length(Modalities))
        Modality = 'ERROR';
        ModNum   = [];
    else
        ModNum   = Modality;
        Modality = Modalities{ModNum};
    end
end
if isempty(ModNum)
    if isempty(Modality)
        fprintf('Modality is not set: use spm(''defaults'',''MOD''); ');
        fprintf('where MOD is one of PET, FMRI, EEG.\n');
    end
    error('Unknown Modality.');
end
varargout = {upper(Modality),ModNum};


%=======================================================================
case 'createmenuwin'                            %-Create SPM menu window
%=======================================================================
% Fmenu = spm('CreateMenuWin',Vis)
%-----------------------------------------------------------------------
if nargin<2, Vis='on'; else Vis=varargin{2}; end

%-Close any existing 'Menu' 'Tag'ged windows
%-----------------------------------------------------------------------
delete(spm_figure('FindWin','Menu'))
Fmenu = openfig(fullfile(spm('Dir'),'spm_Menu.fig'),'new','invisible');
set(Fmenu,'name',sprintf('%s%s: Menu',spm('ver'),spm('GetUser',' (%s)')));
S0 = spm('WinSize','0',1);
SM = spm('WinSize','M');
set(Fmenu,'Units','pixels', 'Position',[S0(1) S0(2) 0 0] + SM);

%-Set SPM colour
%-----------------------------------------------------------------------
set(findobj(Fmenu,'Tag', 'frame'),'backgroundColor',spm('colour'));
try
    if ismac
        b = findobj(Fmenu,'Style','pushbutton');
        set(b,'backgroundColor',get(b(1),'backgroundColor')+0.002);
    end
end

%-Set toolbox
%-----------------------------------------------------------------------
xTB       = spm('tbs');
if ~isempty(xTB)
    set(findobj(Fmenu,'Tag', 'Toolbox'),'String',{'Toolbox:' xTB.name });
    set(findobj(Fmenu,'Tag', 'Toolbox'),'UserData',xTB);
else
    set(findobj(Fmenu,'Tag', 'Toolbox'),'Visible','off')
end
set(Fmenu,'Visible',Vis);
varargout = {Fmenu};


%=======================================================================
case 'createintwin'                      %-Create SPM interactive window
%=======================================================================
% Finter = spm('CreateIntWin',Vis)
%-----------------------------------------------------------------------
if nargin<2, Vis='on'; else Vis=varargin{2}; end

%-Close any existing 'Interactive' 'Tag'ged windows
%-----------------------------------------------------------------------
delete(spm_figure('FindWin','Interactive'))

%-Create SPM Interactive window
%-----------------------------------------------------------------------
FS     = spm('FontSizes');
PF     = spm_platform('fonts');
S0     = spm('WinSize','0',1);
SI     = spm('WinSize','I');
Finter = figure('IntegerHandle','off',...
    'Tag','Interactive',...
    'Name',spm('Ver'),...
    'NumberTitle','off',...
    'Units','pixels',...
    'Position',[S0(1) S0(2) 0 0] +  SI,...
    'Resize','on',...
    'Color',[1 1 1]*.8,...
    'MenuBar','none',...
    'DefaultTextFontName',PF.helvetica,...
    'DefaultTextFontSize',FS(10),...
    'DefaultAxesFontName',PF.helvetica,...
    'DefaultUicontrolBackgroundColor',[1 1 1]*.7,...
    'DefaultUicontrolFontName',PF.helvetica,...
    'DefaultUicontrolFontSize',FS(10),...
    'DefaultUicontrolInterruptible','on',...
    'Renderer','painters',...
    'Visible',Vis);
varargout = {Finter};

%=======================================================================
case 'fnuisetup'                %-Robust UI setup for main SPM functions
%=======================================================================
% [Finter,Fgraph,CmdLine] = spm('FnUIsetup',Iname,bGX,CmdLine)
%-----------------------------------------------------------------------
if nargin<4, CmdLine=spm('CmdLine'); else CmdLine=varargin{4}; end
if nargin<3, bGX=1; else bGX=varargin{3}; end
if nargin<2, Iname=''; else Iname=varargin{2}; end
if CmdLine
    Finter = spm_figure('FindWin','Interactive');
    if ~isempty(Finter), spm_figure('Clear',Finter), end
    %if ~isempty(Iname), fprintf('%s:\n',Iname), end
else
    Finter = spm_figure('GetWin','Interactive');
    spm_figure('Clear',Finter)
    if ~isempty(Iname)
        str = sprintf('%s (%s): %s',spm('ver'),spm('GetUser'),Iname);
    else
        str = '';
    end
    set(Finter,'Name',str)
end

if bGX
    Fgraph = spm_figure('GetWin','Graphics');
    spm_figure('Clear',Fgraph)
else
    Fgraph = spm_figure('FindWin','Graphics');
end
varargout = {Finter,Fgraph,CmdLine};


%=======================================================================
case 'winscale'                  %-Window scale factors (to fit display)
%=======================================================================
% WS = spm('WinScale')
%-----------------------------------------------------------------------
S0 = spm('WinSize','0',1);

if all(ismember(S0(:),[0 1]))
    varargout = {[1 1 1 1]};
    return;
end

tmp   = [S0(3)/1152 (S0(4)-50)/900];

varargout = {min(tmp)*[1 1 1 1]};

% Make sure that aspect ratio is about right - for funny shaped screens
% varargout = {[S0(3)/1152 (S0(4)-50)/900 S0(3)/1152 (S0(4)-50)/900]};


%=======================================================================
case {'fontsize','fontsizes','fontscale'}                 %-Font scaling
%=======================================================================
% [FS,sf] = spm('FontSize',FS)
% [FS,sf] = spm('FontSizes',FS)
% sf = spm('FontScale')
%-----------------------------------------------------------------------
if nargin<2, FS=1:36; else FS=varargin{2}; end

offset     = 1;
%try, if ismac, offset = 1.4; end; end

sf  = offset + 0.85*(min(spm('WinScale'))-1);

if strcmpi(Action,'fontscale')
    varargout = {sf};
else
    varargout = {ceil(FS*sf),sf};
end


%=======================================================================
case 'winsize'                 %-Standard SPM window locations and sizes
%=======================================================================
% Rect = spm('WinSize',Win,raw)
%-----------------------------------------------------------------------
if nargin<3, raw=0; else raw=1; end
if nargin<2, Win=''; else Win=varargin{2}; end

Rect = [[108 466 400 445];...
        [108 045 400 395];...
        [515 015 600 865];...
        [326 310 500 280]];

if isempty(Win)
    %-All windows
elseif upper(Win(1))=='M'
    %-Menu window
    Rect = Rect(1,:);
elseif upper(Win(1))=='I'
    %-Interactive window
    Rect = Rect(2,:);
elseif upper(Win(1))=='G'
    %-Graphics window
    Rect = Rect(3,:);
elseif upper(Win(1))=='W'
    %-Welcome window
    Rect = Rect(4,:);
elseif Win(1)=='0'
    %-Root workspace
    Rect = get(0, 'MonitorPosition');
    if all(ismember(Rect(:),[0 1]))
        warning('SPM:noDisplay','Unable to open display.');
    end
    if size(Rect,1) > 1 % Multiple Monitors
        %-Use Monitor containing the Pointer
        pl = get(0,'PointerLocation');
        Rect(:,[3 4]) = Rect(:,[3 4]) + Rect(:,[1 2]);
        w  = find(pl(1)>=Rect(:,1) & pl(1)<=Rect(:,3) &...
                  pl(2)>=Rect(:,2) & pl(2)<=Rect(:,4));
        if numel(w)~=1, w = 1; end
        Rect = Rect(w,:);
        %-Make sure that the format is [x y width height]
        Rect(1,[3 4]) = Rect(1,[3 4]) - Rect(1,[1 2]) + 1;
    end
else
    error('Unknown Win type');
end

if ~raw
    WS = repmat(spm('WinScale'),size(Rect,1),1);
    Rect = Rect.*WS;
end

varargout = {Rect};


%=======================================================================
case 'colour'                                     %-SPM interface colour
%=======================================================================
% spm('Colour')
%-----------------------------------------------------------------------
%-Pre-developmental livery
% varargout = {[1.0,0.2,0.3],'fightening red'};
%-Developmental livery
% varargout = {[0.7,1.0,0.7],'flourescent green'};
%-Alpha release livery
% varargout = {[0.9,0.9,0.5],'over-ripe banana'};
%-Beta release livery
% varargout = {[0.9 0.8 0.9],'blackcurrant purple'};
%-Distribution livery
  varargout = {[0.8 0.8 1.0],'vile violet'};

try
    varargout = {spm_get_defaults('ui.colour'),'bluish'};
end


%=======================================================================
case 'figname'                                %-Robust SPM figure naming
%=======================================================================
% F = spm('FigName',Iname,F,CmdLine)
%-----------------------------------------------------------------------
if nargin<4, CmdLine=spm('CmdLine'); else CmdLine=varargin{4}; end
if nargin<3, F='Interactive'; else F=varargin{3}; end
if nargin<2, Iname=''; else Iname=varargin{2}; end

%if ~isempty(Iname), fprintf('\t%s\n',Iname), end
if CmdLine, varargout={[]}; return, end
F = spm_figure('FindWin',F);
if ~isempty(F) && ~isempty(Iname)
    set(F,'Name',sprintf('%s (%s): %s',spm('ver'),spm('GetUser'),Iname))
end
varargout={F};


%=======================================================================
case 'show'                   %-Bring visible MATLAB windows to the fore
%=======================================================================
% Fs = spm('Show')
%-----------------------------------------------------------------------
cF = get(0,'CurrentFigure');
Fs = get(0,'Children');
Fs = findobj(Fs,'flat','Visible','on');
for F=Fs(:)', figure(F), end
try, figure(cF), set(0,'CurrentFigure',cF); end
varargout={Fs};


%=======================================================================
case 'clear'                                             %-Clear SPM GUI
%=======================================================================
% spm('Clear',Finter, Fgraph)
%-----------------------------------------------------------------------
if nargin<3, Fgraph='Graphics'; else Fgraph=varargin{3}; end
if nargin<2, Finter='Interactive'; else Finter=varargin{2}; end
spm_figure('Clear',Fgraph)
spm_figure('Clear',Finter)
spm('Pointer','Arrow')
spm_conman('Initialise','reset');
local_clc;
fprintf('\n');
%evalin('base','clear')


%=======================================================================
case {'fnbanner','sfnbanner','ssfnbanner'}  %-Text banners for functions
%=======================================================================
% SPMid = spm('FnBanner', Fn,FnV)
% SPMid = spm('SFnBanner',Fn,FnV)
% SPMid = spm('SSFnBanner',Fn,FnV)
%-----------------------------------------------------------------------
time = spm('time');
str  = spm('ver');
if nargin>=2, str = [str,': ',varargin{2}]; end
if nargin>=3
    v = regexp(varargin{3},'\$Rev: (\d*) \$','tokens','once');
    if ~isempty(v)
        str = [str,' (v',v{1},')'];
    else
        str = [str,' (v',varargin{3},')'];
    end
end

switch lower(Action)
case 'fnbanner'
    tab = '';
    wid = 72;
    lch = '=';
case 'sfnbanner'
    tab = sprintf('\t');
    wid = 72-8;
    lch = '-';
case 'ssfnbanner'
    tab = sprintf('\t\t');
    wid = 72-2*8;
    lch = '-';
end

fprintf('\n%s%s',tab,str)
fprintf('%c',repmat(' ',1,wid-length([str,time])))
fprintf('%s\n%s',time,tab)
fprintf('%c',repmat(lch,1,wid)),fprintf('\n')
varargout = {str};


%=======================================================================
case 'dir'                           %-Identify specific (SPM) directory
%=======================================================================
% spm('Dir',Mfile)
%-----------------------------------------------------------------------
if nargin<2, Mfile='spm'; else Mfile=varargin{2}; end
SPMdir = which(Mfile);
if isempty(SPMdir)             %-Not found or full pathname given
    if exist(Mfile,'file')==2  %-Full pathname
        SPMdir = Mfile;
    else
        error(['Can''t find ',Mfile,' on MATLABPATH']);
    end
end
SPMdir    = fileparts(SPMdir);
varargout = {SPMdir};


%=======================================================================
case 'ver'                                                 %-SPM version
%=======================================================================
% [SPMver, SPMrel] = spm('Ver',Mfile,ReDo)
%-----------------------------------------------------------------------
if nargin > 3, warning('This usage of "spm ver" is now deprecated.'); end
if nargin ~= 3, ReDo = false; else ReDo = logical(varargin{3}); end
if nargin == 1 || (nargin > 1 && isempty(varargin{2}))
    Mfile = '';
else
    Mfile = which(varargin{2});
    if isempty(Mfile)
        error('Can''t find %s on MATLABPATH.',varargin{2});
    end
end

v = spm_version(ReDo);

if isempty(Mfile)
    varargout = {v.Release v.Version};
else
    unknown = struct('file',Mfile,'id','???','date','','author','');
    if ~isdeployed
        fp  = fopen(Mfile,'rt');
        if fp == -1, error('Can''t read %s.',Mfile); end
        str = fread(fp,Inf,'*uchar');
        fclose(fp);
        str = char(str(:)');
        r = regexp(str,['\$Id: (?<file>\S+) (?<id>[0-9]+) (?<date>\S+) ' ...
            '(\S+Z) (?<author>\S+) \$'],'names','once');
        if isempty(r), r = unknown; end
    else
        r = unknown;
    end
    varargout = {r(1).id v.Release};
end


%=======================================================================
case 'mlver'                       %-MATLAB major & point version number
%=======================================================================
% v = spm('MLver')
%-----------------------------------------------------------------------
v = version; tmp = find(v=='.');
if length(tmp)>1, varargout={v(1:tmp(2)-1)}; end


%=======================================================================
case 'tbs'                                %-Identify installed toolboxes
%=======================================================================
% xTB = spm('TBs')
%-----------------------------------------------------------------------

% Toolbox directory
%-----------------------------------------------------------------------
Tdir  = fullfile(spm('Dir'),'toolbox');

%-List of potential installed toolboxes directories
%-----------------------------------------------------------------------
if exist(Tdir,'dir')
    d = dir(Tdir);
    d = {d([d.isdir]).name};
    d = {d{cellfun('isempty',regexp(d,'^\.'))}};
else
    d = {};
end


%-Look for a "main" M-file in each potential directory
%-----------------------------------------------------------------------
xTB = [];
for i = 1:length(d)
    tdir = fullfile(Tdir,d{i});
    fn   = cellstr(spm_select('List',tdir,['^.*' d{i} '\.m$']));

    if ~isempty(fn{1}),
        xTB(end+1).name = strrep(d{i},'_','');
        xTB(end).prog   = spm_str_manip(fn{1},'r');
        xTB(end).dir    = tdir;
    end

end

varargout{1} = xTB;


%=======================================================================
case 'tblaunch'                                  %-Launch an SPM toolbox
%=======================================================================
% xTB = spm('TBlaunch',xTB,i)
%-----------------------------------------------------------------------
if nargin < 3, i   = 1;          else i   = varargin{3}; end
if nargin < 2, xTB = spm('TBs'); else xTB = varargin{2}; end

if i > 0
    %-Addpath (& report)
    %-------------------------------------------------------------------
    if isempty(strfind(path,xTB(i).dir))
        if ~isdeployed, addpath(xTB(i).dir,'-begin'); end
        spm('alert"',{'Toolbox directory prepended to MATLAB path:',...
            xTB(i).dir},...
            [xTB(i).name,' toolbox'],1);
    end

    %-Launch
    %-------------------------------------------------------------------
    evalin('base',xTB(i).prog);
end


%=======================================================================
case 'getglobal'                           %-Get global variable cleanly
%=======================================================================
% varargout = spm('GetGlobal',varargin)
%-----------------------------------------------------------------------
wg = who('global');
for i=1:nargin-1
    if any(strcmp(wg,varargin{i+1}))
        eval(['global ',varargin{i+1},', tmp=',varargin{i+1},';'])
        varargout{i} = tmp;
    else
        varargout{i} = [];
    end
end


%=======================================================================
case 'cmdline'                                  %-SPM command line mode?
%=======================================================================
% CmdLine = spm('CmdLine',CmdLine)
%-----------------------------------------------------------------------
if nargin<2, CmdLine=[]; else CmdLine=varargin{2}; end
if isempty(CmdLine)
    try
        CmdLine = spm_get_defaults('cmdline');
    catch
        CmdLine = 0;
    end
end
varargout = { CmdLine | ...
              (get(0,'ScreenDepth')==0) | ...
              strcmpi(spm_check_version,'octave') };


%=======================================================================
case 'popupcb'               %-Callback handling utility for PopUp menus
%=======================================================================
% spm('PopUpCB',h)
%-----------------------------------------------------------------------
if nargin<2, h=gcbo; else h=varargin{2}; end
v   = get(h,'Value');
if v==1, return, end
set(h,'Value',1)
CBs = get(h,'UserData');
evalin('base',CBs{v-1})


%=======================================================================
case 'getuser'                                           %-Get user name
%=======================================================================
% str = spm('GetUser',fmt)
%-----------------------------------------------------------------------
str = spm_platform('user');
if ~isempty(str) && nargin>1, str = sprintf(varargin{2},str); end
varargout = {str};


%=======================================================================
case 'beep'                                         %-Produce beep sound
%=======================================================================
% spm('Beep')
%-----------------------------------------------------------------------
beep;


%=======================================================================
case 'time'                          %-Return formatted date/time string
%=======================================================================
% [timestr, date_vec] = spm('Time')
%-----------------------------------------------------------------------
tmp = clock;
varargout = {sprintf('%02d:%02d:%02d - %02d/%02d/%4d',...
             tmp(4),tmp(5),floor(tmp(6)),tmp(3),tmp(2),tmp(1)), tmp};


%=======================================================================
case 'memory'
%=======================================================================
% m = spm('Memory')
%-----------------------------------------------------------------------
maxmemdef = 200*1024*1024; % 200 MB
%m = spm_get_defaults('stats.maxmem');
m = maxmemdef;
varargout = {m};


%=======================================================================
case 'pointer'                 %-Set mouse pointer in all MATLAB windows
%=======================================================================
% spm('Pointer',Pointer)
%-----------------------------------------------------------------------
if nargin<2, Pointer='Arrow'; else  Pointer=varargin{2}; end
set(get(0,'Children'),'Pointer',lower(Pointer))


%=======================================================================
case {'alert','alert"','alert*','alert!'}                %-Alert dialogs
%=======================================================================
% h = spm('alert',Message,Title,CmdLine,wait)
%-----------------------------------------------------------------------

%- Globals
%-----------------------------------------------------------------------
if nargin<5, wait    = 0;  else wait    = varargin{5}; end
if nargin<4, CmdLine = []; else CmdLine = varargin{4}; end
if nargin<3, Title   = ''; else Title   = varargin{3}; end
if nargin<2, Message = ''; else Message = varargin{2}; end
Message = cellstr(Message);

if isreal(CmdLine)
    CmdLine  = spm('CmdLine',CmdLine);
    CmdLine2 = 0;
else
    CmdLine  = spm('CmdLine');
    CmdLine2 = 1;
end
timestr = spm('Time');
SPMv    = spm('ver');

switch(lower(Action))
    case 'alert',  icon = 'none';  str = '--- ';
    case 'alert"', icon = 'help';  str = '~ - ';
    case 'alert*', icon = 'error'; str = '* - ';
    case 'alert!', icon = 'warn';  str = '! - ';
end

if CmdLine || CmdLine2
    Message(strcmp(Message,'')) = {' '};
    tmp = sprintf('%s: %s',SPMv,Title);
    fprintf('\n    %s%s  %s\n\n',str,tmp,repmat('-',1,62-length(tmp)))
    fprintf('        %s\n',Message{:})
    fprintf('\n        %s  %s\n\n',repmat('-',1,62-length(timestr)),timestr)
    h = [];
end

if ~CmdLine
    tmp = max(size(char(Message),2),42) - length(SPMv) - length(timestr);
    str = sprintf('%s  %s  %s',SPMv,repmat(' ',1,tmp-4),timestr);
    h   = msgbox([{''};Message(:);{''};{''};{str}],...
          sprintf('%s%s: %s',SPMv,spm('GetUser',' (%s)'),Title),...
          icon,'non-modal');
    drawnow
    set(h,'windowstyle','modal');
end

if wait
    if isempty(h)
        input('        press ENTER to continue...');
    else
        uiwait(h)
        h = [];
    end
end

if nargout, varargout = {h}; end


%=======================================================================
case 'gui_filedelete'                                %-GUI file deletion
%=======================================================================
% spm('GUI_FileDelete')
%-----------------------------------------------------------------------
[P, sts] = spm_select(Inf,'.*','Select file(s) to delete');
if ~sts, return; end
P = cellstr(P);
n = numel(P);
if n==0 || (n==1 && isempty(P{1}))
    spm('alert"','Nothing selected to delete!','file delete',0);
    return
elseif n<4
    str=[{' '};P];
elseif n<11
    str=[{' '};P;{' ';sprintf('(%d files)',n)}];
else
    str=[{' '};P(1:min(n,10));{'...';' ';sprintf('(%d files)',n)}];
end
if spm_input(str,-1,'bd','delete|cancel',[1,0],[],'confirm file delete')
    feval(@spm_unlink,P{:});
    spm('alert"',P,'file delete',1);
end


%=======================================================================
case 'clean'                                    %-Clean MATLAB workspace
%=======================================================================
% spm('Clean')
%-----------------------------------------------------------------------
evalin('base','clear all');
evalc('clear classes');


%=======================================================================
case 'help'                                  %-Pass through for spm_help
%=======================================================================
% spm('Help',varargin)
%-----------------------------------------------------------------------
if nargin>1, spm_help(varargin{2:end}), else spm_help, end


%=======================================================================
case 'quit'                                      %-Quit SPM and clean up
%=======================================================================
% spm('Quit')
%-----------------------------------------------------------------------
spm_figure('close',allchild(0));
local_clc;
fprintf('Bye for now...\n\n');


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

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


%=======================================================================
function local_clc                                %-Clear command window
%=======================================================================
if ~isdeployed
    clc;
end


%=======================================================================
function v = spm_version(ReDo)                    %-Retrieve SPM version
%=======================================================================
persistent SPM_VER;
v = SPM_VER;
if isempty(SPM_VER) || (nargin > 0 && ReDo)
    v = struct('Name','','Version','','Release','','Date','');
    try
        if isdeployed
            % in deployed mode, M-files are encrypted
            % (even if first two lines of Contents.m "should" be preserved)
            vfile = fullfile(spm('Dir'),'Contents.txt');
        else
            vfile = fullfile(spm('Dir'),'Contents.m');
        end
        fid = fopen(vfile,'rt');
        if fid == -1, error(str); end
        l1 = fgetl(fid); l2 = fgetl(fid);
        fclose(fid);
        l1 = strtrim(l1(2:end)); l2 = strtrim(l2(2:end));
        t  = textscan(l2,'%s','delimiter',' '); t = t{1};
        v.Name = l1; v.Date = t{4};
        v.Version = t{2}; v.Release = t{3}(2:end-1);
    catch
        error('Can''t obtain SPM Revision information.');
    end
    SPM_VER = v;
end
spm_select
function varargout = spm_select(varargin)
% File selector
% FORMAT [t,sts] = spm_select(n,typ,mesg,sel,wd,filt,frames)
%     n    - Number of files
%            A single value or a range.  e.g.
%            1       - Select one file
%            Inf     - Select any number of files
%            [1 Inf] - Select 1 to Inf files
%            [0 1]   - select 0 or 1 files
%            [10 12] - select from 10 to 12 files
%     typ  - file type
%           'any'   - all files
%           'image' - Image files (".img" and ".nii")
%                     Note that it gives the option to select
%                     individual volumes of the images.
%           'mesh'  - Surface mesh files (".gii" or ".mat")
%           'xml'   - XML files
%           'mat'   - Matlab .mat files or .txt files (assumed to contain
%                     ASCII representation of a 2D-numeric array)
%           'batch' - SPM batch files (.m, .mat and XML)
%           'dir'   - select a directory
%           Other strings act as a filter to regexp.  This means
%           that e.g. DCM*.mat files should have a typ of '^DCM.*\.mat$'
%      mesg - a prompt (default 'Select files...')
%      sel  - list of already selected files
%      wd   - Directory to start off in
%      filt - value for user-editable filter (default '.*')
%      frames - Image frame numbers to include (default '1')
%
%      t    - selected files
%      sts  - status (1 means OK, 0 means window quit)
%
% FORMAT [t,ind] = spm_select('Filter',files,typ,filt,frames)
% filter the list of files (cell or char array) in the same way as the
% GUI would do. There is an additional typ 'extimage' which will match
% images with frame specifications, too. Also, there is a typ 'extdir',
% which will match canonicalised directory names. The 'frames' argument
% is currently ignored, i.e. image files will not be filtered out if
% their frame numbers do not match.
% t returns the filtered list (cell or char array, depending on input),
% ind an index array, such that t = files{ind}, or t = files(ind,:).
%
% FORMAT cpath = spm_select('CPath',path,cwd)
% function to canonicalise paths: Prepends cwd to relative paths, processes
% '..' & '.' directories embedded in path.
% path     - string matrix containing path name
% cwd      - current working directory [default '.']
% cpath    - conditioned paths, in same format as input path argument
%
% FORMAT [files,dirs] = spm_select('List',direc,filt)
% Returns files matching the filter (filt) and directories within direc
% direc    - directory to search
% filt     - filter to select files with (see regexp) e.g. '^w.*\.img$'
% files    - files matching 'filt' in directory 'direc'
% dirs     - subdirectories of 'direc'
%
% FORMAT [files,dirs] = spm_select('ExtList',direc,filt,frames)
% As above, but for selecting frames of 4D NIfTI files
% frames   - vector of frames to select (defaults to 1, if not
%            specified). If the frame number is Inf, all frames for the
%            matching images are listed.
%
% FORMAT [files,dirs] = spm_select('FPList',direc,filt)
% FORMAT [files,dirs] = spm_select('ExtFPList',direc,filt,frames)
% As above, but returns files with full paths (i.e. prefixes direc to each)
% FORMAT [files,dirs] = spm_select('FPListRec',direc,filt)
% FORMAT [files,dirs] = spm_select('ExtFPListRec',direc,filt,frames)
% As above, but returns files with full paths (i.e. prefixes direc to
% each) and searches through sub directories recursively.
%
% FORMAT spm_select('prevdirs',dir)
% Add directory dir to list of previous directories.
% FORMAT dirs = spm_select('prevdirs')
% Retrieve list of previous directories.
%__________________________________________________________________________
% Copyright (C) 2005-2011 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_select.m 4185 2011-02-01 18:46:18Z guillaume $

if ~isdeployed && ~exist('cfg_getfile','file')
    addpath(fullfile(spm('dir'),'matlabbatch'));
end
% cfg_getfile expects and returns cellstr arguments for multi-line strings
if nargin > 0 && ischar(varargin{1}) && strcmpi(varargin{1},'filter') && ischar(varargin{2})
    varargin{2} = cellstr(varargin{2});
end
[t sts] = cfg_getfile(varargin{:});
% cfg_getfile returns cell arrays, convert to char arrays
if nargin > 0 && ischar(varargin{1})
    switch lower(varargin{1})
        case 'filter',
            if ischar(varargin{2})
                t = char(t);
            end
        case {'list','fplist','extlist','extfplist'},
                t = char(t);
                sts = char(sts);
    end
else
    t = char(t);
end
varargout{1} = t;
if nargout > 1
    varargout{2} = sts;
end
spm_vol
function V = spm_vol(P)
% Get header information for images.
% FORMAT V = spm_vol(P)
% P - a matrix of filenames.
% V - a vector of structures containing image volume information.
% The elements of the structures are:
%       V.fname - the filename of the image.
%       V.dim   - the x, y and z dimensions of the volume
%       V.dt    - A 1x2 array.  First element is datatype (see spm_type).
%                 The second is 1 or 0 depending on the endian-ness.
%       V.mat   - a 4x4 affine transformation matrix mapping from
%                 voxel coordinates to real world coordinates.
%       V.pinfo - plane info for each plane of the volume.
%              V.pinfo(1,:) - scale for each plane
%              V.pinfo(2,:) - offset for each plane
%                 The true voxel intensities of the jth image are given
%                 by: val*V.pinfo(1,j) + V.pinfo(2,j)
%              V.pinfo(3,:) - offset into image (in bytes).
%                 If the size of pinfo is 3x1, then the volume is assumed
%                 to be contiguous and each plane has the same scalefactor
%                 and offset.
%__________________________________________________________________________
%
% The fields listed above are essential for the mex routines, but other
% fields can also be incorporated into the structure.
%
% The images are not memory mapped at this step, but are mapped when
% the mex routines using the volume information are called.
%
% Note that spm_vol can also be applied to the filename(s) of 4-dim
% volumes. In that case, the elements of V will point to a series of 3-dim
% images.
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_vol.m 4045 2010-08-26 15:10:46Z guillaume $

if ~nargin
    V = struct('fname',   {},...
               'dim',     {},...
               'dt',      {},...
               'pinfo',   {},...
               'mat',     {},...
               'n',       {},...
               'descrip', {},...
               'private', {});
    return;
end

% If is already a vol structure then just return
if isstruct(P), V = P; return; end

V = subfunc2(P);


%==========================================================================
function V = subfunc2(P)
if iscell(P)
    V = cell(size(P));
    for j=1:numel(P)
        if iscell(P{j})
            V{j} = subfunc2(P{j});
        else
            V{j} = subfunc1(P{j});
        end
    end
else
    V = subfunc1(P);
end

%==========================================================================
function V = subfunc1(P)
if isempty(P), V = []; return; end
counter = 0;
for i=1:size(P,1)
    v = subfunc(P(i,:));
    [V(counter+1:counter+size(v, 2),1).fname] = deal('');
    [V(counter+1:counter+size(v, 2),1).dim]   = deal([0 0 0 0]);
    [V(counter+1:counter+size(v, 2),1).mat]   = deal(eye(4));
    [V(counter+1:counter+size(v, 2),1).pinfo] = deal([1 0 0]');
    [V(counter+1:counter+size(v, 2),1).dt]    = deal([0 0]);
    if isempty(v)
        hread_error_message(P(i,:));
        error(['Can''t get volume information for ''' P(i,:) '''']);
    end

    f = fieldnames(v);
    for j=1:size(f,1)
        eval(['[V(counter+1:counter+size(v,2),1).' f{j} '] = deal(v.' f{j} ');']);
    end
    counter = counter + size(v,2);
end

%==========================================================================
function V = subfunc(p)
[pth,nam,ext,n1] = spm_fileparts(deblank(p));
p = fullfile(pth,[nam ext]);
n = str2num(n1);
if ~spm_existfile(p)
    error('File "%s" does not exist.', p);
end
switch ext
    case {'.nii','.NII'}
        % Do nothing

    case {'.img','.IMG'}
        if ~spm_existfile(fullfile(pth,[nam '.hdr'])) && ...
           ~spm_existfile(fullfile(pth,[nam '.HDR']))
            error('File "%s" does not exist.', fullfile(pth,[nam '.hdr']));
        end

    case {'.hdr','.HDR'}
        ext = '.img';
        p   = fullfile(pth,[nam ext]);
        if ~spm_existfile(p)
            error('File "%s" does not exist.', p);
        end

    otherwise
        error('File "%s" is not of a recognised type.', p);
end

V = spm_vol_nifti(p,n);

if isempty(n) && length(V.private.dat.dim) > 3
    V0(1) = V;
    for i = 2:V.private.dat.dim(4)
        V0(i) = spm_vol_nifti(p, i);
    end
    V = V0;
end
if ~isempty(V), return; end
return;

%==========================================================================
function hread_error_message(q)
str = {...
    'Error reading information on:',...
    ['        ',spm_str_manip(q,'k40d')],...
    ' ',...
    'Please check that it is in the correct format.'};
spm('alert*',str,mfilename,sqrt(-1));
return;
spm_write_vol
function V = spm_write_vol(V,Y)
% Write an image volume to disk, setting scales and offsets as appropriate
% FORMAT V = spm_write_vol(V,Y)
% V (input)  - a structure containing image volume information (see spm_vol)
% Y          - a one, two or three dimensional matrix containing the image voxels
% V (output) - data structure after modification for writing.
%
% Note that if there is no 'pinfo' field, then SPM will figure out the
% max and min values from the data and use these to automatically determine
% scalefactors.  If 'pinfo' exists, then the scalefactor in this is used.
%_______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_write_vol.m 1143 2008-02-07 19:33:33Z spm $

use_offset = false;

if ndims(Y)>3, error('Can only handle a maximum of 3 dimensions.'), end
dim = [size(Y) 1 1 1];
if ~all(dim(1:3) == V.dim(1:3)),
    error('Incompatible dimensions.');
end

if ~isfield(V,'pinfo'),
    V.pinfo = [1;0;0];
    rescal  = true;
elseif ~all(isfinite(V.pinfo(1:2))) || V.pinfo(1) == 0,
    V.pinfo(1:2) = [1;0];
    rescal  = true;
else
    rescal = false;
end

if rescal,
    % Set scalefactors and offsets
    %-----------------------------------------------------------------------
    dt           = V.dt(1);
    s            = find(dt == [2 4 8 256 512 768]);
    if isempty(s),
        V.pinfo(1:2) = [1;0];
    else
        dmnmx        = [0 -2^15 -2^31 -2^7 0 0 ; 2^8-1 2^15-1 2^31-1 2^7-1 2^16-1 2^32-1];
        dmnmx        = dmnmx(:,s);
        mxs          = zeros(dim(3),1)+NaN;
        mns          = zeros(dim(3),1)+NaN;

        for p=1:dim(3),
            tmp    = double(Y(:,:,p));
            tmp    = tmp(isfinite(tmp));
            if ~isempty(tmp),
                mxs(p) = max(tmp);
                mns(p) = min(tmp);
            end
        end

        mx = max(mxs(isfinite(mxs)));
        mn = min(mns(isfinite(mns)));
        if isempty(mx), mx = 0; end;
        if isempty(mn), mn = 0; end;
        if mx~=mn,
            if use_offset,
                V.pinfo(1,1) = (mx-mn)/(dmnmx(2)-dmnmx(1));
                V.pinfo(2,1) = (dmnmx(2)*mn-dmnmx(1)*mx)/(dmnmx(2)-dmnmx(1));
            else
                if dmnmx(1)<0,
                    V.pinfo(1) = max(mx/dmnmx(2),mn/dmnmx(1));
                else
                    V.pinfo(1) = mx/dmnmx(2);
                end
                V.pinfo(2) = 0;
            end
        else
            V.pinfo(1,1) = mx/dmnmx(2);
            V.pinfo(2,1) = 0;
        end
    end
end

%-Create and write image
%-----------------------------------------------------------------------
V = spm_create_vol(V);
V = spm_write_plane(V,Y,':');