function IsOwner = mouseevent(Constr,EventName,EventSrc)
%MOUSEEVENT  Processes mouse events.

%   Authors: N. Hickey
%   Revised: B. Eryilmaz, A. Stothert
%   Copyright 1986-2004 The MathWorks, Inc. 
%   $Revision: 1.1.6.2.2.1 $  $Date: 2005/01/08 23:42:29 $

%REVISIT
IsOwner = 0;
HostAx = handle(Constr.Parent);
HostFig = HostAx.Parent;
hChildren = Constr.Children;

switch EventName
   case 'bd'
      %Find closest edge to clicked point
      CP = get(HostAx,'CurrentPoint');
      CP(1,1) = unitconv(CP(1,1),Constr.xDisplayUnits,Constr.xUnits);
      CP(1,2) = unitconv(CP(1,2),Constr.yDisplayUnits,Constr.yUnits);
      xM = ( (CP(1,1) > Constr.xCoords(:,1)) & (CP(1,1) <= Constr.xCoords(:,2)) | ...
         (CP(1,1) > Constr.xCoords(:,2)) & (CP(1,1) <= Constr.xCoords(:,1)) );
      yM = ( (CP(1,2) > Constr.yCoords(:,1)) & (CP(1,2) <= Constr.yCoords(:,2)) | ...
         (CP(1,2) > Constr.yCoords(:,2)) & (CP(1,2) <= Constr.yCoords(:,1)) );
      
      switch Constr.Orientation
         case 'horizontal'
            Constr.SelectedEdge = find(xM,1,'first');
         case 'vertical'
            Constr.SelectedEdge = find(yM,1,'first');
         otherwise
            Constr.SelectedEdge = find(xM&yM,1,'first');
      end
      if isempty(Constr.SelectedEdge)
         Constr.SelectedEdge = 1;
      end

      % Button down event
      SelectionType = get(HostFig, 'SelectionType');
      % Constraint editor management
      switch SelectionType
         case 'open'
            % Open constraint editor
            Constr.TextEditor.show(Constr);
         case {'normal','extend'}
            % Interaction with constraint
            % Left click
            if Constr.TextEditor.isVisible
               % Silently retarget editor
               Constr.TextEditor.target(Constr);
            end

            Tags = get(hChildren,'Tag');
            idxMarker = strcmp(Tags,'ConstraintMarkers');
            idxPatch  = strcmp(Tags,'ConstraintPatch');
            idxEdge   = strcmp(Tags,'ConstraintInfeasibleEdge');
            if any(EventSrc == hChildren(idxMarker))
               % Initialize resize
               setptr(HostFig, 'closedhand');
               LocalResize([], [], Constr, 'init', EventSrc);
            elseif any(EventSrc == hChildren(idxEdge))
               %Selecting edge to move
               setptr(HostFig, 'fleur');
               LocalMove([],[], 'init', Constr);
            elseif any(EventSrc == hChildren(idxPatch))
               % Selecting whole constraint
               Constr.SelectedEdge = 1:size(Constr.xCoords,1);
               setptr(HostFig, 'fleur');
               LocalMove([],[], 'init', Constr);
            end
         case 'alt'
            % Right click
            % Pops up context menu, need to set menu items
            % appropriately
            if strcmp(Constr.Selected,'off')
               Constr.Selected = 'on';
            end

            Tags = get(hChildren,'Tag');
            idxPatch  = strcmp(Tags,'ConstraintPatch');
            idxEdge   = strcmp(Tags,'ConstraintInfeasibleEdge');
            idxMarker = strcmp(Tags,'ConstraintMarkers');
            
            if any(EventSrc == hChildren(idxEdge)) || ...
               any(EventSrc == hChildren(idxMarker))   
               MenuType = 'Edge';
               VisibleSplit = 'on';
               VisibleFlip = 'off';
               VisibleEdit = 'on';
               idx = idxEdge;
            elseif any(EventSrc == hChildren(idxPatch))
               MenuType = 'Patch';
               VisibleSplit = 'off';
               VisibleFlip = 'on';
               VisibleEdit = 'off';
               idx = idxPatch;
               Constr.SelectedEdge = 1:size(Constr.xCoords,1);
            end

            %Find the patch and its context menu
            hMenu      = get(hChildren(idx),'UIContextMenu');
            hMenuItems = get(hMenu,'Children');
            menuTags   = get(hMenuItems,'Tag');
            %Check the orientation so that correct axis
            %join state is displayed
            switch Constr.Orientation
               case 'horizontal', idxOrient = 2;     %y-coord is free
               case 'vertical',   idxOrient = 1;     %x-coord is free
               case 'both',       idxOrient = [];    %niether coord is free
            end
            if ~strcmp(MenuType,'Patch') && ~isempty(idxOrient)
               switch Constr.SelectedEdge
                  case 1
                     %Edge at left end, doesn't have a left neighbour
                     VisibleLeft = 'off';
                     CheckedLeft = 'off';
                     if size(Constr.xCoords,1)==1
                        %Only one constraint
                        VisibleRight = 'off';
                        CheckedRight = 'off';
                     else
                        VisibleRight = 'on';
                        if Constr.Linked(1,idxOrient)
                           CheckedRight = 'on';
                        else
                           CheckedRight = 'off';
                        end
                     end
                  case size(Constr.xCoords,1)
                     %Edge at right end, doesn't have a right neighbour
                     VisibleRight = 'off';
                     CheckedRight = 'off';
                     if size(Constr.xCoords,1)==1
                        %Only one constraint
                        VisibleLeft = 'off';
                        CheckedLeft = 'off';
                     else
                        VisibleLeft = 'on';
                        if Constr.Linked(end,idxOrient)
                           CheckedLeft = 'on';
                        else
                           CheckedLeft = 'off';
                        end
                     end
                  case num2cell(2:size(Constr.xCoords,1)-1)
                     %Edge in the middle, has both left and right neighbour
                     VisibleLeft = 'on';
                     if Constr.Linked(Constr.SelectedEdge-1,idxOrient)
                        CheckedLeft = 'on';
                     else
                        CheckedLeft = 'off';
                     end
                     VisibleRight = 'on';
                     if Constr.Linked(Constr.SelectedEdge,idxOrient)
                        CheckedRight = 'on';
                     else
                        CheckedRight = 'off';
                     end
                  otherwise
                     %No edge selected
                     VisibleLeft  = 'off';
                     VisibleRight = 'off';
                     CheckedLeft  = 'off';
                     CheckedRight = 'off';
               end
            else 
               %Niether coord is free, turn off all join menus
               VisibleLeft  = 'off';
               VisibleRight = 'off';
               CheckedLeft  = 'off';
               CheckedRight = 'off';
            end
            
            %Set the join menu items correctly
            idx = strcmp(menuTags, 'left');
            set(hMenuItems(idx),'Visible',VisibleLeft);
            set(hMenuItems(idx),'Checked',CheckedLeft);
            idx = strcmp(menuTags, 'right');
            set(hMenuItems(idx),'Visible',VisibleRight);
            set(hMenuItems(idx),'Checked',CheckedRight);
            
            %Make the split and flip menu options visible
            idx = strcmp(menuTags,'flip');
            set(hMenuItems(idx),'visible',VisibleFlip);
            idx = strcmp(menuTags,'split');
            set(hMenuItems(idx),'visible',VisibleSplit);
            idx = strcmp(menuTags,'edit');
            set(hMenuItems(idx),'visible',VisibleEdit);
            
      end

   case 'wbm'
      % Mouse motion.  REVISIT: upgrade to local event when available
      % Get object currently hovered
      HitObject = hittest(HostFig);
      IsOwner = any(HitObject == hChildren);
      Tags = get(hChildren,'Tag');
      idx = strcmp(Tags,'ConstraintMarkers');
      if any(HitObject == hChildren(idx))
         % Over resize markers
         setptr(HostFig, 'hand');
         Constr.EventManager.poststatus(Constr.status('hovermarker'));
      elseif IsOwner
         % Over patch or edge
         Constr.EventManager.poststatus(Constr.status('hover'));
      end
