1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 #include <tools/diagnose_ex.h>
34 
35 #include <comphelper/anytostring.hxx>
36 #include <cppuhelper/exc_hlp.hxx>
37 
38 #include <com/sun/star/awt/SystemPointer.hpp>
39 #include <com/sun/star/awt/MouseButton.hpp>
40 #include <com/sun/star/awt/MouseEvent.hpp>
41 
42 #include <boost/bind.hpp>
43 
44 #include "delayevent.hxx"
45 #include "usereventqueue.hxx"
46 #include "cursormanager.hxx"
47 #include "slideshowexceptions.hxx"
48 
49 #include <vector>
50 #include <queue>
51 #include <map>
52 #include <functional>
53 #include <algorithm>
54 
55 
56 using namespace com::sun::star;
57 
58 /* Implementation of UserEventQueue class */
59 
60 namespace slideshow {
61 namespace internal {
62 
63 namespace {
64 
65 typedef std::vector<EventSharedPtr> ImpEventVector;
66 typedef std::queue<EventSharedPtr> ImpEventQueue;
67 typedef std::map<uno::Reference<animations::XAnimationNode>,
68                  ImpEventVector> ImpAnimationEventMap;
69 typedef std::map<ShapeSharedPtr, ImpEventQueue,
70                  Shape::lessThanShape> ImpShapeEventMap;
71 
72 // MouseEventHandler base class, not consuming any event:
73 class MouseEventHandler_ : public MouseEventHandler
74 {
75 public:
76     virtual bool handleMousePressed( awt::MouseEvent const& /*e*/ ) { return false;}
77     virtual bool handleMouseReleased( awt::MouseEvent const& /*e*/) { return false;}
78     virtual bool handleMouseEntered( awt::MouseEvent const& /*e*/ ) { return false;}
79     virtual bool handleMouseExited( awt::MouseEvent const& /*e*/ ) { return false; }
80     virtual bool handleMouseDragged( awt::MouseEvent const& /*e*/ ) { return false;}
81     virtual bool handleMouseMoved( awt::MouseEvent const& /*e*/ ) { return false; }
82 };
83 
84 /** @return one event has been posted
85  */
86 template <typename ContainerT>
87 bool fireSingleEvent( ContainerT & rQueue, EventQueue & rEventQueue )
88 {
89     // post next event in given queue:
90     while (! rQueue.empty())
91     {
92         EventSharedPtr const pEvent(rQueue.front());
93         rQueue.pop();
94 
95         // skip all inactive events (as the purpose of
96         // nextEventFromQueue() is to activate the next
97         // event, and events which return false on
98         // isCharged() will never be activated by the
99         // EventQueue)
100         if(pEvent->isCharged())
101             return rEventQueue.addEvent( pEvent );
102     }
103     return false; // no more (active) events in queue
104 }
105 
106 /** @return at least one event has been posted
107  */
108 template <typename ContainerT>
109 bool fireAllEvents( ContainerT & rQueue, EventQueue & rEventQueue )
110 {
111     bool bFiredAny = false;
112     while (fireSingleEvent( rQueue, rEventQueue ))
113         bFiredAny = true;
114     return bFiredAny;
115 }
116 
117 class EventContainer
118 {
119 public:
120     EventContainer() :
121         maEvents()
122     {}
123 
124     void clearContainer()
125     {
126         maEvents = ImpEventQueue();
127     }
128 
129     void addEvent( const EventSharedPtr& rEvent )
130     {
131         maEvents.push( rEvent );
132     }
133 
134     bool isEmpty()
135     {
136         return maEvents.empty();
137     }
138 
139 protected:
140     ImpEventQueue maEvents;
141 };
142 
143 } // anon namespace
144 
145 class PlainEventHandler : public EventHandler,
146                           public EventContainer
147 {
148 public:
149     PlainEventHandler( EventQueue & rEventQueue )
150         : EventContainer(), mrEventQueue(rEventQueue) {}
151 
152     virtual void dispose()
153     {
154         clearContainer();
155     }
156 
157     virtual bool handleEvent()
158     {
159         return fireAllEvents( maEvents, mrEventQueue );
160     }
161 
162 private:
163     EventQueue & mrEventQueue;
164 };
165 
166 class AllAnimationEventHandler : public AnimationEventHandler
167 {
168 public:
169     AllAnimationEventHandler( EventQueue& rEventQueue ) :
170         mrEventQueue( rEventQueue ),
171         maAnimationEventMap()
172     {}
173 
174     virtual void dispose()
175     {
176         maAnimationEventMap.clear();
177     }
178 
179     virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
180     {
181         ENSURE_OR_RETURN_FALSE(
182             rNode,
183             "AllAnimationEventHandler::handleAnimationEvent(): Invalid node" );
184 
185         bool bRet( false );
186 
187         ImpAnimationEventMap::iterator aIter;
188         if( (aIter=maAnimationEventMap.find(
189                  rNode->getXAnimationNode() )) != maAnimationEventMap.end() )
190         {
191             ImpEventVector& rVec( aIter->second );
192 
193             bRet = !rVec.empty();
194 
195             // registered node found -> fire all events in the vector
196             std::for_each( rVec.begin(), rVec.end(),
197                            boost::bind( &EventQueue::addEvent,
198                                         boost::ref( mrEventQueue ), _1 ) );
199 
200             rVec.clear();
201         }
202 
203         return bRet;
204     }
205 
206     void addEvent( const EventSharedPtr&                                rEvent,
207                    const uno::Reference< animations::XAnimationNode >&  xNode )
208     {
209         ImpAnimationEventMap::iterator aIter;
210         if( (aIter=maAnimationEventMap.find( xNode )) ==
211             maAnimationEventMap.end() )
212         {
213             // no entry for this animation -> create one
214             aIter = maAnimationEventMap.insert(
215                 ImpAnimationEventMap::value_type( xNode,
216                                                   ImpEventVector() ) ).first;
217         }
218 
219         // add new event to queue
220         aIter->second.push_back( rEvent );
221     }
222 
223     bool isEmpty()
224     {
225         // find at least one animation with a non-empty vector
226         ImpAnimationEventMap::const_iterator aCurr( maAnimationEventMap.begin() );
227         const ImpAnimationEventMap::const_iterator aEnd( maAnimationEventMap.end() );
228         while( aCurr != aEnd )
229         {
230             if( !aCurr->second.empty() )
231                 return false; // at least one non-empty entry found
232 
233             ++aCurr;
234         }
235 
236         return true; // not a single non-empty entry found
237     }
238 
239 private:
240     EventQueue&             mrEventQueue;
241     ImpAnimationEventMap    maAnimationEventMap;
242 };
243 
244 class ClickEventHandler : public MouseEventHandler_,
245                           public EventHandler,
246                           public EventContainer
247 {
248 public:
249     ClickEventHandler( EventQueue& rEventQueue ) :
250         EventContainer(),
251         mrEventQueue( rEventQueue ),
252         mbAdvanceOnClick( true )
253     {}
254 
255     void setAdvanceOnClick( bool bAdvanceOnClick )
256     {
257         mbAdvanceOnClick = bAdvanceOnClick;
258     }
259 
260 private:
261     virtual void dispose()
262     {
263         clearContainer();
264     }
265 
266     // triggered by API calls, e.g. space bar
267     virtual bool handleEvent()
268     {
269         return handleEvent_impl();
270     }
271 
272     // triggered by mouse release:
273     virtual bool handleMouseReleased( const awt::MouseEvent& evt )
274     {
275         if(evt.Buttons != awt::MouseButton::LEFT)
276             return false;
277 
278         if( mbAdvanceOnClick ) {
279             // fire next event
280             return handleEvent_impl();
281         }
282         else {
283             return false; // advance-on-click disabled
284         }
285     }
286 
287     // triggered by both:
288     virtual bool handleEvent_impl()
289     {
290         // fire next event:
291         return fireSingleEvent( maEvents, mrEventQueue );
292     }
293 
294 private:
295     EventQueue& mrEventQueue;
296     bool        mbAdvanceOnClick;
297 };
298 
299 class SkipEffectEventHandler : public ClickEventHandler
300 {
301 public:
302     SkipEffectEventHandler( EventQueue & rEventQueue,
303                             EventMultiplexer & rEventMultiplexer )
304         : ClickEventHandler(rEventQueue),
305           mrEventQueue(rEventQueue),
306           mrEventMultiplexer(rEventMultiplexer),
307           mbSkipTriggersNextEffect(true) {}
308 
309     /** Remember to trigger (or not to trigger) the next effect after the
310         current effect is skiped.
311     */
312     void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect)
313     { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; }
314 
315     ///  Skip the current effect but do not triggere the next effect.
316     void skipEffect (void) { handleEvent_impl(false); }
317 
318 private:
319     virtual bool handleEvent_impl()
320     {
321         return handleEvent_impl(true);
322     }
323 
324     bool handleEvent_impl (bool bNotifyNextEffect)
325     {
326         // fire all events, so animation nodes can register their
327         // next effect listeners:
328         if(fireAllEvents( maEvents, mrEventQueue ))
329         {
330             if (mbSkipTriggersNextEffect && bNotifyNextEffect)
331             {
332                 // then simulate a next effect event: this skip effect
333                 // handler is triggered upon next effect events (multiplexer
334                 // prio=-1)!  Posting a notifyNextEffect() here is only safe
335                 // (we don't run into busy loop), because we assume that
336                 // someone has registerered above for next effects
337                 // (multiplexer prio=0) at the user event queue.
338                 return mrEventQueue.addEventWhenQueueIsEmpty(
339                     makeEvent( boost::bind( &EventMultiplexer::notifyNextEffect,
340                                             boost::ref(mrEventMultiplexer) ),
341                                "EventMultiplexer::notifyNextEffect") );
342             }
343             else
344                 return true;
345         }
346         return false;
347     }
348 
349 private:
350     EventQueue & mrEventQueue;
351     EventMultiplexer & mrEventMultiplexer;
352     bool mbSkipTriggersNextEffect;
353 };
354 
355 class RewindEffectEventHandler : public MouseEventHandler_,
356                                  public EventContainer
357 {
358 public:
359     RewindEffectEventHandler( EventQueue & rEventQueue )
360         : EventContainer(), mrEventQueue(rEventQueue) {}
361 
362 private:
363     virtual void dispose()
364     {
365         clearContainer();
366     }
367 
368     virtual bool handleMouseReleased( awt::MouseEvent const& evt )
369     {
370         if(evt.Buttons != awt::MouseButton::RIGHT)
371             return false;
372 
373         return fireAllEvents( maEvents, mrEventQueue );
374     }
375 
376 private:
377     EventQueue & mrEventQueue;
378 };
379 
380 /** Base class to share some common code between
381     ShapeClickEventHandler and MouseMoveHandler
382 
383     @derive override necessary MouseEventHandler interface methods,
384     call sendEvent() method to actually process the event.
385 */
386 class MouseHandlerBase : public MouseEventHandler_
387 {
388 public:
389     MouseHandlerBase( EventQueue& rEventQueue ) :
390         mrEventQueue( rEventQueue ),
391         maShapeEventMap()
392     {}
393 
394     virtual void dispose()
395     {
396         // TODO(Q1): Check whether plain vector with swap idiom is
397         // okay here
398         maShapeEventMap = ImpShapeEventMap();
399     }
400 
401     void addEvent( const EventSharedPtr& rEvent,
402                    const ShapeSharedPtr& rShape )
403     {
404         ImpShapeEventMap::iterator aIter;
405         if( (aIter=maShapeEventMap.find( rShape )) == maShapeEventMap.end() )
406         {
407             // no entry for this shape -> create one
408             aIter = maShapeEventMap.insert(
409                 ImpShapeEventMap::value_type( rShape,
410                                               ImpEventQueue() ) ).first;
411         }
412 
413         // add new event to queue
414         aIter->second.push( rEvent );
415     }
416 
417     bool isEmpty()
418     {
419         // find at least one shape with a non-empty queue
420         ImpShapeEventMap::reverse_iterator aCurrShape( maShapeEventMap.begin());
421         ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.end() );
422         while( aCurrShape != aEndShape )
423         {
424             if( !aCurrShape->second.empty() )
425                 return false; // at least one non-empty entry found
426 
427             ++aCurrShape;
428         }
429 
430         return true; // not a single non-empty entry found
431     }
432 
433 protected:
434     bool hitTest( const awt::MouseEvent&                e,
435                   ImpShapeEventMap::reverse_iterator&   o_rHitShape )
436     {
437         // find hit shape in map
438         const basegfx::B2DPoint aPosition( e.X, e.Y );
439 
440         // find matching shape (scan reversely, to coarsely match
441         // paint order)
442         ImpShapeEventMap::reverse_iterator       aCurrShape(maShapeEventMap.rbegin());
443         const ImpShapeEventMap::reverse_iterator aEndShape( maShapeEventMap.rend() );
444         while( aCurrShape != aEndShape )
445         {
446             // TODO(F2): Get proper geometry polygon from the
447             // shape, to avoid having areas outside the shape
448             // react on the mouse
449             if( aCurrShape->first->getBounds().isInside( aPosition ) &&
450                 aCurrShape->first->isVisible() )
451             {
452                 // shape hit, and shape is visible - report a
453                 // hit
454                 o_rHitShape = aCurrShape;
455                 return true;
456             }
457 
458             ++aCurrShape;
459         }
460 
461         return false; // nothing hit
462     }
463 
464     bool sendEvent( ImpShapeEventMap::reverse_iterator& io_rHitShape )
465     {
466         // take next event from queue
467         const bool bRet( fireSingleEvent( io_rHitShape->second,
468                                           mrEventQueue ) );
469 
470         // clear shape entry, if its queue is
471         // empty. This is important, since the shapes
472         // are held by shared ptr, and might otherwise
473         // not get released, even after their owning
474         // slide is long gone.
475         if( io_rHitShape->second.empty() )
476         {
477             // this looks funny, since ::std::map does
478             // provide an erase( iterator )
479             // method. Unfortunately, stlport does not
480             // declare the obvious erase(
481             // reverse_iterator ) needed here (missing
482             // orthogonality, eh?)
483             maShapeEventMap.erase( io_rHitShape->first );
484         }
485 
486         return bRet;
487     }
488 
489     bool processEvent( const awt::MouseEvent& e )
490     {
491         ImpShapeEventMap::reverse_iterator aCurrShape;
492 
493         if( hitTest( e, aCurrShape ) )
494             return sendEvent( aCurrShape );
495 
496         return false; // did not handle the event
497     }
498 
499 private:
500     EventQueue&         mrEventQueue;
501     ImpShapeEventMap    maShapeEventMap;
502 };
503 
504 class ShapeClickEventHandler : public MouseHandlerBase
505 {
506 public:
507     ShapeClickEventHandler( CursorManager& rCursorManager,
508                             EventQueue&    rEventQueue ) :
509         MouseHandlerBase( rEventQueue ),
510         mrCursorManager( rCursorManager )
511     {}
512 
513     virtual bool handleMouseReleased( const awt::MouseEvent& e )
514     {
515         if(e.Buttons != awt::MouseButton::LEFT)
516             return false;
517         return processEvent( e );
518     }
519 
520     virtual bool handleMouseMoved( const awt::MouseEvent& e )
521     {
522         // TODO(P2): Maybe buffer last shape touched
523 
524         // if we have a shape click event, and the mouse
525         // hovers over this shape, change cursor to hand
526         ImpShapeEventMap::reverse_iterator aDummy;
527         if( hitTest( e, aDummy ) )
528             mrCursorManager.requestCursor( awt::SystemPointer::REFHAND );
529 
530         return false; // we don't /eat/ this event. Lower prio
531         // handler should see it, too.
532     }
533 
534 private:
535     CursorManager& mrCursorManager;
536 };
537 
538 class MouseEnterHandler : public MouseHandlerBase
539 {
540 public:
541     MouseEnterHandler( EventQueue& rEventQueue )
542         : MouseHandlerBase( rEventQueue ),
543           mpLastShape() {}
544 
545     virtual bool handleMouseMoved( const awt::MouseEvent& e )
546     {
547         // TODO(P2): Maybe buffer last shape touched, and
548         // check against that _first_
549 
550         ImpShapeEventMap::reverse_iterator aCurr;
551         if( hitTest( e, aCurr ) )
552         {
553             if( aCurr->first != mpLastShape )
554             {
555                 // we actually hit a shape, and it's different
556                 // from the previous one - thus we just
557                 // entered it, raise event
558                 sendEvent( aCurr );
559                 mpLastShape = aCurr->first;
560             }
561         }
562         else
563         {
564             // don't hit no shape - thus, last shape is NULL
565             mpLastShape.reset();
566         }
567 
568         return false; // we don't /eat/ this event. Lower prio
569         // handler should see it, too.
570     }
571 
572 private:
573     ShapeSharedPtr mpLastShape;
574 };
575 
576 class MouseLeaveHandler : public MouseHandlerBase
577 {
578 public:
579     MouseLeaveHandler( EventQueue& rEventQueue )
580         : MouseHandlerBase( rEventQueue ),
581           maLastIter() {}
582 
583     virtual bool handleMouseMoved( const awt::MouseEvent& e )
584     {
585         // TODO(P2): Maybe buffer last shape touched, and
586         // check against that _first_
587 
588         ImpShapeEventMap::reverse_iterator aCurr;
589         if( hitTest( e, aCurr ) )
590         {
591             maLastIter = aCurr;
592         }
593         else
594         {
595             if( maLastIter->first )
596             {
597                 // last time, we were over a shape, now we're
598                 // not - we thus just left that shape, raise
599                 // event
600                 sendEvent( maLastIter );
601             }
602 
603             // in any case, when we hit this else-branch: no
604             // shape hit, thus have to clear maLastIter
605             maLastIter = ImpShapeEventMap::reverse_iterator();
606         }
607 
608         return false; // we don't /eat/ this event. Lower prio
609         // handler should see it, too.
610     }
611 
612 private:
613     ImpShapeEventMap::reverse_iterator maLastIter;
614 };
615 
616 template< typename Handler, typename Functor >
617 void UserEventQueue::registerEvent(
618     boost::shared_ptr< Handler >& rHandler,
619     const EventSharedPtr&         rEvent,
620     const Functor&                rRegistrationFunctor )
621 {
622     ENSURE_OR_THROW( rEvent,
623                       "UserEventQueue::registerEvent(): Invalid event" );
624 
625     if( !rHandler ) {
626         // create handler
627         rHandler.reset( new Handler( mrEventQueue ) );
628         // register handler on EventMultiplexer
629         rRegistrationFunctor( rHandler );
630     }
631 
632     rHandler->addEvent( rEvent );
633 }
634 
635 template< typename Handler, typename Arg, typename Functor >
636 void UserEventQueue::registerEvent(
637     boost::shared_ptr< Handler >& rHandler,
638     const EventSharedPtr&         rEvent,
639     const Arg&                    rArg,
640     const Functor&                rRegistrationFunctor )
641 {
642     ENSURE_OR_THROW( rEvent,
643                       "UserEventQueue::registerEvent(): Invalid event" );
644 
645     if( !rHandler ) {
646         // create handler
647         rHandler.reset( new Handler( mrEventQueue ) );
648 
649         // register handler on EventMultiplexer
650         rRegistrationFunctor( rHandler );
651     }
652 
653     rHandler->addEvent( rEvent, rArg );
654 }
655 
656 
657 // Public methods
658 // =====================================================
659 
660 UserEventQueue::UserEventQueue( EventMultiplexer&   rMultiplexer,
661                                 EventQueue&         rEventQueue,
662                                 CursorManager&      rCursorManager )
663     : mrMultiplexer( rMultiplexer ),
664       mrEventQueue( rEventQueue ),
665       mrCursorManager( rCursorManager ),
666       mpStartEventHandler(),
667       mpEndEventHandler(),
668       mpAnimationStartEventHandler(),
669       mpAnimationEndEventHandler(),
670       mpAudioStoppedEventHandler(),
671       mpClickEventHandler(),
672       mpSkipEffectEventHandler(),
673       mpRewindEffectEventHandler(),
674       mpDoubleClickEventHandler(),
675       mpMouseEnterHandler(),
676       mpMouseLeaveHandler(),
677       mbAdvanceOnClick( true )
678 {
679 }
680 
681 UserEventQueue::~UserEventQueue()
682 {
683     try
684     {
685         // unregister all handlers
686         clear();
687     }
688     catch (uno::Exception &) {
689         OSL_ENSURE( false, rtl::OUStringToOString(
690                         comphelper::anyToString(
691                             cppu::getCaughtException() ),
692                         RTL_TEXTENCODING_UTF8 ).getStr() );
693     }
694 }
695 
696 bool UserEventQueue::isEmpty() const
697 {
698     // TODO(T2): This is not thread safe, the handlers are all
699     // only separately synchronized. This poses the danger of
700     // generating false empty status on XSlideShow::update(), such
701     // that the last events of a slide are not triggered.
702 
703     // we're empty iff all handler queues are empty
704     return
705         (mpStartEventHandler ? mpStartEventHandler->isEmpty() : true) &&
706         (mpEndEventHandler ? mpEndEventHandler->isEmpty() : true) &&
707         (mpAnimationStartEventHandler ? mpAnimationStartEventHandler->isEmpty() : true) &&
708         (mpAnimationEndEventHandler ? mpAnimationEndEventHandler->isEmpty() : true) &&
709         (mpAudioStoppedEventHandler ? mpAudioStoppedEventHandler->isEmpty() : true) &&
710         (mpShapeClickEventHandler ? mpShapeClickEventHandler->isEmpty() : true) &&
711         (mpClickEventHandler ? mpClickEventHandler->isEmpty() : true) &&
712         (mpSkipEffectEventHandler ? mpSkipEffectEventHandler->isEmpty() : true) &&
713         (mpRewindEffectEventHandler ? mpRewindEffectEventHandler->isEmpty() : true) &&
714         (mpShapeDoubleClickEventHandler ? mpShapeDoubleClickEventHandler->isEmpty() : true) &&
715         (mpDoubleClickEventHandler ? mpDoubleClickEventHandler->isEmpty() : true) &&
716         (mpMouseEnterHandler ? mpMouseEnterHandler->isEmpty() : true) &&
717         (mpMouseLeaveHandler ? mpMouseLeaveHandler->isEmpty() : true);
718 }
719 
720 void UserEventQueue::clear()
721 {
722     // unregister and delete all handlers
723     if( mpStartEventHandler ) {
724         mrMultiplexer.removeSlideStartHandler( mpStartEventHandler );
725         mpStartEventHandler.reset();
726     }
727     if( mpEndEventHandler ) {
728         mrMultiplexer.removeSlideEndHandler( mpEndEventHandler );
729         mpEndEventHandler.reset();
730     }
731     if( mpAnimationStartEventHandler ) {
732         mrMultiplexer.removeAnimationStartHandler(
733             mpAnimationStartEventHandler );
734         mpAnimationStartEventHandler.reset();
735     }
736     if( mpAnimationEndEventHandler ) {
737         mrMultiplexer.removeAnimationEndHandler( mpAnimationEndEventHandler );
738         mpAnimationEndEventHandler.reset();
739     }
740     if( mpAudioStoppedEventHandler ) {
741         mrMultiplexer.removeAudioStoppedHandler( mpAudioStoppedEventHandler );
742         mpAudioStoppedEventHandler.reset();
743     }
744     if( mpShapeClickEventHandler ) {
745         mrMultiplexer.removeClickHandler( mpShapeClickEventHandler );
746         mrMultiplexer.removeMouseMoveHandler( mpShapeClickEventHandler );
747         mpShapeClickEventHandler.reset();
748     }
749     if( mpClickEventHandler ) {
750         mrMultiplexer.removeClickHandler( mpClickEventHandler );
751         mrMultiplexer.removeNextEffectHandler( mpClickEventHandler );
752         mpClickEventHandler.reset();
753     }
754     if(mpSkipEffectEventHandler) {
755         mrMultiplexer.removeClickHandler( mpSkipEffectEventHandler );
756         mrMultiplexer.removeNextEffectHandler( mpSkipEffectEventHandler );
757         mpSkipEffectEventHandler.reset();
758     }
759     if(mpRewindEffectEventHandler) {
760         mrMultiplexer.removeClickHandler( mpRewindEffectEventHandler );
761         mpRewindEffectEventHandler.reset();
762     }
763     if( mpShapeDoubleClickEventHandler ) {
764         mrMultiplexer.removeDoubleClickHandler( mpShapeDoubleClickEventHandler );
765         mrMultiplexer.removeMouseMoveHandler( mpShapeDoubleClickEventHandler );
766         mpShapeDoubleClickEventHandler.reset();
767     }
768     if( mpDoubleClickEventHandler ) {
769         mrMultiplexer.removeDoubleClickHandler( mpDoubleClickEventHandler );
770         mpDoubleClickEventHandler.reset();
771     }
772     if( mpMouseEnterHandler ) {
773         mrMultiplexer.removeMouseMoveHandler( mpMouseEnterHandler );
774         mpMouseEnterHandler.reset();
775     }
776     if( mpMouseLeaveHandler ) {
777         mrMultiplexer.removeMouseMoveHandler( mpMouseLeaveHandler );
778         mpMouseLeaveHandler.reset();
779     }
780 }
781 
782 void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick )
783 {
784     mbAdvanceOnClick = bAdvanceOnClick;
785 
786     // forward to handler, if existing. Otherwise, the handler
787     // creation will do the forwarding.
788     if( mpClickEventHandler )
789         mpClickEventHandler->setAdvanceOnClick( bAdvanceOnClick );
790 }
791 
792 
793 void UserEventQueue::registerSlideStartEvent( const EventSharedPtr& rEvent )
794 {
795     registerEvent( mpStartEventHandler,
796                    rEvent,
797                    boost::bind( &EventMultiplexer::addSlideStartHandler,
798                                 boost::ref( mrMultiplexer ), _1 ) );
799 }
800 
801 void UserEventQueue::registerSlideEndEvent( const EventSharedPtr& rEvent )
802 {
803     registerEvent( mpEndEventHandler,
804                    rEvent,
805                    boost::bind( &EventMultiplexer::addSlideEndHandler,
806                                 boost::ref( mrMultiplexer ), _1 ) );
807 }
808 
809 void UserEventQueue::registerAnimationStartEvent(
810     const EventSharedPtr&                                 rEvent,
811     const uno::Reference< animations::XAnimationNode>&    xNode )
812 {
813     registerEvent( mpAnimationStartEventHandler,
814                    rEvent,
815                    xNode,
816                    boost::bind( &EventMultiplexer::addAnimationStartHandler,
817                                 boost::ref( mrMultiplexer ), _1 ) );
818 }
819 
820 void UserEventQueue::registerAnimationEndEvent(
821     const EventSharedPtr&                               rEvent,
822     const uno::Reference<animations::XAnimationNode>&   xNode )
823 {
824     registerEvent( mpAnimationEndEventHandler,
825                    rEvent,
826                    xNode,
827                    boost::bind( &EventMultiplexer::addAnimationEndHandler,
828                                 boost::ref( mrMultiplexer ), _1 ) );
829 }
830 
831 void UserEventQueue::registerAudioStoppedEvent(
832     const EventSharedPtr&                               rEvent,
833     const uno::Reference<animations::XAnimationNode>&   xNode )
834 {
835     registerEvent( mpAudioStoppedEventHandler,
836                    rEvent,
837                    xNode,
838                    boost::bind( &EventMultiplexer::addAudioStoppedHandler,
839                                 boost::ref( mrMultiplexer ), _1 ) );
840 }
841 
842 void UserEventQueue::registerShapeClickEvent( const EventSharedPtr& rEvent,
843                                               const ShapeSharedPtr& rShape )
844 {
845     ENSURE_OR_THROW(
846         rEvent,
847         "UserEventQueue::registerShapeClickEvent(): Invalid event" );
848 
849     if( !mpShapeClickEventHandler )
850     {
851         // create handler
852         mpShapeClickEventHandler.reset(
853             new ShapeClickEventHandler(mrCursorManager,
854                                        mrEventQueue) );
855 
856         // register handler on EventMultiplexer
857         mrMultiplexer.addClickHandler( mpShapeClickEventHandler, 1.0 );
858         mrMultiplexer.addMouseMoveHandler( mpShapeClickEventHandler, 1.0 );
859     }
860 
861     mpShapeClickEventHandler->addEvent( rEvent, rShape );
862 }
863 
864 namespace {
865 class ClickEventRegistrationFunctor
866 {
867 public:
868     ClickEventRegistrationFunctor( EventMultiplexer& rMultiplexer,
869                                    double            nPrio,
870                                    bool              bAdvanceOnClick )
871         : mrMultiplexer( rMultiplexer ),
872           mnPrio(nPrio),
873           mbAdvanceOnClick( bAdvanceOnClick ) {}
874 
875     void operator()( const boost::shared_ptr<ClickEventHandler>& rHandler )const
876     {
877         // register the handler on _two_ sources: we want the
878         // nextEffect events, e.g. space bar, to trigger clicks, as well!
879         mrMultiplexer.addClickHandler( rHandler, mnPrio );
880         mrMultiplexer.addNextEffectHandler( rHandler, mnPrio );
881 
882         // forward advance-on-click state to newly
883         // generated handler (that's the only reason why
884         // we're called here)
885         rHandler->setAdvanceOnClick( mbAdvanceOnClick );
886     }
887 
888 private:
889     EventMultiplexer&   mrMultiplexer;
890     double const        mnPrio;
891     bool const          mbAdvanceOnClick;
892 };
893 } // anon namespace
894 
895 void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent )
896 {
897     // TODO: better name may be mpNextEffectEventHandler?  then we have
898     //       next effect (=> waiting to be started)
899     //       skip effect (skipping the currently running one)
900     //       rewind effect (rewinding back running one and waiting (again)
901     //                      to be started)
902     registerEvent( mpClickEventHandler,
903                    rEvent,
904                    ClickEventRegistrationFunctor( mrMultiplexer,
905                                                   0.0 /* default prio */,
906                                                   mbAdvanceOnClick ) );
907 }
908 
909 void UserEventQueue::registerSkipEffectEvent(
910     EventSharedPtr const & pEvent,
911     const bool bSkipTriggersNextEffect)
912 {
913     if(!mpSkipEffectEventHandler)
914     {
915         mpSkipEffectEventHandler.reset(
916             new SkipEffectEventHandler( mrEventQueue, mrMultiplexer ) );
917         // register the handler on _two_ sources: we want the
918         // nextEffect events, e.g. space bar, to trigger clicks, as well!
919         mrMultiplexer.addClickHandler( mpSkipEffectEventHandler,
920                                        -1.0 /* prio below default */ );
921         mrMultiplexer.addNextEffectHandler( mpSkipEffectEventHandler,
922                                             -1.0 /* prio below default */ );
923         // forward advance-on-click state to newly
924         // generated handler (that's the only reason why
925         // we're called here)
926         mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick );
927     }
928     mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect);
929     mpSkipEffectEventHandler->addEvent( pEvent );
930 }
931 
932 void UserEventQueue::registerRewindEffectEvent( EventSharedPtr const& pEvent )
933 {
934     registerEvent( mpRewindEffectEventHandler,
935                    pEvent,
936                    boost::bind( &EventMultiplexer::addClickHandler,
937                                 boost::ref(mrMultiplexer), _1,
938                                 -1.0 /* prio below default */ ) );
939 }
940 
941 void UserEventQueue::registerShapeDoubleClickEvent(
942     const EventSharedPtr& rEvent,
943     const ShapeSharedPtr& rShape )
944 {
945     ENSURE_OR_THROW(
946         rEvent,
947         "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
948 
949     if( !mpShapeDoubleClickEventHandler )
950     {
951         // create handler
952         mpShapeDoubleClickEventHandler.reset(
953             new ShapeClickEventHandler(mrCursorManager,
954                                        mrEventQueue) );
955 
956         // register handler on EventMultiplexer
957         mrMultiplexer.addDoubleClickHandler( mpShapeDoubleClickEventHandler,
958                                              1.0 );
959         mrMultiplexer.addMouseMoveHandler( mpShapeDoubleClickEventHandler,
960                                            1.0 );
961     }
962 
963     mpShapeDoubleClickEventHandler->addEvent( rEvent, rShape );
964 }
965 
966 void UserEventQueue::registerDoubleClickEvent( const EventSharedPtr& rEvent )
967 {
968     registerEvent( mpDoubleClickEventHandler,
969                    rEvent,
970                    boost::bind( &EventMultiplexer::addDoubleClickHandler,
971                                 boost::ref( mrMultiplexer ), _1,
972                                 0.0 /* default prio */ ) );
973 }
974 
975 void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr& rEvent,
976                                               const ShapeSharedPtr& rShape )
977 {
978     registerEvent( mpMouseEnterHandler,
979                    rEvent,
980                    rShape,
981                    boost::bind( &EventMultiplexer::addMouseMoveHandler,
982                                 boost::ref( mrMultiplexer ), _1,
983                                 0.0 /* default prio */ ) );
984 }
985 
986 void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent,
987                                               const ShapeSharedPtr& rShape )
988 {
989     registerEvent( mpMouseLeaveHandler,
990                    rEvent,
991                    rShape,
992                    boost::bind( &EventMultiplexer::addMouseMoveHandler,
993                                 boost::ref( mrMultiplexer ), _1,
994                                 0.0 /* default prio */ ) );
995 }
996 
997 void UserEventQueue::callSkipEffectEventHandler (void)
998 {
999     ::boost::shared_ptr<SkipEffectEventHandler> pHandler (
1000         ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler));
1001     if (pHandler)
1002         pHandler->skipEffect();
1003 }
1004 
1005 } // namespace internal
1006 } // namespace presentation
1007 
1008