// $Revision: 1.5 $ // Copyright (c) 1994-1995 Taligent, Inc. All rights reserved. #ifndef TaligentSamples_CLICKORDRAGINTERACTOR #include "ClickOrDragInteractor.h" #endif #ifndef Taligent_GRAPHICS #include #endif #ifndef Taligent_ASSERTIONS #include #endif VersionDefinitionsMacro(TClickOrDragInteractor, 0); // See MouseInput5 for a discussion of why the view needs to maintain a reference // to us. TClickOrDragInteractor::TClickOrDragInteractor(TGraphicView* view, TInteractor* doubleClickInteractorToAdopt, TInteractor* dragInteractorToAdopt) : TInteractor(), MMouseEventHandler(), fViewHandle(*view), fDoubleClickInteractor(doubleClickInteractorToAdopt), fDragInteractor(dragInteractorToAdopt), fSubInteractor(NIL) { Assertion(view != NIL); Assertion(doubleClickInteractorToAdopt != NIL); Assertion(dragInteractorToAdopt != NIL); SetCoordinateView(fViewHandle); view->AddInteractor(this); } TClickOrDragInteractor::~TClickOrDragInteractor() { delete fDoubleClickInteractor; delete fDragInteractor; TGraphicView* view = (TGraphicView*)fViewHandle.GetView(); if (view) { view->RemoveInteractor(*this); } } // The view will start us on the first mousedown, and we wait for another mouse event. // If it is a double-click, start the double click interactor, if it is a move, // start the drag interactor. Quit if the mouse is clicked outside the view. // We set the coordinate view in our constructor, so the event position will be in // the view's coordinate system. // We must call StartMouseMovedEvents in order to receive mouse moved calls. We can // call it multiple times and since subsequent calls are ignored we don't need to check. // We can't call it in the constructor like SetCoordinateView because we need the // correct mouse input device, and need to get it from the mouse event. // Setting the interactor will cause our implementation of DispatchEvent to send events // to that interactor instead of this. Since this event was already dispatched to this // interactor, we explicitly redispatch it to the new interactor. bool TClickOrDragInteractor::MouseButtonDown(TMouseDownEvent& mouseDown, short) { TGraphicView* view = (TGraphicView*)fViewHandle.GetView(); Assertion(view != NIL); TGPoint point = mouseDown.GetEventPosition(); bool handled = view->ContainsPoint(point); if (handled) { StartMouseMovedEvents(*mouseDown.GetMouseInputDevice()); if (mouseDown.GetClickCount() > 1) { StopMouseMovedEvents(); fSubInteractor = fDoubleClickInteractor; fSubInteractor->Activate(); handled = DispatchEvent(mouseDown); } } else { SetDone(true); } return handled; } bool TClickOrDragInteractor::MouseButtonUp(TMouseUpEvent& mouseUp, short) { StopMouseMovedEvents(); return true; } // We stop mouse moved events since we ourselves no longer need them. // StartMouseMovedEvents must be called on the subinteractor itself or else its base // MMouseEventHandler will not call MouseMoved even when it receives the mouse moved // event. The approach this example takes is to treat the interactors as generically // as possible, i.e., only use TInteractor prococol with them. So this interactor can // not call StartMouseMovedEvents on the sub interactor, because it 'does not know' // the subinteractor is a MMouseEventHandler (or even that it wants mouse moved events). // The subinteractor cannot override HandleActivate because this does not provide // access to the mouse device, which is needed by StartMouseMovedEvents. And it // cannot start mouse moved events within MouseMoved itself, because MouseMoved will // not be called until StartMouseMovedEvents has been set. Also, mouse interactors // commonly perform their setup in mousedown rather than in the constructor, since they // often care about the mouse down location. // // So the approach taken here is to assume the provided interactor wants to initialize // itself using a TMouseDownEvent. So we synthesize one. We throw away the result // from the MouseDown, and simply return the result from MouseMoved. bool TClickOrDragInteractor::MouseMoved(TMouseMovedEvent& mouseMoved) { StopMouseMovedEvents(); fSubInteractor = fDragInteractor; #if 0 MMouseEventHandler *mouseInteractor = MMouseEventHandler::DynamicCastHack(*fDragInteractor); if (mouseInteractor) { mouseInteractor->StartMouseMovedEvents(*mouseMoved.GetMouseInputDevice()); } #endif fSubInteractor->Activate(); TMouseDownEvent mouseDown(1, mouseMoved.GetEventPosition(), mouseMoved.GetMouseInputDevice(), mouseMoved.GetEventReceiver()); DispatchEvent(mouseDown); return DispatchEvent(mouseMoved); } // Override DispatchEvent to forward event dispatching to the sub-interactor, checking // the active and done states to mimic the behavior of TInteractor. We must also // mark ourselves as done when the sub-interactor completes in order for our owner to // know to delete us. bool TClickOrDragInteractor::DispatchEvent(TEvent& event) { bool handled = false; if (IsActive() && !IsDone() && fSubInteractor) { handled = fSubInteractor->DispatchEvent(event); SetDone(fSubInteractor->IsDone()); } else { handled = TInteractor::DispatchEvent(event); } return handled; }