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