end

%-------------------- Callback functions -------------------

% %%%%%%%%%%%%%%%%%
% %%% LocalMove %%%
% %%%%%%%%%%%%%%%%%
function LocalMove(eventSrc,eventData,action,Constr)
% Callback for button down on constraint

persistent WBMU MoveCounter TransAction

EventMgr = Constr.EventManager;    % @eventmgr object
HostAx   = handle(Constr.Parent);
HostFig  = double(HostAx.Parent);

switch action
case 'init'
    % Initialize constraint moving algorithm. hSrc is handle of selected line
    setptr(HostFig,'fleur');
    if strcmp(get(HostFig, 'SelectionType'),'normal')
       % resize is always single constr select on normal selection
       EventMgr.clearselect;
    else
       %Don't do anything, another key pressed while moving
       return
    end
    Constr.Selected = 'on';

    % Switch to mouse edit mode (ensures quick update with no axis limit adjustment)
    % and initialize move for selected objects in axes
    EventMgr.moveselect('init');
    MoveCounter = 0;   % Counts WBM calls
    
    % Take over window mouse events
    WBMU = get(HostFig,{'WindowButtonMotionFcn','WindowButtonUpFcn'});
    
    %Change window motion and button down function
    set(HostFig,'WindowButtonMotionFcn',{@LocalMove 'acquire' Constr},...
        'WindowButtonUpFcn',{@LocalMove 'finish' Constr}); 

    % Start recording move
    TransAction = ctrluis.transaction(Constr.Data,'Name',xlate('Move Constraint'),...
       'OperationStore','on','InverseOperationStore','on','Compression','on');

