1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <tools/diagnose_ex.h>
23 #include <comphelper/anytostring.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
26 #include <com/sun/star/awt/SystemPointer.hpp>
27 #include <com/sun/star/awt/MouseButton.hpp>
28 #include <com/sun/star/awt/MouseEvent.hpp>
30 #include <delayevent.hxx>
31 #include <usereventqueue.hxx>
32 #include <cursormanager.hxx>
33 #include <slideshowexceptions.hxx>
42 using namespace com::sun::star
;
44 /* Implementation of UserEventQueue class */
51 typedef std::vector
<EventSharedPtr
> ImpEventVector
;
52 typedef std::queue
<EventSharedPtr
> ImpEventQueue
;
53 typedef std::map
<uno::Reference
<animations::XAnimationNode
>,
54 ImpEventVector
> ImpAnimationEventMap
;
55 typedef std::map
<ShapeSharedPtr
, ImpEventQueue
,
56 Shape::lessThanShape
> ImpShapeEventMap
;
58 // MouseEventHandler base class, not consuming any event:
59 class MouseEventHandler_
: public MouseEventHandler
62 virtual bool handleMousePressed( awt::MouseEvent
const& /*e*/ ) override
{ return false;}
63 virtual bool handleMouseReleased( awt::MouseEvent
const& /*e*/) override
{ return false;}
64 virtual bool handleMouseDragged( awt::MouseEvent
const& /*e*/ ) override
{ return false;}
65 virtual bool handleMouseMoved( awt::MouseEvent
const& /*e*/ ) override
{ return false; }
68 /** @return one event has been posted
70 template <typename ContainerT
>
71 bool fireSingleEvent( ContainerT
& rQueue
, EventQueue
& rEventQueue
)
73 // post next event in given queue:
74 while (! rQueue
.empty())
76 EventSharedPtr
const pEvent(rQueue
.front());
79 // skip all inactive events (as the purpose of
80 // nextEventFromQueue() is to activate the next
81 // event, and events which return false on
82 // isCharged() will never be activated by the
84 if(pEvent
->isCharged())
85 return rEventQueue
.addEvent( pEvent
);
87 return false; // no more (active) events in queue
90 /** @return at least one event has been posted
92 template <typename ContainerT
>
93 bool fireAllEvents( ContainerT
& rQueue
, EventQueue
& rEventQueue
)
95 bool bFiredAny
= false;
96 while (fireSingleEvent( rQueue
, rEventQueue
))
108 void addEvent( const EventSharedPtr
& rEvent
)
110 maEvents
.push( rEvent
);
114 ImpEventQueue maEvents
;
119 class AllAnimationEventHandler
: public AnimationEventHandler
122 explicit AllAnimationEventHandler( EventQueue
& rEventQueue
) :
123 mrEventQueue( rEventQueue
),
124 maAnimationEventMap()
127 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr
& rNode
) override
129 ENSURE_OR_RETURN_FALSE(
131 "AllAnimationEventHandler::handleAnimationEvent(): Invalid node" );
135 ImpAnimationEventMap::iterator aIter
;
136 if( (aIter
=maAnimationEventMap
.find(
137 rNode
->getXAnimationNode() )) != maAnimationEventMap
.end() )
139 ImpEventVector
& rVec( aIter
->second
);
141 bRet
= !rVec
.empty();
143 // registered node found -> fire all events in the vector
144 for( const auto& pEvent
: rVec
)
145 mrEventQueue
.addEvent( pEvent
);
153 void addEvent( const EventSharedPtr
& rEvent
,
154 const uno::Reference
< animations::XAnimationNode
>& xNode
)
156 ImpAnimationEventMap::iterator aIter
;
157 if( (aIter
=maAnimationEventMap
.find( xNode
)) ==
158 maAnimationEventMap
.end() )
160 // no entry for this animation -> create one
161 aIter
= maAnimationEventMap
.emplace( xNode
, ImpEventVector() ).first
;
164 // add new event to queue
165 aIter
->second
.push_back( rEvent
);
169 EventQueue
& mrEventQueue
;
170 ImpAnimationEventMap maAnimationEventMap
;
173 class ClickEventHandler
: public MouseEventHandler_
,
175 public EventContainer
178 explicit ClickEventHandler( EventQueue
& rEventQueue
) :
180 mrEventQueue( rEventQueue
),
181 mbAdvanceOnClick( true )
184 void setAdvanceOnClick( bool bAdvanceOnClick
)
186 mbAdvanceOnClick
= bAdvanceOnClick
;
191 // triggered by API calls, e.g. space bar
192 virtual bool handleEvent() override
194 return handleEvent_impl();
197 // triggered by mouse release:
198 virtual bool handleMouseReleased( const awt::MouseEvent
& evt
) override
200 if(evt
.Buttons
!= awt::MouseButton::LEFT
)
203 if( mbAdvanceOnClick
) {
205 return handleEvent_impl();
208 return false; // advance-on-click disabled
212 // triggered by both:
213 virtual bool handleEvent_impl()
216 return fireSingleEvent( maEvents
, mrEventQueue
);
220 EventQueue
& mrEventQueue
;
221 bool mbAdvanceOnClick
;
224 class SkipEffectEventHandler
: public ClickEventHandler
227 SkipEffectEventHandler( EventQueue
& rEventQueue
,
228 EventMultiplexer
& rEventMultiplexer
)
229 : ClickEventHandler(rEventQueue
),
230 mrEventQueue(rEventQueue
),
231 mrEventMultiplexer(rEventMultiplexer
),
232 mbSkipTriggersNextEffect(true) {}
234 /** Remember to trigger (or not to trigger) the next effect after the
235 current effect is skipped.
237 void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect
)
238 { mbSkipTriggersNextEffect
= bSkipTriggersNextEffect
; }
240 /// Skip the current effect but do not trigger the next effect.
241 void skipEffect() { handleEvent_impl(false); }
244 virtual bool handleEvent_impl() override
246 return handleEvent_impl(true);
249 bool handleEvent_impl (bool bNotifyNextEffect
)
251 // fire all events, so animation nodes can register their
252 // next effect listeners:
253 if(fireAllEvents( maEvents
, mrEventQueue
))
255 if (mbSkipTriggersNextEffect
&& bNotifyNextEffect
)
257 // then simulate a next effect event: this skip effect
258 // handler is triggered upon next effect events (multiplexer
259 // prio=-1)! Posting a notifyNextEffect() here is only safe
260 // (we don't run into busy loop), because we assume that
261 // someone has registerered above for next effects
262 // (multiplexer prio=0) at the user event queue.
263 return mrEventQueue
.addEventWhenQueueIsEmpty(
264 makeEvent( [this] () {
265 this->mrEventMultiplexer
.notifyNextEffect();
266 }, "EventMultiplexer::notifyNextEffect") );
275 EventQueue
& mrEventQueue
;
276 EventMultiplexer
& mrEventMultiplexer
;
277 bool mbSkipTriggersNextEffect
;
280 /** Base class to share some common code between
281 ShapeClickEventHandler and MouseMoveHandler
283 @derive override necessary MouseEventHandler interface methods,
284 call sendEvent() method to actually process the event.
286 class MouseHandlerBase
: public MouseEventHandler_
289 explicit MouseHandlerBase( EventQueue
& rEventQueue
) :
290 mrEventQueue( rEventQueue
),
294 void addEvent( const EventSharedPtr
& rEvent
,
295 const ShapeSharedPtr
& rShape
)
297 ImpShapeEventMap::iterator aIter
;
298 if( (aIter
=maShapeEventMap
.find( rShape
)) == maShapeEventMap
.end() )
300 // no entry for this shape -> create one
301 aIter
= maShapeEventMap
.emplace(rShape
, ImpEventQueue()).first
;
304 // add new event to queue
305 aIter
->second
.push( rEvent
);
309 bool hitTest( const awt::MouseEvent
& e
,
310 ImpShapeEventMap::reverse_iterator
& o_rHitShape
)
312 // find hit shape in map
313 const basegfx::B2DPoint
aPosition( e
.X
, e
.Y
);
315 // find matching shape (scan reversely, to coarsely match
317 ImpShapeEventMap::reverse_iterator
aCurrShape(maShapeEventMap
.rbegin());
318 const ImpShapeEventMap::reverse_iterator
aEndShape( maShapeEventMap
.rend() );
319 while( aCurrShape
!= aEndShape
)
321 // TODO(F2): Get proper geometry polygon from the
322 // shape, to avoid having areas outside the shape
323 // react on the mouse
324 if( aCurrShape
->first
->getBounds().isInside( aPosition
) &&
325 aCurrShape
->first
->isVisible() )
327 // shape hit, and shape is visible - report a
329 o_rHitShape
= aCurrShape
;
336 return false; // nothing hit
339 bool sendEvent( ImpShapeEventMap::reverse_iterator
const & io_rHitShape
)
341 // take next event from queue
342 const bool bRet( fireSingleEvent( io_rHitShape
->second
,
345 // clear shape entry, if its queue is
346 // empty. This is important, since the shapes
347 // are held by shared ptr, and might otherwise
348 // not get released, even after their owning
349 // slide is long gone.
350 if( io_rHitShape
->second
.empty() )
352 // this looks funny, since ::std::map does
353 // provide an erase( iterator )
354 // method. Unfortunately, C++ does not
355 // declare the obvious erase(
356 // reverse_iterator ) needed here (missing
357 // orthogonality, eh?)
358 maShapeEventMap
.erase( io_rHitShape
->first
);
364 bool processEvent( const awt::MouseEvent
& e
)
366 ImpShapeEventMap::reverse_iterator aCurrShape
;
368 if( hitTest( e
, aCurrShape
) )
369 return sendEvent( aCurrShape
);
371 return false; // did not handle the event
375 EventQueue
& mrEventQueue
;
376 ImpShapeEventMap maShapeEventMap
;
379 class ShapeClickEventHandler
: public MouseHandlerBase
382 ShapeClickEventHandler( CursorManager
& rCursorManager
,
383 EventQueue
& rEventQueue
) :
384 MouseHandlerBase( rEventQueue
),
385 mrCursorManager( rCursorManager
)
388 virtual bool handleMouseReleased( const awt::MouseEvent
& e
) override
390 if(e
.Buttons
!= awt::MouseButton::LEFT
)
392 return processEvent( e
);
395 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
397 // TODO(P2): Maybe buffer last shape touched
399 // if we have a shape click event, and the mouse
400 // hovers over this shape, change cursor to hand
401 ImpShapeEventMap::reverse_iterator aDummy
;
402 if( hitTest( e
, aDummy
) )
403 mrCursorManager
.requestCursor( awt::SystemPointer::REFHAND
);
405 return false; // we don't /eat/ this event. Lower prio
406 // handler should see it, too.
410 CursorManager
& mrCursorManager
;
413 class MouseEnterHandler
: public MouseHandlerBase
416 explicit MouseEnterHandler( EventQueue
& rEventQueue
)
417 : MouseHandlerBase( rEventQueue
),
420 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
422 // TODO(P2): Maybe buffer last shape touched, and
423 // check against that _first_
425 ImpShapeEventMap::reverse_iterator aCurr
;
426 if( hitTest( e
, aCurr
) )
428 if( aCurr
->first
!= mpLastShape
)
430 // we actually hit a shape, and it's different
431 // from the previous one - thus we just
432 // entered it, raise event
434 mpLastShape
= aCurr
->first
;
439 // don't hit no shape - thus, last shape is NULL
443 return false; // we don't /eat/ this event. Lower prio
444 // handler should see it, too.
448 ShapeSharedPtr mpLastShape
;
451 class MouseLeaveHandler
: public MouseHandlerBase
454 explicit MouseLeaveHandler( EventQueue
& rEventQueue
)
455 : MouseHandlerBase( rEventQueue
),
458 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
460 // TODO(P2): Maybe buffer last shape touched, and
461 // check against that _first_
463 ImpShapeEventMap::reverse_iterator aCurr
;
464 if( hitTest( e
, aCurr
) )
470 if( maLastIter
->first
)
472 // last time, we were over a shape, now we're
473 // not - we thus just left that shape, raise
475 sendEvent( maLastIter
);
478 // in any case, when we hit this else-branch: no
479 // shape hit, thus have to clear maLastIter
480 maLastIter
= ImpShapeEventMap::reverse_iterator();
483 return false; // we don't /eat/ this event. Lower prio
484 // handler should see it, too.
488 ImpShapeEventMap::reverse_iterator maLastIter
;
491 template< typename Handler
, typename Functor
>
492 void UserEventQueue::registerEvent(
493 std::shared_ptr
< Handler
>& rHandler
,
494 const EventSharedPtr
& rEvent
,
495 const Functor
& rRegistrationFunctor
)
497 ENSURE_OR_THROW( rEvent
,
498 "UserEventQueue::registerEvent(): Invalid event" );
502 rHandler
.reset( new Handler( mrEventQueue
) );
503 // register handler on EventMultiplexer
504 rRegistrationFunctor( rHandler
);
507 rHandler
->addEvent( rEvent
);
510 template< typename Handler
, typename Arg
, typename Functor
>
511 void UserEventQueue::registerEvent(
512 std::shared_ptr
< Handler
>& rHandler
,
513 const EventSharedPtr
& rEvent
,
515 const Functor
& rRegistrationFunctor
)
517 ENSURE_OR_THROW( rEvent
,
518 "UserEventQueue::registerEvent(): Invalid event" );
522 rHandler
.reset( new Handler( mrEventQueue
) );
524 // register handler on EventMultiplexer
525 rRegistrationFunctor( rHandler
);
528 rHandler
->addEvent( rEvent
, rArg
);
532 UserEventQueue::UserEventQueue( EventMultiplexer
& rMultiplexer
,
533 EventQueue
& rEventQueue
,
534 CursorManager
& rCursorManager
)
535 : mrMultiplexer( rMultiplexer
),
536 mrEventQueue( rEventQueue
),
537 mrCursorManager( rCursorManager
),
538 mpAnimationStartEventHandler(),
539 mpAnimationEndEventHandler(),
540 mpAudioStoppedEventHandler(),
541 mpClickEventHandler(),
542 mpSkipEffectEventHandler(),
543 mpDoubleClickEventHandler(),
544 mpMouseEnterHandler(),
545 mpMouseLeaveHandler(),
546 mbAdvanceOnClick( true )
550 UserEventQueue::~UserEventQueue()
554 // unregister all handlers
557 catch (const uno::Exception
& e
)
559 SAL_WARN("slideshow", e
);
563 void UserEventQueue::clear()
565 // unregister and delete all handlers
566 if( mpAnimationStartEventHandler
) {
567 mrMultiplexer
.removeAnimationStartHandler(
568 mpAnimationStartEventHandler
);
569 mpAnimationStartEventHandler
.reset();
571 if( mpAnimationEndEventHandler
) {
572 mrMultiplexer
.removeAnimationEndHandler( mpAnimationEndEventHandler
);
573 mpAnimationEndEventHandler
.reset();
575 if( mpAudioStoppedEventHandler
) {
576 mrMultiplexer
.removeAudioStoppedHandler( mpAudioStoppedEventHandler
);
577 mpAudioStoppedEventHandler
.reset();
579 if( mpShapeClickEventHandler
) {
580 mrMultiplexer
.removeClickHandler( mpShapeClickEventHandler
);
581 mrMultiplexer
.removeMouseMoveHandler( mpShapeClickEventHandler
);
582 mpShapeClickEventHandler
.reset();
584 if( mpClickEventHandler
) {
585 mrMultiplexer
.removeClickHandler( mpClickEventHandler
);
586 mrMultiplexer
.removeNextEffectHandler( mpClickEventHandler
);
587 mpClickEventHandler
.reset();
589 if(mpSkipEffectEventHandler
) {
590 mrMultiplexer
.removeClickHandler( mpSkipEffectEventHandler
);
591 mrMultiplexer
.removeNextEffectHandler( mpSkipEffectEventHandler
);
592 mpSkipEffectEventHandler
.reset();
594 if( mpShapeDoubleClickEventHandler
) {
595 mrMultiplexer
.removeDoubleClickHandler( mpShapeDoubleClickEventHandler
);
596 mrMultiplexer
.removeMouseMoveHandler( mpShapeDoubleClickEventHandler
);
597 mpShapeDoubleClickEventHandler
.reset();
599 if( mpDoubleClickEventHandler
) {
600 mrMultiplexer
.removeDoubleClickHandler( mpDoubleClickEventHandler
);
601 mpDoubleClickEventHandler
.reset();
603 if( mpMouseEnterHandler
) {
604 mrMultiplexer
.removeMouseMoveHandler( mpMouseEnterHandler
);
605 mpMouseEnterHandler
.reset();
607 if( mpMouseLeaveHandler
) {
608 mrMultiplexer
.removeMouseMoveHandler( mpMouseLeaveHandler
);
609 mpMouseLeaveHandler
.reset();
613 void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick
)
615 mbAdvanceOnClick
= bAdvanceOnClick
;
617 // forward to handler, if existing. Otherwise, the handler
618 // creation will do the forwarding.
619 if( mpClickEventHandler
)
620 mpClickEventHandler
->setAdvanceOnClick( bAdvanceOnClick
);
623 void UserEventQueue::registerAnimationStartEvent(
624 const EventSharedPtr
& rEvent
,
625 const uno::Reference
< animations::XAnimationNode
>& xNode
)
627 registerEvent( mpAnimationStartEventHandler
,
630 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
631 { return this->mrMultiplexer
.addAnimationStartHandler( rHandler
); } );
634 void UserEventQueue::registerAnimationEndEvent(
635 const EventSharedPtr
& rEvent
,
636 const uno::Reference
<animations::XAnimationNode
>& xNode
)
638 registerEvent( mpAnimationEndEventHandler
,
641 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
642 { return this->mrMultiplexer
.addAnimationEndHandler( rHandler
); } );
645 void UserEventQueue::registerAudioStoppedEvent(
646 const EventSharedPtr
& rEvent
,
647 const uno::Reference
<animations::XAnimationNode
>& xNode
)
649 registerEvent( mpAudioStoppedEventHandler
,
652 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
653 { return this->mrMultiplexer
.addAudioStoppedHandler( rHandler
); } );
656 void UserEventQueue::registerShapeClickEvent( const EventSharedPtr
& rEvent
,
657 const ShapeSharedPtr
& rShape
)
661 "UserEventQueue::registerShapeClickEvent(): Invalid event" );
663 if( !mpShapeClickEventHandler
)
666 mpShapeClickEventHandler
.reset(
667 new ShapeClickEventHandler(mrCursorManager
,
670 // register handler on EventMultiplexer
671 mrMultiplexer
.addClickHandler( mpShapeClickEventHandler
, 1.0 );
672 mrMultiplexer
.addMouseMoveHandler( mpShapeClickEventHandler
, 1.0 );
675 mpShapeClickEventHandler
->addEvent( rEvent
, rShape
);
679 class ClickEventRegistrationFunctor
682 ClickEventRegistrationFunctor( EventMultiplexer
& rMultiplexer
,
684 bool bAdvanceOnClick
)
685 : mrMultiplexer( rMultiplexer
),
687 mbAdvanceOnClick( bAdvanceOnClick
) {}
689 void operator()( const std::shared_ptr
<ClickEventHandler
>& rHandler
)const
691 // register the handler on _two_ sources: we want the
692 // nextEffect events, e.g. space bar, to trigger clicks, as well!
693 mrMultiplexer
.addClickHandler( rHandler
, mnPrio
);
694 mrMultiplexer
.addNextEffectHandler( rHandler
, mnPrio
);
696 // forward advance-on-click state to newly
697 // generated handler (that's the only reason why
698 // we're called here)
699 rHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
703 EventMultiplexer
& mrMultiplexer
;
705 bool const mbAdvanceOnClick
;
709 void UserEventQueue::registerNextEffectEvent( const EventSharedPtr
& rEvent
)
711 // TODO: better name may be mpNextEffectEventHandler? then we have
712 // next effect (=> waiting to be started)
713 // skip effect (skipping the currently running one)
714 // rewind effect (rewinding back running one and waiting (again)
716 registerEvent( mpClickEventHandler
,
718 ClickEventRegistrationFunctor( mrMultiplexer
,
719 0.0 /* default prio */,
720 mbAdvanceOnClick
) );
723 void UserEventQueue::registerSkipEffectEvent(
724 EventSharedPtr
const & pEvent
,
725 const bool bSkipTriggersNextEffect
)
727 if(!mpSkipEffectEventHandler
)
729 mpSkipEffectEventHandler
.reset(
730 new SkipEffectEventHandler( mrEventQueue
, mrMultiplexer
) );
731 // register the handler on _two_ sources: we want the
732 // nextEffect events, e.g. space bar, to trigger clicks, as well!
733 mrMultiplexer
.addClickHandler( mpSkipEffectEventHandler
,
734 -1.0 /* prio below default */ );
735 mrMultiplexer
.addNextEffectHandler( mpSkipEffectEventHandler
,
736 -1.0 /* prio below default */ );
737 // forward advance-on-click state to newly
738 // generated handler (that's the only reason why
739 // we're called here)
740 mpSkipEffectEventHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
742 mpSkipEffectEventHandler
->setSkipTriggersNextEffect(bSkipTriggersNextEffect
);
743 mpSkipEffectEventHandler
->addEvent( pEvent
);
746 void UserEventQueue::registerShapeDoubleClickEvent(
747 const EventSharedPtr
& rEvent
,
748 const ShapeSharedPtr
& rShape
)
752 "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
754 if( !mpShapeDoubleClickEventHandler
)
757 mpShapeDoubleClickEventHandler
.reset(
758 new ShapeClickEventHandler(mrCursorManager
,
761 // register handler on EventMultiplexer
762 mrMultiplexer
.addDoubleClickHandler( mpShapeDoubleClickEventHandler
,
764 mrMultiplexer
.addMouseMoveHandler( mpShapeDoubleClickEventHandler
,
768 mpShapeDoubleClickEventHandler
->addEvent( rEvent
, rShape
);
771 void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr
& rEvent
,
772 const ShapeSharedPtr
& rShape
)
774 registerEvent( mpMouseEnterHandler
,
777 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
778 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
781 void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr
& rEvent
,
782 const ShapeSharedPtr
& rShape
)
784 registerEvent( mpMouseLeaveHandler
,
787 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
788 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
791 void UserEventQueue::callSkipEffectEventHandler()
793 ::std::shared_ptr
<SkipEffectEventHandler
> pHandler (
794 ::std::dynamic_pointer_cast
<SkipEffectEventHandler
>(mpSkipEffectEventHandler
));
796 pHandler
->skipEffect();
799 } // namespace internal
800 } // namespace presentation
802 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */