Code covered by the BSD License  

Highlights from
plotLDS

from plotLDS by Sebastian Hölz
Enhance zooming and panning on large data sets by automatically downsampling data

CallbackStack(varargin)
%
% Add/remove arbitrary number of callbacks on a callback-stack for objects of type ...
%
%   figure, axes, line, patch, surface, text, image, zoom, pan, rotate.
%
% Using this utility makes it possible to add multiple callbacks to the callback-function of an objects
% without overwriting any already existing callbacks.
%
% SYNTAX
% ======
% CallbackStack(obj, cb_type, callback [,'remove' or 'add'])
%
% ARGUMENTS
% =========
% obj       (Vector of) object-handle(s) or object(s) as returned from z = zoom; p = pan; r = rotate3d;
% cb_type   Depends on object type, e.g. 'ButtonDownFcn', if obj is an axis.
% callback  Function handle to be evaluated as action callback (s. NOTES below !!!)
% 'remove'  Optional argument to remove already existing callback(s) instead of adding it (s. NOTES).
% 'add'     Optional argument to ALLWAYS add callback, even if it already exists (s. NOTES).
%
% EXAMPLES (execute one after the other and trigger the callbacks afterwards by zooming, paning or clicking)
% ========
% gca;                                                              % Make sure that there is an existing axis
% CallbackStack(pan,       'ActionPostCallback','disp(1)')          % Create first callback for pan-object
% CallbackStack(zoom,      'ActionPostCallback','disp(2)')          % Create first callback for zoom-object
% CallbackStack([zoom pan],'ActionPostCallback','disp(3)')          % Additional callbacks for pan & zoom !!!
% CallbackStack(pan,       'ActionPostCallback','disp(1)','remove') % Now remove one callback
%
% CallbackStack([gcf gca], 'ButtonDownFcn','disp(''Hello'')')       % Same as above, but for figure and axis-objects
% CallbackStack(gca,       'ButtonDownFcn','disp(''World'')')
%
% NOTES
% =====
% - Callbacks may be specified as strings (s. examples), simple function handles (@MyFcn), cell-array
%   of function handle and arguments ( {@MyFcn,arg1,arg2, ...} ). The use of anonymous function
%   handles may work, but has not been tested ...
% - "CallbackStack(gca, 'Button', 'disp(1)')" will cast error, since no abbreviatons of callback-names are allowed,
%   i.e. 'ButtonDownFcn' => 'Button' is not allowed
% - It is completely your responsibility to make sure that different callbacks do not interfere with each other !!!
% - You may only specify 'remove' OR 'add' as input argument, not both at the same time
%

% AUTHOR
% ======
% Sebastian Hlz (XXX_shoelzATifm-geomar.de_YYY)
%
% BUGS & ToDo
% ===========
%
% VERSION
% =======
% 21.01.2009    0.92    Removed bug in add-switch.
% 10.01.2009    0.91    Callbacks are now casted to the form {@fhandle, 'arg1', ..., argN)
%                       This makes it easier to compare the callback-stack and extends the scope of the function.
% 01.10.2008    0.9     First Release, please report any bugs.
%
function CallbackStack(varargin)
    
    % Parse input
    try [obj,cb_type,callback, add,remove] = local_ParseInput(varargin{:}); catch rethrow(lasterror); end
    
    % The callback of each object is redirected to -> "function local_callback"
    
    for i=1:length(obj)
        
        % Get current callback
        CC = get(obj(i),cb_type);
        
        if isempty(CC)
            CBs = {};
            
        elseif iscell(CC) && length(CC)==4 && isa(CC{1}, 'function_handle') && isequal(CC{1}, @local_callback)
            CBs = CC{end};
            
        else
            CBs = {CC};
            
        end
        
        % Check, if this callback has already been added
        if remove && ~isempty(CBs)
            for j = length(CBs):-1:1
                if isequal(CBs{j},callback) 
                    CBs(j) = [];
                end
            end
        end
        
        % Add callback to structure
        if add; CBs{end+1} = callback; end %#ok<AGROW>
        
        set(obj(i),cb_type,{@local_callback, obj(i), cb_type, CBs});
    end

end

% ==========================================================
function local_callback(dummy1, dummy2, obj, cb_type, CBs)

    for i_cb = 1:length(CBs)
        if ischar(CBs{i_cb})
            eval (CBs{i_cb})
            
        elseif iscell(CBs{i_cb})
            feval(CBs{i_cb}{:})
            
        elseif isa(CBs{i_cb},'function_handle')
            feval(CBs{i_cb})
            
        else
            warning('Unsupported type of function specification'); %#ok<WNTAG>
            disp(CBs{i_cb})
        end
    end

end
    
% ==========================================================
function [OBJ,cb_type,callback,add,remove] = local_ParseInput(varargin)
    
    error(nargchk(3, 4, nargin))
    add = 1; remove = 1;
    
    if nargin==4 && strcmpi(varargin{4},'remove'); add = 0; end
    if nargin==4 && strcmpi(varargin{4},'add');    remove = 0; end
    
    OBJ = varargin{1};
    for i = 1:length(OBJ)
        obj = OBJ(i);

        % Expand the errorchecking in this loop, if you need to add callbacks to objects,
        % which are currently not implemented here (e.g. for the timer-object ...)
        if any(strcmp({'graphics.pan' 'graphics.zoom' 'graphics.rotate3d'}, class(obj)))
            % Note that these callbacks are only available for Matlab 7.3 (?!) or higher
            valid_callbacks = {'ActionPreCallback' 'ActionPostCallback' 'ButtonDownFilter'};
            obj_type = class(obj);
            
        elseif ishandle(obj) && any(strcmpi(get(obj,'type'),{'axes' 'line' 'patch' 'surface' 'image' 'text'}))
            valid_callbacks = {'ButtonDownFcn' 'CreateFcn' 'DeleteFcn'};
            obj_type = get(obj,'type');
            
        elseif ishandle(obj) && any(strcmpi(get(obj,'type'),'figure'))
            valid_callbacks = {'ButtonDownFcn' 'CloseRequestFcn' 'CreateFcn' 'DeleteFcn' 'KeyPressFcn' 'KeyReleaseFcn' ...
                'ResizeFcn' 'WindowButtonDownFcn' 'WindowButtonMotionFcn' 'WindowButtonUpFcn' 'WindowScrollWheelFcn' };
            obj_type = 'figure';
            
        end
    
        cb_type = varargin{2};
        if ~ischar(cb_type) || ~any(strcmpi(valid_callbacks,cb_type))
            error([ sprintf('\n\tError in specifying callback for object of type ''%s''. Valid callbacks are ...\n\n\t',obj_type) ...
                    sprintf(' ''%s'' ',valid_callbacks{:})])
        end
    end
    
    % All callbacks are casted into the form {@MyFcn, arg1, ... , argN}
    callback = varargin{3};
    if ischar(callback)
        callback = regexp(callback,'\w*','match');
        callback{1} = str2func(callback{1});
        
    elseif isa(callback,'function_handle')
        fcn_tmp = functions(callback);
        if ~strcmp(fcn_tmp.type,'simple'); disp(sprintf('\n\tFunctions-handles of type "%s" may cause problems. Use at own risk.\n',fcn_tmp.type)); end
        callback = {callback};
        
    elseif iscell(callback) && isa(callback{1},'function_handle')
        % This is ok
    else
        error('Third argument needs to be a valid function handle')
    end
    
end
    

Contact us at files@mathworks.com