function moveplot(h,opt,tool)
% function moveplot(h,option)
%
% INPUTS:      h = handle to a line object (e.g. created by a plot command)
%         option = [  'x' | 'y' | {'xy'} | 'axy' ]
%
% This function, once enabled, allows the user to manipulate the coordinates of any
% point in a line object specified by handle h, simply by clicking and moving the mouse.
%
% If the function is invoked with no option, or with the option 'xy', then clicking the mouse 
% over the line will allow the user to drag the point to a new x- and y- coordinate, as long 
% as the new x coordinate is between the two adjacent points.  If the function is invoked 
% with the option 'x' or 'y' then the point will only move in the specified coordinate, 
% leaving the other coordinate fixed. 
%
% If the function is called with the option 'x' or with the option 'axy', the x-coordinate 
% will not be limited to values between adjacent points.
%
% When the button is released, the point will take on the new values.
%
% EXAMPLE:
% >> h = plot(x, sin(x));
% >> moveplot(h,'xy')
% >>
%
% If the function is invoked on a line which already uses the moveplot function, then the
% function will be disabled.  If the function is called on a line with z-data, then the
% function will return an error and not be invoked.
%
% The function can also be disabled by calling it with the opt argument equal to 'off'.
%
% EXAMPLE:
% >> moveplot(h,'off')
% >>
%
% This function makes use of the current axes' 'userdata' property, which must be made available
% to it.  During the immediate execution of the function (i.e. as long as the button is being held down), 
% the axes' 'userdata' property is unavailable; however, upon the release of the mouse button, the
% function restores the axes' userdata to its initial value.
%
% Written by Brandon Kuczenski for Kensington Labs
% brandon_kuczenski@kensingtonlabs.com
% 17 September 2001
%
% Modified to restore button functions to original state
% And test for double-click to end moveplot action
% Alex Woo, woo@geomi.com
% 26 February 2002
%

switch nargin
case 0
    error('Must Specify a handle to a line object')
case 1
    opt='xy';
case 2
    if ~any(strcmp(opt,{'x','y','xy','axy','a','off'}))
        error(['Second argument ''' opt ''' is an unknown command option.'])
    end
end

if nargin<3  % i.e. user-invoked.  
    b=get(h,'tag');
    if ~isempty(b)&strcmp(b,'moveplot')&(nargin==1|strcmp(opt,'off'))  % function being de-invoked
        k=get(h,'userdata');
        %  acw modified to recall buttondownfcn and restore when turned off
        set(h,'buttondownfcn',k.bdfcn,'markersize',k.ms,'marker',k.m,'markerfacecolor',k.mfc,'userdata','','tag','')
    else % function being invoked
        if ~isempty(get(h,'zdata'))
            error('moveplot only works for 2-D graphs')
        end
        if strcmp(opt,'a')
            opt='axy';
        end
        if isempty(b) % only assign original values to marker struct if function is being called the first time
            k.m=get(h,'marker');set(h,'marker','o');
            k.ms=get(h,'markersize');set(h,'markersize',4);
            k.mfc=get(h,'markerfacecolor');set(h,'markerfacecolor',get(h,'color'));
        else
            k=get(h,'userdata');
        end
        k.opt=opt;
        k.bdfcn=get(h,'buttondownfcn'); % save the current buttondownfcn before reset
        set(h,'buttondownfcn',['moveplot(gco,''' opt ''',1)'],'userdata',k,'tag','moveplot');
        set(findobj('children',h),'units','pixels')
    end
else  % i.e. self-invoked
    if strcmp(get(gcf,'selectiontype'),'open')
        moveplot(h,'off');
    else
        switch tool
        case 1 % line's buttondownfcn invoked
            if ~isempty(get(h,'zdata'))
                moveplot(h)
                error('moveplot only works for 2-D graphs')
            end
            cp=get(gca,'currentpoint');
            k=get(h,'userdata');
            y=abs(get(h,'ydata')-cp(2,2));x=abs(get(h,'xdata')-cp(1,1)); % determine which point the user clicked on
            % just use the distance function
            d=sqrt(y.^2+x.^2);
            k.index=find(d==min(d));
            k.axesdata=get(gca,'userdata');
            k.doublebuffer=get(gcf,'doublebuffer');
            k.winbmfcn = get(gcf,'windowbuttonmotionfcn');  %  save current window motion function
            k.winbupfcn = get(gcf,'windowbuttonupfcn');  %  save current window up function
            k.winbdfcn = get(gcf,'windowbuttondownfcn');  %  save current window down function
            
            set(h,'userdata',k);
            set(gcf,'windowbuttonmotionfcn',['moveplot(get(gca,''userdata''),''' opt ''',2)'],'doublebuffer','on');
            set(gca,'userdata',h);
            set(gcf,'windowbuttonupfcn',['moveplot(get(gca,''userdata''),''' opt ''',3)']);
            
            %         end    
        case 2 % button motion function
            k=get(h,'userdata');
            cp=get(gca,'currentpoint');
            x=get(h,'xdata');
            y=get(h,'ydata');
            switch opt
            case 'x'
                x(k.index)=cp(1,1);
            case 'y'
                y(k.index)=cp(2,2);
            case {'xy','axy'}
                x(k.index)=cp(1,1);
                y(k.index)=cp(2,2);
            end
            
            if (~strcmp(opt,'axy')&~strcmp(opt,'x'))
                if k.index>1&x(k.index)<x(k.index-1)
                    x(k.index)=x(k.index-1);
                end
                if k.index<length(x)&x(k.index)>x(k.index+1)
                    x(k.index)=x(k.index+1);
                end
            end
            
            set(h,'xdata',x,'ydata',y)
            % test to see if it moved off the screen - update limits
            fgx=get(gca,'xlim');
            fgy=get(gca,'ylim');
            if any(opt=='y')&cp(2,2)>fgy(2)
                set(gca,'ylim',[fgy(1) cp(2,2)])
            end
            if any(opt=='y')&cp(2,2)<fgy(1)
                set(gca,'ylim',[cp(2,2) fgy(2)])
            end
            if any(opt=='x')&cp(1,1)>fgx(2)
                set(gca,'xlim',[fgx(1) cp(1,1)])
            end
            if any(opt=='x')&cp(1,1)<fgx(1)
                set(gca,'xlim',[cp(1,1) fgx(2)])
            end
            
        case 3 % button up - we're done
            k=get(h,'userdata');
            set(gca,'userdata',k.axesdata); % restore axes data to its previous value
            set(gcf,'windowbuttonmotionfcn',k.winbmfcn,'windowbuttonupfcn',k.winbupfcn,'doublebuffer',k.doublebuffer)
        end
    end
end

