classdef keyControlPlugin < handle
%KEYCONTROLPLUGIN key-control plugin for plot figures
% KEYCONTROLPLUGIN is a simple class whose 'KEYPRESSFUNCTION' method
% can be used as a callback function for plot figures in order to
% add basic key-enabled scrolling and zooming functionality.
%
% Usage example:
% t = 0:0.01:100 ;
% y = sin(t) ;
% c = keyControlPlugin('scrollFactor',0.25) ;
% plot(t,y) ;
% set(gcf,'KeyPressFcn',@c.keyPressFunction)
% c.keyPressFunctionInit(gcf)
%
% For more examples see the attached TESTPLOTCONTROL script.
%
% Use this plugin in combination with LINKAXES in order to control
% multiple plot figures, see examples
%
% Options: (key-value style)
% ydisabled: [ false | true ], default: false
% zoomFactor: [ 0.1 ... 0.9 ], default: 0.5
% scrollFactor: [ 0 ... 1 ], default: 0.5
%
% These keys are available in the current version:
% h ... list of available commands
% n ... next slice (jump by scrollFactor)
% b ... backwards (jump by scrollFactor)
% i ... zoom in (visiblerange = visiblerange*zoomFactor)
% o ... zoom out (visiblerange = visiblerange*(1+zoomFactor))
%
% These modifiers are available
% Shift n ... jump to the beginning
% Shift b ... jump to the end
% Shift i ... zoom in (fast mode)
% Shift o ... show entire axis range
% Alt <COMMAND> ... apply any command to the y-axis
% AltShift <COMMAND> ... apply shift command to y-axis
%
% Known issues:
% - In case the figure is not in default mode, the plugin may fail;
% it may even fail to be initialized. You will either notice
% no change or see a warning message.
% Use figure(...,'ToolBar','none') in case you won't use the mouse
% anyway.
%
%
% See also FIGURE, LINKAXES.
% georg ogris ::: spantec.at ::: fall 2011
% Copyright (c) 2011 ::: georg ogris
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
% * Neither the name of the author nor the names
% of its contributors may be used to endorse or promote products derived
% from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% -------------------------------------------------------------------
properties (SetAccess = private, Hidden=true)
xmin ;
ymin ;
xmax ;
ymax ;
axes ;
end
% -------------------------------------------------------------------
properties (SetAccess = private)
ydisabled = false ;
end
% -------------------------------------------------------------------
properties (SetAccess = public)
scrollFactor = 0.5 ;
zoomFactor = 0.5 ;
end
% -------------------------------------------------------------------
methods
% ---------------------------------------------------------------
function k = keyControlPlugin(varargin)
nV = numel(varargin) ;
if mod(nV,2)
error('Options should be passed in key-value-style.')
end
%% parse options
for iV = 1:2:nV
if strcmpi('ydisabled',varargin{iV})
if ~islogical(varargin{iV+1}) || length(varargin{iV+1}) ~= 1
error('''ydisabled'' must be either true or false.') ;
end
k.ydisabled = varargin{iV+1} ;
elseif strcmpi('scrollFactor',varargin{iV})
if varargin{iV+1} < 0 || varargin{iV+1} > 1
error('''scrollFactor'' must be in [0 ... 1].') ;
end
k.scrollFactor = varargin{iV+1} ;
elseif strcmpi('zoomFactor',varargin{iV})
if varargin{iV+1} < 0.1 || varargin{iV+1} > 0.9
error('''zoomFactor'' must be in [0.1 ... 0.9].') ;
end
k.zoomFactor = varargin{iV+1} ;
end
end
end
% ---------------------------------------------------------------
function keyPressFunction(k,h,e)
%% initialize maximum ratings
if isempty(k.xmax)
k.keyPressFunctionInit(h) ;
end
%% basic checking whether something has to be changed
switch e.Key
case {'n','b','i','o'}
k.axes = get(h,'Children') ;
k.axes = k.axes(ishghandle(k.axes)) ;
nA = length(k.axes) ;
if ~nA
return
end
case 'h'
k.helptext() ;
otherwise
return
end
%% modifier key handling
mod = [e.Modifier{:}] ;
if isempty(mod)
current.lim = 'xlim' ;
current.max = k.xmax ;
current.min = k.xmin ;
current.fun = @xlim ;
current.ext = false ;
else
switch mod
case 'alt'
if k.ydisabled
return
end
current.lim = 'ylim' ;
current.max = k.ymax ;
current.min = k.ymin ;
current.fun = @ylim ;
current.ext = false ;
case 'shift'
current.lim = 'xlim' ;
current.max = k.xmax ;
current.min = k.xmin ;
current.fun = @xlim ;
current.ext = true ;
case {'shiftalt','altshift'}
if k.ydisabled
return
end
current.lim = 'ylim' ;
current.max = k.ymax ;
current.min = k.ymin ;
current.fun = @ylim ;
current.ext = true ;
otherwise
return
end
end
%% the actual key-stroke handling
switch e.Key
case 'n'
l = get(k.axes(1),current.lim) ;
if l(2) >= current.max
return
end
r = diff(l) ;
l = l + r*k.scrollFactor ;
if l(2)>current.max || current.ext
l = [current.max-r current.max] ;
end
for iA = 1:length(k.axes)
current.fun(k.axes(iA),l)
end
case 'b'
l = get(k.axes(1),current.lim) ;
if l(1) <= current.min
return
end
r = diff(l) ;
l = l - r*k.scrollFactor ;
if l(1)<current.min || current.ext
l = [current.min current.min+r] ;
end
for iA = 1:length(k.axes)
current.fun(k.axes(iA),l)
end
case 'i'
l = get(k.axes(1),current.lim) ;
if ~current.ext
r = diff(l)*(k.zoomFactor/2) ;
else
r = diff(l)*0.45 ;
end
l = l + [r -r] ;
for iA = 1:length(k.axes)
current.fun(k.axes(iA),l)
end
case 'o'
l = get(k.axes(1),current.lim) ;
r = diff(l)*(1+k.zoomFactor/2) ;
l = l + [-r r] ;
if l(1)<current.min || current.ext
l(1) = current.min ;
end
if l(2)>current.max || current.ext
l(2) = current.max ;
end
for iA = 1:length(k.axes)
current.fun(k.axes(iA),l)
end
end
end
end
% -------------------------------------------------------------------
methods (Access = private)
% ---------------------------------------------------------------
function keyPressFunctionInit(k,h)
if ~ishghandle(h)
figure(h)
return
end
k.axes = get(h,'Children') ;
k.axes = k.axes(ishghandle(k.axes)) ;
nA = length(k.axes) ;
if ~nA
return
end
xl = get(k.axes(1),'xlim') ;
k.xmin = xl(1) ;
k.xmax = xl(2) ;
for iA = 2:length(k.axes)
xl = get(k.axes(iA),'xlim') ;
k.xmin = min(k.xmin,xl(1)) ;
k.xmax = max(k.xmax,xl(2)) ;
end
if ~k.ydisabled
yl = get(k.axes(1),'ylim') ;
k.ymin = yl(1) ;
k.ymax = yl(2) ;
for iA = 2:length(k.axes)
yl = get(k.axes(iA),'ylim') ;
k.ymin = min(k.ymin,yl(1)) ;
k.ymax = max(k.ymax,yl(2)) ;
end
end
for iA = 1:length(k.axes)
xlim([k.xmin k.xmax]);
if ~k.ydisabled
ylim([k.ymin k.ymax]);
end
end
end
end
% -------------------------------------------------------------------
methods (Static = true)
% ---------------------------------------------------------------
function helptext()
fprintf(' \n') ;
fprintf(' These keys are available in the current version:\n') ;
fprintf(' h ... this list\n');
fprintf(' n ... next slice (jump by scrollFactor)\n') ;
fprintf(' b ... backwards (jump by scrollFactor)\n') ;
fprintf(' i ... zoom in (visiblerange = visiblerange*zoomFactor)\n') ;
fprintf(' o ... zoom out (visiblerange = visiblerange*(1+zoomFactor))\n') ;
fprintf(' \n') ;
fprintf(' These modifiers are available\n') ;
fprintf(' Shift n ... jump to the beginning\n') ;
fprintf(' Shift b ... jump to the end\n') ;
fprintf(' Shift i ... zoom in (fast mode)\n') ;
fprintf(' Shift o ... show entire axis range\n') ;
fprintf(' Alt <COMMAND> ... apply any command to the y-axis\n') ;
fprintf(' AltShift <COMMAND> ... apply shift command to y-axis\n') ;
fprintf(' \n') ;
end
end
end