function clickUIcontrol(h, focus, opt)
%CLICKUICONTROL Emulate clicking on a UIControl object
% CLICKUICONTROL(H) behaves as if clicking on a UIControl object. The
% object can be one of the following: pushbutton, checkbox, radiobutton,
% togglebutton, slider, listbox, popupmenu. The Value property of the
% object will be appropriately set, and the object's Callback is called
% (ButtonDownFcn will be called if the object's Enable property is set to
% OFF or INACTIVE). It will work with callbacks defined as strings or
% function handles. The UICONTROL will not be brought to focus.
%
% CLICKUICONTROL(H, 'focus') brings focus to the object. This is useful
% for bringing focus to an EDIT box. Default is 'nofocus'.
%
% CLICKUICONTROL(H, OPT) or CLICKUICONTROL(H, 'focus', OPT)
% Some objects accept a additional argument OPT. Acceptable arguments are
% dependent on the style of UIControl object.
%
% SLIDER:
% '-' : Click on the decreasing arrow.
% '+' : Click on the increasing arrow.
% '--' : Click on the decreasing slider trough.
% '++' : Click on the increasing slider trough.
% '.' : Click without change of value.
%
% LISTBOX:
% 'top' : Select the top-most element.
% 'end' : Select the last element.
% '+' : Select the next element.
% '-' : Select the previous element.
% '.' : Select the current element.
% N : Select the N-th element. Can also be an array if multi
% selection is permitted.
%
% POPUPMENU:
% 'top' : Select the top-most element.
% 'end' : Select the last element.
% '+' : Select the next element.
% '-' : Select the previous element.
% '.' : Select the current element.
% N : Select the N-th element.
%
% Example:
%
% h1 = uicontrol('style', 'slider', ...
% 'position', [5 5 150 20], ...
% 'callback', 'disp(get(h1, ''value''));');
% h2 = uicontrol('style', 'pushbutton', ...
% 'position', [5 40 150 20], ...
% 'string', 'Change Figure Color', ...
% 'callback', 'set(gcbf, ''Color'', rand(1, 3))');
% h3 = uicontrol('style', 'popupmenu', ...
% 'position', [5 75 150 20], ...
% 'string', {'Red', 'Green', 'Blue'}, ...
% 'value', 1, ...
% 'callback', 'fprintf(''Item # %d selected.\n'', get(gcbo, ''value''))');
% h4 = uicontrol('style', 'edit', ...
% 'position', [5 110 150 20]);
%
% clickUIcontrol(h1, '++'); % increase slider value
% clickUIcontrol(h2); % click on button 2
% clickUIcontrol(h3, 2); % select item 2 of menu
% clickUIcontrol(h4, 'focus') % put cursor in edit box
% VERSIONS:
% v1.0 - first version
% v1.1 - modify code based on MLINT
% v1.2 - fix bug so that it works with UICONTROLs in panels. Also add
% ability to set focus
%
% Copyright 2009 The MathWorks, Inc.
error(nargchk(1, 3, nargin, 'struct'));
if numel(h) ~= 1 || ~ishandle(h) || ~strcmpi(get(h, 'type'), 'uicontrol')
error('clickUIcontrol:InvalidHandle', ...
'It must be a handle to a UICONTROL');
end
if nargin == 1
focus = 'nofocus';
opt = '';
elseif nargin == 2
if ischar(focus) && ismember(focus, {'focus', 'nofocus'})
opt = '';
else
opt = focus;
focus = 'nofocus';
end
else
if ischar(focus) && ismember(focus, {'focus', 'nofocus'})
else
if ischar(opt) && ismember(opt, {'focus', 'nofocus'})
[focus, opt] = deal(opt, focus);
else
error('clickUIcontrol:InvalidInputArgs', ...
'One of the input arguments must be either ''%s'' or ''%s''', ...
'focus', 'nofocus');
end
end
end
cb = '';
switch lower(get(h, 'style'))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PUSHBUTTON
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'pushbutton'
if ~isempty(opt)
warning('clickUIcontrol:IgnoreOption', 'Ignoring option...');
end
if strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'callback');
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% CHECKBOX, RADIOBUTTON, TOGGLEBUTTON
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case {'checkbox', 'radiobutton', 'togglebutton'}
if ~isempty(opt)
warning('clickUIcontrol:IgnoreOption', 'Ignoring option...');
end
if strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'callback');
if get(h, 'value') == get(h, 'max')
set(h, 'value', get(h, 'min'));
else
set(h, 'value', get(h, 'max'));
end
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SLIDER
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'slider'
if strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'callback');
sliderStep = get(h, 'sliderstep');
sliderMax = get(h, 'max');
sliderMin = get(h, 'min');
sliderVal = get(h, 'value');
steps = [- (sliderMax - sliderMin) * sliderStep, ...
(sliderMax - sliderMin) * sliderStep, 0];
optChoices = {'-', '--', '+', '++', '.'};
if isempty(opt)
opt = '.';
elseif ~ischar(opt) || ~ismember(opt, optChoices)
error('clickUIcontrol:InvalidSliderStep', ...
'Invalid step option for Slider.');
end
id = strmatch(opt, optChoices, 'exact');
newSliderVal = sliderVal + steps(id);
% make sure the new value is within the range:
newSliderVal = max([min([newSliderVal, sliderMax]), sliderMin]);
set(h, 'value', newSliderVal);
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% LISTBOX
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'listbox'
if strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'callback');
listboxMax = get(h, 'max');
listboxMin = get(h, 'min');
listboxStr = get(h, 'string');
listboxVal = get(h, 'value');
if ischar(listboxStr)
listboxStr = {listboxStr};
end
isMulti = (listboxMax - listboxMin) > 1;
optChoices = {'top', 'end', '+', '-', '.'};
if isempty(opt)
opt = '.';
elseif ~ischar(opt) && ~isnumeric(opt)
error('clickUIcontrol:InvalidListBoxOption', ...
'Option must be either a string or a numeric array.');
elseif ischar(opt) && ~ismember(opt, optChoices)
error('clickUIcontrol:InvalidListBoxItem', ...
'Invalid option for ListBox.');
elseif isnumeric(opt) && min(size(opt)) > 1
error('clickUIcontrol:VectorListBox', ...
'Option must be 1-by-N numeric array.');
end
if ischar(opt)
switch opt
case 'top'
listboxVal = 1;
case 'end'
listboxVal = length(listboxStr);
case '+'
listboxVal = min([length(listboxStr), listboxVal(end)+1]);
case '-'
listboxVal = max([1, listboxVal(1)-1]);
end
else % is a numeric array
if ~isMulti && length(opt) > 1
error('clickUIcontrol:SingleSelectListBox', ...
'The ListBox is a single-select.');
else
if any(opt ~= fix(opt)) || min(opt) < 1 || ...
max(opt) > length(listboxStr)
error('clickUIcontrol:InvalidListBoxValue', ...
'Invalid value for ListBox.');
end
listboxVal = opt;
end
end
set(h, 'string', listboxStr, 'value', listboxVal);
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% POPUPMENU
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'popupmenu'
if strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'callback');
str = cellstr(get(h, 'string'));
val = get(h, 'value');
optChoices = {'top', 'end', '+', '-', '.'};
if isempty(opt)
opt = '.';
elseif ~ischar(opt) && ~isnumeric(opt)
error('clickUIcontrol:InvalidPopupMenuOption', ...
'Option must be either a string or a scalar.');
elseif ischar(opt) && ~ismember(opt, optChoices)
error('clickUIcontrol:InvalidPopupMenuItem', ...
'Invalid option for PopupMenu.');
elseif isnumeric(opt) && numel(opt) > 1
error('clickUIcontrol:ScalarPopupMenu', ...
'Option must be a scalar.');
end
if ischar(opt)
switch opt
case 'top'
val = 1;
case 'end'
val = length(str);
case '+'
val = min([length(str), val+1]);
case '-'
val = max([1, val-1]);
end
else % is a scalar
if opt ~= fix(opt) || opt < 1 || opt > length(str)
error('clickUIcontrol:InvalidMenuItem', ...
'The value must be an integer within [1, %d].', length(str));
end
val = opt;
end
set(h, 'value', val);
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EDIT BOX
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'edit'
if ~isempty(opt)
warning('clickUIcontrol:IgnoreOption', 'Ignoring option...');
end
if strcmpi(get(h, 'enable'), 'on')
% just give focus (see below)
else
cb = get(h, 'buttondownfcn');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% TEXT BOX
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
case 'text'
if ~isempty(opt)
warning('clickUIcontrol:IgnoreOption', 'Ignoring option...');
end
if ~strcmpi(get(h, 'enable'), 'on')
cb = get(h, 'buttondownfcn');
end
otherwise
error('clickUIcontrol:InvalidUIControl', 'Invalid UI Control');
end
% set the current figure and object
hFig = ancestor(h, 'figure');
set(0, 'CurrentFigure', hFig);
set(hFig, 'CurrentObject', h);
% evaluate the Callback function
evaluateCallback(cb, h);
if strcmp(focus, 'focus')
% bring focus
uicontrol(h);
end
end
%%-------------------------------------------------------------------------
% Helper function for evaluating the Callback function of the UIControl
function evaluateCallback(cb, h)
if ~isempty(cb)
switch class(cb)
case 'function_handle'
feval(cb, h, []);
case 'cell'
if strcmpi(class(cb{1}), 'function_handle')
feval(cb{1}, h, [], cb{2:end});
end
case 'char'
%%% Attempt to replace GCBO and GCBF with the appropriate GCO and GCF
% This is necessary because Matlab will not recognize the original
% callback object.
cb = strrep(cb, 'gcbo', 'gco');
cb = strrep(cb, 'gcbf', 'gcf');
evalin('base', cb);
end
end
end