case 'acquire'
    % Move selected objects
    % RE: Disregard single WBM event issued when adjusting pointer location
    if MoveCounter
       % Move selected objects (issues MouseEdit event)
       Status = EventMgr.moveselect('track');
       EventMgr.poststatus(Status);
    end
    MoveCounter = MoveCounter+1;
    
case 'finish'
    % Restore initial conditions
    set(HostFig,{'WindowButtonMotionFcn','WindowButtonUpFcn'},WBMU,'Pointer','arrow')
    
    % Finish move
    Status = EventMgr.moveselect('finish');
    if MoveCounter>1 && ~isempty(TransAction)
       % Record transaction and update status
       EventMgr.record(TransAction);
       EventMgr.newstatus(Status);
       % Issue DataChanged even to force full observer update 
       Constr.send('DataChanged');
       Constr.send('DataChangeFinished');
    end
    TransAction = [];   % release persistent object
end

%%%%%%%%%%%%%%%%%
% LocalResize   %
%%%%%%%%%%%%%%%%%
function LocalResize(eventSrc,eventData,Constr,action,marker)
% Resizes gain constraint when button down on end marker

persistent WBMU TransAction

EventMgr = Constr.EventManager;    % @eventmgr object
HostAx = handle(Constr.Parent);
HostFig = HostAx.Parent;

switch action
   case 'init'
      if ~strcmp(get(HostFig, 'SelectionType'),'normal')
         %Don't do anything, another key pressed while moving
         return
      end

      CP = get(HostAx,'CurrentPoint');

      % Initialize constraint resizing algorithm
      setptr(HostFig,'closedhand');

      % Select constraint
      EventMgr.clearselect;   % resize is always single-select
      Constr.Selected = 'on';

      % Switch to mouse edit mode (ensures quick update with no axis limit adjustment)
      EventMgr.MouseEditMode = 'on';

      % Find if left or right marker is being moved, and initialize resize
      CPX = unitconv(CP(1,1),Constr.xDisplayUnits,Constr.xUnits);
      CPY = unitconv(CP(1,2),Constr.yDisplayUnits,Constr.yUnits);
      Dist = (Constr.xCoords - CPX).^2 + (Constr.yCoords - CPY).^2;
      [Constr.SelectedEdge,markerend] = find(Dist==min(min(Dist)),1);
      Constr.resize('init',markerend);

      % Take over window mouse events
      WBMU = get(HostFig,{'WindowButtonMotionFcn','WindowButtonUpFcn'});
      set(HostFig,'WindowButtonMotionFcn',{@LocalResize Constr 'acquire'},...
         'WindowButtonUpFcn',{@LocalResize Constr 'finish'});
      % Start recording
      TransAction = ctrluis.transaction(Constr.Data,'Name',xlate('Resize Constraint'),...
         'OperationStore','on','InverseOperationStore','on','Compression','on');

   case 'acquire'
      % Call to get X and Y values during constraint resize
      % RE: RESIZE should issue MouseEdit event with proper data for axes rescale
      Constr.resize('acquire');

   case 'finish'
      
      % Restore initial conditions
      set(HostFig, {'WindowButtonMotionFcn', 'WindowButtonUpFcn'}, ...
         WBMU, 'Pointer', 'arrow')

      % Call to finish resize
      Constr.resize('finish');
      EventMgr.MouseEditMode = 'off';
      
      % Record transaction
      EventMgr.record(TransAction);
      TransAction = [];   % release persistent object
      
      % Issue DataChanged even to force full observer update
      Constr.send('DataChanged');
      Constr.send('DataChangeFinished');
end
