function statusbarHandles = statusbar(varargin)
%statusbar set/get the status-bar of Matlab desktop or a figure
%
% statusbar sets the status-bar text of the Matlab desktop or a figure.
% statusbar accepts arguments in the format accepted by the sprintf
% function and returns the statusbar handle(s), if available.
%
% Syntax:
% statusbarHandle = statusbar(handle, text, sprintf_args...)
%
% statusbar(text, sprintf_args...) sets the status bar text for the
% current figure. If no figure is selected, then one will be created.
% Note that figures with 'HandleVisibility' turned off will be skipped
% (compare findobj & findall).
% In these cases, simply pass their figure handle as first argument.
% text may be a single string argument, or anything accepted by sprintf.
%
% statusbar(handle, ...) sets the status bar text of the figure
% handle (or the figure which contains handle). If the status bar was
% not yet displayed for this figure, it will be created and displayed.
% If text is not supplied, then any existing status bar is erased,
% unlike statusbar(handle, '') which just clears the text.
%
% statusbar(0, ...) sets the Matlab desktop's status bar text. If text is
% not supplied, then any existing text is erased, like statusbar(0, '').
%
% statusbar([handles], ...) sets the status bar text of all the
% requested handles.
%
% statusbarHandle = statusbar(...) returns the status bar handle
% for the selected figure. The Matlab desktop does not expose its
% statusbar object, so statusbar(0, ...) always returns [].
% If multiple unique figure handles were requested, then
% statusbarHandle is an array of all non-empty status bar handles.
%
% Notes:
% 1) The format statusbarHandle = statusbar(handle) does NOT erase
% any existing statusbar, but just returns the handles.
% 2) The status bar is 20 pixels high across the entire bottom of
% the figure. It hides everything between pixel heights 0-20,
% even parts of uicontrols, regardless of who was created first!
% 3) Three internal handles are exposed to the user (Figures only):
% - CornerGrip: a small square resizing grip on bottom-right corner
% - TextPanel: main panel area, containing the status text
% - ProgressBar: a progress bar within TextPanel (default: invisible)
%
% Examples:
% statusbar; % delete status bar from current figure
% statusbar(0, 'Desktop status: processing...');
% statusbar([hFig1,hFig2], 'Please wait while processing...');
% statusbar('Processing %d of %d (%.1f%%)...',idx,total,100*idx/total);
% statusbar('Running... [%s%s]',repmat('*',1,fix(N*idx/total)),repmat('.',1,N-fix(N*idx/total)));
% existingText = get(statusbar(myHandle),'Text');
%
% Examples customizing the status-bar appearance:
% sb = statusbar('text');
% set(sb.CornerGrip, 'visible','off');
% set(sb.TextPanel, 'Foreground',[1,0,0], 'Background','cyan', 'ToolTipText','tool tip...')
% set(sb, 'Background',java.awt.Color.cyan);
%
% % sb.ProgressBar is by default invisible, determinite, non-continuous fill, min=0, max=100, initial value=0
% set(sb.ProgressBar, 'Visible','on', 'Minimum',0, 'Maximum',500, 'Value',234);
% set(sb.ProgressBar, 'Visible','on', 'Indeterminate','off'); % indeterminate (annimated)
% set(sb.ProgressBar, 'Visible','on', 'StringPainted','on'); % continuous fill
% set(sb.ProgressBar, 'Visible','on', 'StringPainted','on', 'string',''); % continuous fill, no percentage text
%
% % Adding a checkbox
% jCheckBox = javax.swing.JCheckBox('cb label');
% sb.add(jCheckBox,'West'); % Beware: East also works but doesn't resize automatically
%
% Notes:
% Statusbar will probably NOT work on Matlab versions earlier than 6.0 (R12)
% In Matlab 6.0 (R12), figure statusbars are not supported (only desktop statusbar)
%
% 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:
% 2007-May-04: Added partial support for Matlab 6
% 2007-Apr-29: Added internal ProgressBar; clarified main comment
% 2007-Apr-25: First version posted on MathWorks file exchange: http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=14773
%
% See also:
% ishghandle, sprintf, findjobj (on the file exchange)
% License to use and modify this code is granted freely without warranty to all, 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
% $Revision: 1.0 $ $Date: 2007/04/25 16:43:24 $
% Check for available Java/AWT (not sure if Swing is really needed so let's just check AWT)
if ~usejava('awt')
error('YMA:statusbar:noJava','statusbar only works on Matlab envs that run on java');
end
% Args check
if nargin < 1 | ischar(varargin{1}) %#ok for Matlab 6 compatibility
handles = gcf; % note: this skips over figures with 'HandleVisibility'='off'
else
handles = varargin{1};
varargin(1) = [];
end
% Ensure that all supplied handles are valid HG GUI handles (Note: 0 is a valid HG handle)
if isempty(handles) | ~all(ishandle(handles)) %#ok for Matlab 6 compatibility
error('YMA:statusbar:invalidHandle','invalid GUI handle passed to statusbar');
end
% Retrieve the requested text string (only process once, for all handles)
if isempty(varargin)
deleteFlag = (nargout==0);
updateFlag = 0;
statusText = '';
else
deleteFlag = 0;
updateFlag = 1;
statusText = sprintf(varargin{:});
end
% Loop over all unique root handles (figures/desktop) of the supplied handles
rootHandles = [];
if any(handles) % non-0, i.e. non-desktop
try
rootHandles = ancestor(handles,'figure');
if iscell(rootHandles), rootHandles = cell2mat(rootHandles); end
catch
errMsg = 'Matlab version is too old to support figure statusbars';
% Note: old Matlab version didn't have the ID optional arg in warning/error, so I can't use it here
if any(handles==0)
warning([errMsg, '. Updating the desktop statusbar only.']); %#ok for Matlab 6 compatibility
else
error(errMsg);
end
end
end
rootHandles = unique(rootHandles);
if any(handles==0), rootHandles(end+1)=0; end
statusbarObjs = handle([]);
for rootIdx = 1 : length(rootHandles)
if rootHandles(rootIdx) == 0
setDesktopStatus(statusText);
else
thisStatusbarObj = setFigureStatus(rootHandles(rootIdx), deleteFlag, updateFlag, statusText);
if ~isempty(thisStatusbarObj)
statusbarObjs(end+1) = thisStatusbarObj;
end
end
end
% If statusbarHandles requested
if nargout
% Return the list of all valid (non-empty) statusbarHandles
statusbarHandles = statusbarObjs;
end
%end % statusbar %#ok for Matlab 6 compatibility
%% Set the status bar text of the Matlab desktop
function setDesktopStatus(statusText)
try
% First, get the desktop reference
try
desktop = com.mathworks.mde.desk.MLDesktop.getInstance; % Matlab 7+
catch
desktop = com.mathworks.ide.desktop.MLDesktop.getMLDesktop; % Matlab 6
end
% Schedule a timer to update the status text
% Note: can't update immediately, since it will be overridden by Matlab's 'busy' message...
try
t = timer('Name','statusbarTimer', 'TimerFcn',{@setText,desktop,statusText}, 'StartDelay',0.05, 'ExecutionMode','singleShot');
start(t);
catch
% Probably an old Matlab version that still doesn't have timer
desktop.setStatusText(statusText);
end
catch
%if any(ishandle(hFig)), delete(hFig); end
error('YMA:statusbar:desktopError',['error updating desktop status text: ' lasterr]);
end
%end %#ok for Matlab 6 compatibility
%% Utility function used as setDesktopStatus's internal timer's callback
function setText(varargin)
if nargin == 4 % just in case...
targetObj = varargin{3};
statusText = varargin{4};
targetObj.setStatusText(statusText);
else
% should never happen...
end
%end %#ok for Matlab 6 compatibility
%% Set the status bar text for a figure
function statusbarObj = setFigureStatus(hFig, deleteFlag, updateFlag, statusText)
try
jFrame = get(hFig,'JavaFrame');
jFigPanel = get(jFrame,'FigurePanelContainer');
jRootPane = jFigPanel.getComponent(0).getRootPane;
% If invalid RootPane, retry up to N times
tries = 10;
while isempty(jRootPane) & tries>0 %#ok for Matlab 6 compatibility - might happen if figure is still undergoing rendering...
drawnow; pause(0.001);
tries = tries - 1;
jRootPane = jFigPanel.getComponent(0).getRootPane;
end
jRootPane = jRootPane.getTopLevelAncestor;
% Get the existing statusbarObj
statusbarObj = jRootPane.getStatusBar;
% If status-bar deletion was requested
if deleteFlag
% Non-empty statusbarObj - delete it
if ~isempty(statusbarObj)
jRootPane.setStatusBarVisible(0);
end
elseif updateFlag % status-bar update was requested
% If no statusbarObj yet, create it
if isempty(statusbarObj)
statusbarObj = com.mathworks.mwswing.MJStatusBar;
jProgressBar = javax.swing.JProgressBar;
jProgressBar.setVisible(false);
statusbarObj.add(jProgressBar,'West'); % Beware: East also works but doesn't resize automatically
jRootPane.setStatusBar(statusbarObj);
end
statusbarObj.setText(statusText);
jRootPane.setStatusBarVisible(1);
end
statusbarObj = handle(statusbarObj);
% Add quick references to the corner grip and status-bar panel area
if ~isempty(statusbarObj)
addOrUpdateProp(statusbarObj,'CornerGrip', statusbarObj.getParent.getComponent(0));
addOrUpdateProp(statusbarObj,'TextPanel', statusbarObj.getComponent(0));
addOrUpdateProp(statusbarObj,'ProgressBar', statusbarObj.getComponent(1).getComponent(0));
end
catch
try
title = jFrame.fFigureClient.getWindow.getTitle;
catch
title = get(hFig,'Name');
end
error('YMA:statusbar:figureError',['error updating status text for figure ' title ': ' lasterr]);
end
%end %#ok for Matlab 6 compatibility
%% Utility function: add a new property to a handle and update its value
function addOrUpdateProp(handle,propName,propValue)
try
if ~isprop(handle,propName)
schema.prop(handle,propName,'mxArray');
end
set(handle,propName,propValue);
catch exception
% never mind...
end
%end %#ok for Matlab 6 compatibility