/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: usereventqueue.cxx,v $ * $Revision: 1.13 $ * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_slideshow.hxx" // must be first #include #include #include #include #include #include #include #include #include "delayevent.hxx" #include "usereventqueue.hxx" #include "cursormanager.hxx" #include "slideshowexceptions.hxx" #include #include #include #include #include using namespace com::sun::star; /* Implementation of UserEventQueue class */ namespace slideshow { namespace internal { namespace { typedef std::vector ImpEventVector; typedef std::queue ImpEventQueue; typedef std::map, ImpEventVector> ImpAnimationEventMap; typedef std::map ImpShapeEventMap; // MouseEventHandler base class, not consuming any event: class MouseEventHandler_ : public MouseEventHandler { public: virtual bool handleMousePressed( awt::MouseEvent const& /*e*/ ) { return false;} virtual bool handleMouseReleased( awt::MouseEvent const& /*e*/) { return false;} virtual bool handleMouseEntered( awt::MouseEvent const& /*e*/ ) { return false;} virtual bool handleMouseExited( awt::MouseEvent const& /*e*/ ) { return false; } virtual bool handleMouseDragged( awt::MouseEvent const& /*e*/ ) { return false;} virtual bool handleMouseMoved( awt::MouseEvent const& /*e*/ ) { return false; } }; /** @return one event has been posted */ template bool fireSingleEvent( ContainerT & rQueue, EventQueue & rEventQueue ) { // post next event in given queue: while (! rQueue.empty()) { EventSharedPtr const pEvent(rQueue.front()); rQueue.pop(); // skip all inactive events (as the purpose of // nextEventFromQueue() is to activate the next // event, and events which return false on // isCharged() will never be activated by the // EventQueue) if(pEvent->isCharged()) return rEventQueue.addEvent( pEvent ); } return false; // no more (active) events in queue } /** @return at least one event has been posted */ template bool fireAllEvents( ContainerT & rQueue, EventQueue & rEventQueue ) { bool bFiredAny = false; while (fireSingleEvent( rQueue, rEventQueue )) bFiredAny = true; return bFiredAny; } class EventContainer { public: EventContainer() : maEvents() {} void clearContainer() { maEvents = ImpEventQueue(); } void addEvent( const EventSharedPtr& rEvent ) { maEvents.push( rEvent ); } bool isEmpty() { return maEvents.empty(); } protected: ImpEventQueue maEvents; }; } // anon namespace class PlainEventHandler : public EventHandler, public EventContainer { public: PlainEventHandler( EventQueue & rEventQueue ) : EventContainer(), mrEventQueue(rEventQueue) {} virtual void dispose() { clearContainer(); } virtual bool handleEvent() { return fireAllEvents( maEvents, mrEventQueue ); } private: EventQueue & mrEventQueue; }; class AllAnimationEventHandler : public AnimationEventHandler { public: AllAnimationEventHandler( EventQueue& rEventQueue ) : mrEventQueue( rEventQueue ), maAnimationEventMap() {} virtual void dispose() { maAnimationEventMap.clear(); } virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) { ENSURE_OR_RETURN( rNode, "AllAnimationEventHandler::handleAnimationEvent(): Invalid node" ); bool bRet( false ); ImpAnimationEventMap::iterator aIter; if( (aIter=maAnimationEventMap.find( rNode->getXAnimationNode() )) != maAnimationEventMap.end() ) { ImpEventVector& rVec( aIter->second ); bRet = !rVec.empty(); // registered node found -> fire all events in the vector std::for_each( rVec.begin(), rVec.end(), boost::bind( &EventQueue::addEvent, boost::ref( mrEventQueue ), _1 ) ); rVec.clear(); } return bRet; } void addEvent( const EventSharedPtr& rEvent, const uno::Reference< animations::XAnimationNode >& xNode ) { ImpAnimationEventMap::iterator aIter; if( (aIter=maAnimationEventMap.find( xNode )) == maAnimationEventMap.end() ) { // no entry for this animation -> create one aIter = maAnimationEventMap.insert( ImpAnimationEventMap::value_type( xNode, ImpEventVector() ) ).first; } // add new event to queue aIter->second.push_back( rEvent ); } bool isEmpty() { // find at least one animation with a non-empty vector ImpAnimationEventMap::const_iterator aCurr( maAnimationEventMap.begin() ); const ImpAnimationEventMap::const_iterator aEnd( maAnimationEventMap.end() ); while( aCurr != aEnd ) { if( !aCurr->second.empty() ) return false; // at least one non-empty entry found ++aCurr; } return true; // not a single non-empty entry found } private: EventQueue& mrEventQueue; ImpAnimationEventMap maAnimationEventMap; }; class ClickEventHandler : public MouseEventHandler_, public EventHandler, public EventContainer { public: ClickEventHandler( EventQueue& rEventQueue ) : EventContainer(), mrEventQueue( rEventQueue ), mbAdvanceOnClick( true ) {} void setAdvanceOnClick( bool bAdvanceOnClick ) { mbAdvanceOnClick = bAdvanceOnClick; } private: virtual void dispose() { clearContainer(); } // triggered by API calls, e.g. space bar virtual bool handleEvent() { return handleEvent_impl(); } // triggered by mouse release: virtual bool handleMouseReleased( const awt::MouseEvent& evt ) { if(evt.Buttons != awt::MouseButton::LEFT) return false; if( mbAdvanceOnClick ) { // fire next event return handleEvent_impl(); } else { return false; // advance-on-click disabled } } // triggered by both: virtual bool handleEvent_impl() { // fire next event: return fireSingleEvent( maEvents, mrEventQueue ); } private: EventQueue& mrEventQueue; bool mbAdvanceOnClick; }; class SkipEffectEventHandler : public ClickEventHandler { public: SkipEffectEventHandler( EventQueue & rEventQueue, EventMultiplexer & rEventMultiplexer ) : ClickEventHandler(rEventQueue), mrEventQueue(rEventQueue), mrEventMultiplexer(rEventMultiplexer), mbSkipTriggersNextEffect(true) {} /** Remember to trigger (or not to trigger) the next effect after the current effect is skiped. */ void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect) { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; } /// Skip the current effect but do not triggere the next effect. void skipEffect (void) { handleEvent_impl(false); } private: virtual bool handleEvent_impl() { return handleEvent_impl(true); } bool handleEvent_impl (bool bNotifyNextEffect) { // fire all events, so animation nodes can register their // next effect listeners: if(fireAllEvents( maEvents, mrEventQueue )) { if (mbSkipTriggersNextEffect && bNotifyNextEffect) { // then simulate a next effect event: this skip effect // handler is triggered upon next effect events (multiplexer // prio=-1)! Posting a notifyNextEffect() here is only safe // (we don't run into busy loop), because we assume that // someone has registerered above for next effects // (multiplexer prio=0) at the user event queue. return mrEventQueue.addEventWhenQueueIsEmpty( makeEvent( boost::bind( &EventMultiplexer::notifyNextEffect, boost::ref(mrEventMultiplexer) ), "EventMultiplexer::notifyNextEffect") ); } else return true; } return false; } private: EventQueue & mrEventQueue; EventMultiplexer & mrEventMultiplexer; bool mbSkipTriggersNextEffect; }; class RewindEffectEventHandler : public MouseEventHandler_, public EventContainer { public: RewindEffectEventHandler( EventQueue & rEventQueue ) : EventContainer(), mrEventQueue(rEventQueue) {} private: virtual void dispose() { clearContainer(); } virtual bool handleMouseReleased( awt::MouseEvent const& evt ) { if(evt.Buttons != awt::MouseButton::RIGHT) return false; return fireAllEvents( maEvents, mrEventQueue ); } private: EventQueue & mrEventQueue; }; /** Base class to share some common code between ShapeClickEventHandler and MouseMoveHandler @derive override necessary MouseEventHandler interface methods, call sendEvent() method to actually process the event. */ class MouseHandlerBase : public MouseEventHandler_ { public: MouseHandlerBase( EventQueue& rEventQueue ) : mrEventQueue( rEventQueue ), maShapeEventMap() {} virtual void dispose() { // TODO(Q1): Check whether plain vector with swap idiom is // okay here maShapeEventMap = ImpShapeEventMap(); } void addEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ) { ImpShapeEventMap::iterator aIter; if( (aIter=maShapeEventMap.find( rShape )) == maShapeEventMap.end() ) { // no entry for this shape -> create one aIter = maShapeEventMap.insert( ImpShapeEventMap::value_type( rShape, ImpEventQueue() ) ).first; } // add new event to queue aIter->second.push( rEvent ); } bool isEmpty() { // find at least one shape with a non-empty queue ImpShapeEventMap::reverse_iterator aCurrShape( maShapeEventMap.begin()); ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.end() ); while( aCurrShape != aEndShape ) { if( !aCurrShape->second.empty() ) return false; // at least one non-empty entry found ++aCurrShape; } return true; // not a single non-empty entry found } protected: bool hitTest( const awt::MouseEvent& e, ImpShapeEventMap::reverse_iterator& o_rHitShape ) { // find hit shape in map const basegfx::B2DPoint aPosition( e.X, e.Y ); // find matching shape (scan reversely, to coarsely match // paint order) ImpShapeEventMap::reverse_iterator aCurrShape(maShapeEventMap.rbegin()); const ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.rend() ); while( aCurrShape != aEndShape ) { // TODO(F2): Get proper geometry polygon from the // shape, to avoid having areas outside the shape // react on the mouse if( aCurrShape->first->getBounds().isInside( aPosition ) && aCurrShape->first->isVisible() ) { // shape hit, and shape is visible - report a // hit o_rHitShape = aCurrShape; return true; } ++aCurrShape; } return false; // nothing hit } bool sendEvent( ImpShapeEventMap::reverse_iterator& io_rHitShape ) { // take next event from queue const bool bRet( fireSingleEvent( io_rHitShape->second, mrEventQueue ) ); // clear shape entry, if its queue is // empty. This is important, since the shapes // are held by shared ptr, and might otherwise // not get released, even after their owning // slide is long gone. if( io_rHitShape->second.empty() ) { // this looks funny, since ::std::map does // provide an erase( iterator ) // method. Unfortunately, stlport does not // declare the obvious erase( // reverse_iterator ) needed here (missing // orthogonality, eh?) maShapeEventMap.erase( io_rHitShape->first ); } return bRet; } bool processEvent( const awt::MouseEvent& e ) { ImpShapeEventMap::reverse_iterator aCurrShape; if( hitTest( e, aCurrShape ) ) return sendEvent( aCurrShape ); return false; // did not handle the event } private: EventQueue& mrEventQueue; ImpShapeEventMap maShapeEventMap; }; class ShapeClickEventHandler : public MouseHandlerBase { public: ShapeClickEventHandler( CursorManager& rCursorManager, EventQueue& rEventQueue ) : MouseHandlerBase( rEventQueue ), mrCursorManager( rCursorManager ) {} virtual bool handleMouseReleased( const awt::MouseEvent& e ) { if(e.Buttons != awt::MouseButton::LEFT) return false; return processEvent( e ); } virtual bool handleMouseMoved( const awt::MouseEvent& e ) { // TODO(P2): Maybe buffer last shape touched // if we have a shape click event, and the mouse // hovers over this shape, change cursor to hand ImpShapeEventMap::reverse_iterator aDummy; if( hitTest( e, aDummy ) ) mrCursorManager.requestCursor( awt::SystemPointer::REFHAND ); return false; // we don't /eat/ this event. Lower prio // handler should see it, too. } private: CursorManager& mrCursorManager; }; class MouseEnterHandler : public MouseHandlerBase { public: MouseEnterHandler( EventQueue& rEventQueue ) : MouseHandlerBase( rEventQueue ), mpLastShape() {} virtual bool handleMouseMoved( const awt::MouseEvent& e ) { // TODO(P2): Maybe buffer last shape touched, and // check against that _first_ ImpShapeEventMap::reverse_iterator aCurr; if( hitTest( e, aCurr ) ) { if( aCurr->first != mpLastShape ) { // we actually hit a shape, and it's different // from the previous one - thus we just // entered it, raise event sendEvent( aCurr ); mpLastShape = aCurr->first; } } else { // don't hit no shape - thus, last shape is NULL mpLastShape.reset(); } return false; // we don't /eat/ this event. Lower prio // handler should see it, too. } private: ShapeSharedPtr mpLastShape; }; class MouseLeaveHandler : public MouseHandlerBase { public: MouseLeaveHandler( EventQueue& rEventQueue ) : MouseHandlerBase( rEventQueue ), maLastIter() {} virtual bool handleMouseMoved( const awt::MouseEvent& e ) { // TODO(P2): Maybe buffer last shape touched, and // check against that _first_ ImpShapeEventMap::reverse_iterator aCurr; if( hitTest( e, aCurr ) ) { maLastIter = aCurr; } else { if( maLastIter->first ) { // last time, we were over a shape, now we're // not - we thus just left that shape, raise // event sendEvent( maLastIter ); } // in any case, when we hit this else-branch: no // shape hit, thus have to clear maLastIter maLastIter = ImpShapeEventMap::reverse_iterator(); } return false; // we don't /eat/ this event. Lower prio // handler should see it, too. } private: ImpShapeEventMap::reverse_iterator maLastIter; }; template< typename Handler, typename Functor > void UserEventQueue::registerEvent( boost::shared_ptr< Handler >& rHandler, const EventSharedPtr& rEvent, const Functor& rRegistrationFunctor ) { ENSURE_OR_THROW( rEvent, "UserEventQueue::registerEvent(): Invalid event" ); if( !rHandler ) { // create handler rHandler.reset( new Handler( mrEventQueue ) ); // register handler on EventMultiplexer rRegistrationFunctor( rHandler ); } rHandler->addEvent( rEvent ); } template< typename Handler, typename Arg, typename Functor > void UserEventQueue::registerEvent( boost::shared_ptr< Handler >& rHandler, const EventSharedPtr& rEvent, const Arg& rArg, const Functor& rRegistrationFunctor ) { ENSURE_OR_THROW( rEvent, "UserEventQueue::registerEvent(): Invalid event" ); if( !rHandler ) { // create handler rHandler.reset( new Handler( mrEventQueue ) ); // register handler on EventMultiplexer rRegistrationFunctor( rHandler ); } rHandler->addEvent( rEvent, rArg ); } // Public methods // ===================================================== UserEventQueue::UserEventQueue( EventMultiplexer& rMultiplexer, EventQueue& rEventQueue, CursorManager& rCursorManager ) : mrMultiplexer( rMultiplexer ), mrEventQueue( rEventQueue ), mrCursorManager( rCursorManager ), mpStartEventHandler(), mpEndEventHandler(), mpAnimationStartEventHandler(), mpAnimationEndEventHandler(), mpAudioStoppedEventHandler(), mpClickEventHandler(), mpSkipEffectEventHandler(), mpRewindEffectEventHandler(), mpDoubleClickEventHandler(), mpMouseEnterHandler(), mpMouseLeaveHandler(), mbAdvanceOnClick( true ) { } UserEventQueue::~UserEventQueue() { try { // unregister all handlers clear(); } catch (uno::Exception &) { OSL_ENSURE( false, rtl::OUStringToOString( comphelper::anyToString( cppu::getCaughtException() ), RTL_TEXTENCODING_UTF8 ).getStr() ); } } bool UserEventQueue::isEmpty() const { // TODO(T2): This is not thread safe, the handlers are all // only separately synchronized. This poses the danger of // generating false empty status on XSlideShow::update(), such // that the last events of a slide are not triggered. // we're empty iff all handler queues are empty return (mpStartEventHandler ? mpStartEventHandler->isEmpty() : true) && (mpEndEventHandler ? mpEndEventHandler->isEmpty() : true) && (mpAnimationStartEventHandler ? mpAnimationStartEventHandler->isEmpty() : true) && (mpAnimationEndEventHandler ? mpAnimationEndEventHandler->isEmpty() : true) && (mpAudioStoppedEventHandler ? mpAudioStoppedEventHandler->isEmpty() : true) && (mpShapeClickEventHandler ? mpShapeClickEventHandler->isEmpty() : true) && (mpClickEventHandler ? mpClickEventHandler->isEmpty() : true) && (mpSkipEffectEventHandler ? mpSkipEffectEventHandler->isEmpty() : true) && (mpRewindEffectEventHandler ? mpRewindEffectEventHandler->isEmpty() : true) && (mpShapeDoubleClickEventHandler ? mpShapeDoubleClickEventHandler->isEmpty() : true) && (mpDoubleClickEventHandler ? mpDoubleClickEventHandler->isEmpty() : true) && (mpMouseEnterHandler ? mpMouseEnterHandler->isEmpty() : true) && (mpMouseLeaveHandler ? mpMouseLeaveHandler->isEmpty() : true); } void UserEventQueue::clear() { // unregister and delete all handlers if( mpStartEventHandler ) { mrMultiplexer.removeSlideStartHandler( mpStartEventHandler ); mpStartEventHandler.reset(); } if( mpEndEventHandler ) { mrMultiplexer.removeSlideEndHandler( mpEndEventHandler ); mpEndEventHandler.reset(); } if( mpAnimationStartEventHandler ) { mrMultiplexer.removeAnimationStartHandler( mpAnimationStartEventHandler ); mpAnimationStartEventHandler.reset(); } if( mpAnimationEndEventHandler ) { mrMultiplexer.removeAnimationEndHandler( mpAnimationEndEventHandler ); mpAnimationEndEventHandler.reset(); } if( mpAudioStoppedEventHandler ) { mrMultiplexer.removeAudioStoppedHandler( mpAudioStoppedEventHandler ); mpAudioStoppedEventHandler.reset(); } if( mpShapeClickEventHandler ) { mrMultiplexer.removeClickHandler( mpShapeClickEventHandler ); mrMultiplexer.removeMouseMoveHandler( mpShapeClickEventHandler ); mpShapeClickEventHandler.reset(); } if( mpClickEventHandler ) { mrMultiplexer.removeClickHandler( mpClickEventHandler ); mrMultiplexer.removeNextEffectHandler( mpClickEventHandler ); mpClickEventHandler.reset(); } if(mpSkipEffectEventHandler) { mrMultiplexer.removeClickHandler( mpSkipEffectEventHandler ); mrMultiplexer.removeNextEffectHandler( mpSkipEffectEventHandler ); mpSkipEffectEventHandler.reset(); } if(mpRewindEffectEventHandler) { mrMultiplexer.removeClickHandler( mpRewindEffectEventHandler ); mpRewindEffectEventHandler.reset(); } if( mpShapeDoubleClickEventHandler ) { mrMultiplexer.removeDoubleClickHandler( mpShapeDoubleClickEventHandler ); mrMultiplexer.removeMouseMoveHandler( mpShapeDoubleClickEventHandler ); mpShapeDoubleClickEventHandler.reset(); } if( mpDoubleClickEventHandler ) { mrMultiplexer.removeDoubleClickHandler( mpDoubleClickEventHandler ); mpDoubleClickEventHandler.reset(); } if( mpMouseEnterHandler ) { mrMultiplexer.removeMouseMoveHandler( mpMouseEnterHandler ); mpMouseEnterHandler.reset(); } if( mpMouseLeaveHandler ) { mrMultiplexer.removeMouseMoveHandler( mpMouseLeaveHandler ); mpMouseLeaveHandler.reset(); } } void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick ) { mbAdvanceOnClick = bAdvanceOnClick; // forward to handler, if existing. Otherwise, the handler // creation will do the forwarding. if( mpClickEventHandler ) mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick ); } void UserEventQueue::registerSlideStartEvent( const EventSharedPtr& rEvent ) { registerEvent( mpStartEventHandler, rEvent, boost::bind( &EventMultiplexer::addSlideStartHandler, boost::ref( mrMultiplexer ), _1 ) ); } void UserEventQueue::registerSlideEndEvent( const EventSharedPtr& rEvent ) { registerEvent( mpEndEventHandler, rEvent, boost::bind( &EventMultiplexer::addSlideEndHandler, boost::ref( mrMultiplexer ), _1 ) ); } void UserEventQueue::registerAnimationStartEvent( const EventSharedPtr& rEvent, const uno::Reference< animations::XAnimationNode>& xNode ) { registerEvent( mpAnimationStartEventHandler, rEvent, xNode, boost::bind( &EventMultiplexer::addAnimationStartHandler, boost::ref( mrMultiplexer ), _1 ) ); } void UserEventQueue::registerAnimationEndEvent( const EventSharedPtr& rEvent, const uno::Reference& xNode ) { registerEvent( mpAnimationEndEventHandler, rEvent, xNode, boost::bind( &EventMultiplexer::addAnimationEndHandler, boost::ref( mrMultiplexer ), _1 ) ); } void UserEventQueue::registerAudioStoppedEvent( const EventSharedPtr& rEvent, const uno::Reference& xNode ) { registerEvent( mpAudioStoppedEventHandler, rEvent, xNode, boost::bind( &EventMultiplexer::addAudioStoppedHandler, boost::ref( mrMultiplexer ), _1 ) ); } void UserEventQueue::registerShapeClickEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ) { ENSURE_OR_THROW( rEvent, "UserEventQueue::registerShapeClickEvent(): Invalid event" ); if( !mpShapeClickEventHandler ) { // create handler mpShapeClickEventHandler.reset( new ShapeClickEventHandler(mrCursorManager, mrEventQueue) ); // register handler on EventMultiplexer mrMultiplexer.addClickHandler( mpShapeClickEventHandler, 1.0 ); mrMultiplexer.addMouseMoveHandler( mpShapeClickEventHandler, 1.0 ); } mpShapeClickEventHandler->addEvent( rEvent, rShape ); } namespace { class ClickEventRegistrationFunctor { public: ClickEventRegistrationFunctor( EventMultiplexer& rMultiplexer, double nPrio, bool bAdvanceOnClick ) : mrMultiplexer( rMultiplexer ), mnPrio(nPrio), mbAdvanceOnClick( bAdvanceOnClick ) {} void operator()( const boost::shared_ptr& rHandler )const { // register the handler on _two_ sources: we want the // nextEffect events, e.g. space bar, to trigger clicks, as well! mrMultiplexer.addClickHandler( rHandler, mnPrio ); mrMultiplexer.addNextEffectHandler( rHandler, mnPrio ); // forward advance-on-click state to newly // generated handler (that's the only reason why // we're called here) rHandler->setAdvanceOnClick( mbAdvanceOnClick ); } private: EventMultiplexer& mrMultiplexer; double const mnPrio; bool const mbAdvanceOnClick; }; } // anon namespace void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent ) { // TODO: better name may be mpNextEffectEventHandler? then we have // next effect (=> waiting to be started) // skip effect (skipping the currently running one) // rewind effect (rewinding back running one and waiting (again) // to be started) registerEvent( mpClickEventHandler, rEvent, ClickEventRegistrationFunctor( mrMultiplexer, 0.0 /* default prio */, mbAdvanceOnClick ) ); } void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent, const bool bSkipTriggersNextEffect) { if(!mpSkipEffectEventHandler) { mpSkipEffectEventHandler.reset( new SkipEffectEventHandler( mrEventQueue, mrMultiplexer ) ); // register the handler on _two_ sources: we want the // nextEffect events, e.g. space bar, to trigger clicks, as well! mrMultiplexer.addClickHandler( mpSkipEffectEventHandler, -1.0 /* prio below default */ ); mrMultiplexer.addNextEffectHandler( mpSkipEffectEventHandler, -1.0 /* prio below default */ ); // forward advance-on-click state to newly // generated handler (that's the only reason why // we're called here) mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick ); } mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect); mpSkipEffectEventHandler->addEvent( pEvent ); } void UserEventQueue::registerRewindEffectEvent( EventSharedPtr const& pEvent ) { registerEvent( mpRewindEffectEventHandler, pEvent, boost::bind( &EventMultiplexer::addClickHandler, boost::ref(mrMultiplexer), _1, -1.0 /* prio below default */ ) ); } void UserEventQueue::registerShapeDoubleClickEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ) { ENSURE_OR_THROW( rEvent, "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" ); if( !mpShapeDoubleClickEventHandler ) { // create handler mpShapeDoubleClickEventHandler.reset( new ShapeClickEventHandler(mrCursorManager, mrEventQueue) ); // register handler on EventMultiplexer mrMultiplexer.addDoubleClickHandler( mpShapeDoubleClickEventHandler, 1.0 ); mrMultiplexer.addMouseMoveHandler( mpShapeDoubleClickEventHandler, 1.0 ); } mpShapeDoubleClickEventHandler->addEvent( rEvent, rShape ); } void UserEventQueue::registerDoubleClickEvent( const EventSharedPtr& rEvent ) { registerEvent( mpDoubleClickEventHandler, rEvent, boost::bind( &EventMultiplexer::addDoubleClickHandler, boost::ref( mrMultiplexer ), _1, 0.0 /* default prio */ ) ); } void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ) { registerEvent( mpMouseEnterHandler, rEvent, rShape, boost::bind( &EventMultiplexer::addMouseMoveHandler, boost::ref( mrMultiplexer ), _1, 0.0 /* default prio */ ) ); } void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ) { registerEvent( mpMouseLeaveHandler, rEvent, rShape, boost::bind( &EventMultiplexer::addMouseMoveHandler, boost::ref( mrMultiplexer ), _1, 0.0 /* default prio */ ) ); } void UserEventQueue::callSkipEffectEventHandler (void) { ::boost::shared_ptr pHandler ( ::boost::dynamic_pointer_cast(mpSkipEffectEventHandler)); if (pHandler) pHandler->skipEffect(); } } // namespace internal } // namespace presentation