summaryrefslogtreecommitdiff
path: root/slideshow/source/engine/usereventqueue.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/usereventqueue.cxx')
-rw-r--r--slideshow/source/engine/usereventqueue.cxx1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/slideshow/source/engine/usereventqueue.cxx b/slideshow/source/engine/usereventqueue.cxx
new file mode 100644
index 000000000000..0b043968b9c6
--- /dev/null
+++ b/slideshow/source/engine/usereventqueue.cxx
@@ -0,0 +1,1007 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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
+ * <http://www.openoffice.org/license.html>
+ * 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 <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+
+#include <boost/bind.hpp>
+
+#include "delayevent.hxx"
+#include "usereventqueue.hxx"
+#include "cursormanager.hxx"
+#include "slideshowexceptions.hxx"
+
+#include <vector>
+#include <queue>
+#include <map>
+#include <functional>
+#include <algorithm>
+
+
+using namespace com::sun::star;
+
+/* Implementation of UserEventQueue class */
+
+namespace slideshow {
+namespace internal {
+
+namespace {
+
+typedef std::vector<EventSharedPtr> ImpEventVector;
+typedef std::queue<EventSharedPtr> ImpEventQueue;
+typedef std::map<uno::Reference<animations::XAnimationNode>,
+ ImpEventVector> ImpAnimationEventMap;
+typedef std::map<ShapeSharedPtr, ImpEventQueue,
+ Shape::lessThanShape> 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 <typename ContainerT>
+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 <typename ContainerT>
+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_FALSE(
+ 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<animations::XAnimationNode>& xNode )
+{
+ registerEvent( mpAnimationEndEventHandler,
+ rEvent,
+ xNode,
+ boost::bind( &EventMultiplexer::addAnimationEndHandler,
+ boost::ref( mrMultiplexer ), _1 ) );
+}
+
+void UserEventQueue::registerAudioStoppedEvent(
+ const EventSharedPtr& rEvent,
+ const uno::Reference<animations::XAnimationNode>& 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<ClickEventHandler>& 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<SkipEffectEventHandler> pHandler (
+ ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler));
+ if (pHandler)
+ pHandler->skipEffect();
+}
+
+} // namespace internal
+} // namespace presentation
+