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 <comphelper/diagnose_ex.hxx>
23 #include <com/sun/star/awt/SystemPointer.hpp>
24 #include <com/sun/star/awt/MouseButton.hpp>
25 #include <com/sun/star/awt/MouseEvent.hpp>
27 #include <delayevent.hxx>
28 #include <usereventqueue.hxx>
29 #include <cursormanager.hxx>
37 using namespace com::sun::star
;
39 /* Implementation of UserEventQueue class */
41 namespace slideshow::internal
{
45 typedef std::vector
<EventSharedPtr
> ImpEventVector
;
46 typedef std::queue
<EventSharedPtr
> ImpEventQueue
;
47 typedef std::map
<uno::Reference
<animations::XAnimationNode
>,
48 ImpEventVector
> ImpAnimationEventMap
;
49 typedef std::map
<ShapeSharedPtr
, ImpEventQueue
,
50 Shape::lessThanShape
> ImpShapeEventMap
;
52 // MouseEventHandler base class, not consuming any event:
53 class MouseEventHandler_
: public MouseEventHandler
56 virtual bool handleMousePressed( awt::MouseEvent
const& /*e*/ ) override
{ return false;}
57 virtual bool handleMouseReleased( awt::MouseEvent
const& /*e*/) override
{ return false;}
58 virtual bool handleMouseDragged( awt::MouseEvent
const& /*e*/ ) override
{ return false;}
59 virtual bool handleMouseMoved( awt::MouseEvent
const& /*e*/ ) override
{ return false; }
62 /** @return one event has been posted
64 template <typename ContainerT
>
65 bool fireSingleEvent( ContainerT
& rQueue
, EventQueue
& rEventQueue
)
67 // post next event in given queue:
68 while (! rQueue
.empty())
70 EventSharedPtr
const pEvent(rQueue
.front());
73 // skip all inactive events (as the purpose of
74 // nextEventFromQueue() is to activate the next
75 // event, and events which return false on
76 // isCharged() will never be activated by the
78 if(pEvent
->isCharged())
79 return rEventQueue
.addEvent( pEvent
);
81 return false; // no more (active) events in queue
84 /** @return at least one event has been posted
86 template <typename ContainerT
>
87 bool fireAllEvents( ContainerT
& rQueue
, EventQueue
& rEventQueue
)
89 bool bFiredAny
= false;
90 while (fireSingleEvent( rQueue
, rEventQueue
))
102 void addEvent( const EventSharedPtr
& rEvent
)
104 maEvents
.push( rEvent
);
108 ImpEventQueue maEvents
;
113 class AllAnimationEventHandler
: public AnimationEventHandler
116 explicit AllAnimationEventHandler( EventQueue
& rEventQueue
) :
117 mrEventQueue( rEventQueue
),
118 maAnimationEventMap()
121 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr
& rNode
) override
123 ENSURE_OR_RETURN_FALSE(
125 "AllAnimationEventHandler::handleAnimationEvent(): Invalid node" );
129 ImpAnimationEventMap::iterator aIter
;
130 if( (aIter
=maAnimationEventMap
.find(
131 rNode
->getXAnimationNode() )) != maAnimationEventMap
.end() )
133 ImpEventVector
& rVec( aIter
->second
);
135 bRet
= !rVec
.empty();
137 // registered node found -> fire all events in the vector
138 for( const auto& pEvent
: rVec
)
139 mrEventQueue
.addEvent( pEvent
);
147 void addEvent( const EventSharedPtr
& rEvent
,
148 const uno::Reference
< animations::XAnimationNode
>& xNode
)
150 ImpAnimationEventMap::iterator aIter
;
151 if( (aIter
=maAnimationEventMap
.find( xNode
)) ==
152 maAnimationEventMap
.end() )
154 // no entry for this animation -> create one
155 aIter
= maAnimationEventMap
.emplace( xNode
, ImpEventVector() ).first
;
158 // add new event to queue
159 aIter
->second
.push_back( rEvent
);
163 EventQueue
& mrEventQueue
;
164 ImpAnimationEventMap maAnimationEventMap
;
167 class ClickEventHandler
: public MouseEventHandler_
,
169 public EventContainer
172 explicit ClickEventHandler( EventQueue
& rEventQueue
) :
174 mrEventQueue( rEventQueue
),
175 mbAdvanceOnClick( true )
178 void setAdvanceOnClick( bool bAdvanceOnClick
)
180 mbAdvanceOnClick
= bAdvanceOnClick
;
185 // triggered by API calls, e.g. space bar
186 virtual bool handleEvent() override
188 return handleEvent_impl();
191 // triggered by mouse release:
192 virtual bool handleMouseReleased( const awt::MouseEvent
& evt
) override
194 if(evt
.Buttons
!= awt::MouseButton::LEFT
)
197 if( mbAdvanceOnClick
) {
199 return handleEvent_impl();
202 return false; // advance-on-click disabled
206 // triggered by both:
207 virtual bool handleEvent_impl()
210 return fireSingleEvent( maEvents
, mrEventQueue
);
214 EventQueue
& mrEventQueue
;
215 bool mbAdvanceOnClick
;
218 class SkipEffectEventHandler
: public ClickEventHandler
221 SkipEffectEventHandler( EventQueue
& rEventQueue
,
222 EventMultiplexer
& rEventMultiplexer
)
223 : ClickEventHandler(rEventQueue
),
224 mrEventQueue(rEventQueue
),
225 mrEventMultiplexer(rEventMultiplexer
),
226 mbSkipTriggersNextEffect(true) {}
228 /** Remember to trigger (or not to trigger) the next effect after the
229 current effect is skipped.
231 void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect
)
232 { mbSkipTriggersNextEffect
= bSkipTriggersNextEffect
; }
234 /// Skip the current effect but do not trigger the next effect.
235 void skipEffect() { handleEvent_impl(false); }
238 virtual bool handleEvent_impl() override
240 return handleEvent_impl(true);
243 bool handleEvent_impl (bool bNotifyNextEffect
)
245 // fire all events, so animation nodes can register their
246 // next effect listeners:
247 if(fireAllEvents( maEvents
, mrEventQueue
))
249 if (mbSkipTriggersNextEffect
&& bNotifyNextEffect
)
251 // then simulate a next effect event: this skip effect
252 // handler is triggered upon next effect events (multiplexer
253 // prio=-1)! Posting a notifyNextEffect() here is only safe
254 // (we don't run into busy loop), because we assume that
255 // someone has registered above for next effects
256 // (multiplexer prio=0) at the user event queue.
257 return mrEventQueue
.addEventWhenQueueIsEmpty(
258 makeEvent( [this] () {
259 this->mrEventMultiplexer
.notifyNextEffect();
260 }, u
"EventMultiplexer::notifyNextEffect"_ustr
) );
269 EventQueue
& mrEventQueue
;
270 EventMultiplexer
& mrEventMultiplexer
;
271 bool mbSkipTriggersNextEffect
;
276 /** Base class to share some common code between
277 ShapeClickEventHandler and MouseMoveHandler
279 @derive override necessary MouseEventHandler interface methods,
280 call sendEvent() method to actually process the event.
282 class MouseHandlerBase
: public MouseEventHandler_
285 explicit MouseHandlerBase( EventQueue
& rEventQueue
) :
286 mrEventQueue( rEventQueue
),
290 void addEvent( const EventSharedPtr
& rEvent
,
291 const ShapeSharedPtr
& rShape
)
293 ImpShapeEventMap::iterator aIter
;
294 if( (aIter
=maShapeEventMap
.find( rShape
)) == maShapeEventMap
.end() )
296 // no entry for this shape -> create one
297 aIter
= maShapeEventMap
.emplace(rShape
, ImpEventQueue()).first
;
300 // add new event to queue
301 aIter
->second
.push( rEvent
);
305 bool hitTest( const awt::MouseEvent
& e
,
306 ImpShapeEventMap::reverse_iterator
& o_rHitShape
)
308 // find hit shape in map
309 const basegfx::B2DPoint
aPosition( e
.X
, e
.Y
);
311 // find matching shape (scan reversely, to coarsely match
313 auto aCurrShape
= std::find_if(maShapeEventMap
.rbegin(), maShapeEventMap
.rend(),
314 [&aPosition
](const ImpShapeEventMap::value_type
& rShape
) {
315 // TODO(F2): Get proper geometry polygon from the
316 // shape, to avoid having areas outside the shape
317 // react on the mouse
318 return rShape
.first
->getBounds().isInside( aPosition
)
319 && rShape
.first
->isVisible();
321 if (aCurrShape
!= maShapeEventMap
.rend())
323 // shape hit, and shape is visible - report a
325 o_rHitShape
= aCurrShape
;
329 return false; // nothing hit
332 bool sendEvent( ImpShapeEventMap::reverse_iterator
const & io_rHitShape
)
334 // take next event from queue
335 const bool bRet( fireSingleEvent( io_rHitShape
->second
,
338 // clear shape entry, if its queue is
339 // empty. This is important, since the shapes
340 // are held by shared ptr, and might otherwise
341 // not get released, even after their owning
342 // slide is long gone.
343 if( io_rHitShape
->second
.empty() )
345 // this looks funny, since ::std::map does
346 // provide an erase( iterator )
347 // method. Unfortunately, C++ does not
348 // declare the obvious erase(
349 // reverse_iterator ) needed here (missing
350 // orthogonality, eh?)
351 maShapeEventMap
.erase( io_rHitShape
->first
);
357 bool processEvent( const awt::MouseEvent
& e
)
359 ImpShapeEventMap::reverse_iterator aCurrShape
;
361 if( hitTest( e
, aCurrShape
) )
362 return sendEvent( aCurrShape
);
364 return false; // did not handle the event
368 EventQueue
& mrEventQueue
;
369 ImpShapeEventMap maShapeEventMap
;
374 class ShapeClickEventHandler
: public MouseHandlerBase
377 ShapeClickEventHandler( CursorManager
& rCursorManager
,
378 EventQueue
& rEventQueue
) :
379 MouseHandlerBase( rEventQueue
),
380 mrCursorManager( rCursorManager
)
383 virtual bool handleMouseReleased( const awt::MouseEvent
& e
) override
385 if(e
.Buttons
!= awt::MouseButton::LEFT
)
387 return processEvent( e
);
390 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
392 // TODO(P2): Maybe buffer last shape touched
394 // if we have a shape click event, and the mouse
395 // hovers over this shape, change cursor to hand
396 ImpShapeEventMap::reverse_iterator aDummy
;
397 if( hitTest( e
, aDummy
) )
398 mrCursorManager
.requestCursor( awt::SystemPointer::REFHAND
);
400 return false; // we don't /eat/ this event. Lower prio
401 // handler should see it, too.
405 CursorManager
& mrCursorManager
;
408 class MouseEnterHandler
: public MouseHandlerBase
411 explicit MouseEnterHandler( EventQueue
& rEventQueue
)
412 : MouseHandlerBase( rEventQueue
),
415 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
417 // TODO(P2): Maybe buffer last shape touched, and
418 // check against that _first_
420 ImpShapeEventMap::reverse_iterator aCurr
;
421 if( hitTest( e
, aCurr
) )
423 if( aCurr
->first
!= mpLastShape
)
425 // we actually hit a shape, and it's different
426 // from the previous one - thus we just
427 // entered it, raise event
429 mpLastShape
= aCurr
->first
;
434 // don't hit no shape - thus, last shape is NULL
438 return false; // we don't /eat/ this event. Lower prio
439 // handler should see it, too.
443 ShapeSharedPtr mpLastShape
;
446 class MouseLeaveHandler
: public MouseHandlerBase
449 explicit MouseLeaveHandler( EventQueue
& rEventQueue
)
450 : MouseHandlerBase( rEventQueue
),
453 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
455 // TODO(P2): Maybe buffer last shape touched, and
456 // check against that _first_
458 ImpShapeEventMap::reverse_iterator aCurr
;
459 if( hitTest( e
, aCurr
) )
465 if( maLastIter
->first
)
467 // last time, we were over a shape, now we're
468 // not - we thus just left that shape, raise
470 sendEvent( maLastIter
);
473 // in any case, when we hit this else-branch: no
474 // shape hit, thus have to clear maLastIter
475 maLastIter
= ImpShapeEventMap::reverse_iterator();
478 return false; // we don't /eat/ this event. Lower prio
479 // handler should see it, too.
483 ImpShapeEventMap::reverse_iterator maLastIter
;
486 template< typename Handler
, typename Functor
>
487 void UserEventQueue::registerEvent(
488 std::shared_ptr
< Handler
>& rHandler
,
489 const EventSharedPtr
& rEvent
,
490 const Functor
& rRegistrationFunctor
)
492 ENSURE_OR_THROW( rEvent
,
493 "UserEventQueue::registerEvent(): Invalid event" );
497 rHandler
= std::make_shared
<Handler
>( mrEventQueue
);
498 // register handler on EventMultiplexer
499 rRegistrationFunctor( rHandler
);
502 rHandler
->addEvent( rEvent
);
505 template< typename Handler
, typename Arg
, typename Functor
>
506 void UserEventQueue::registerEvent(
507 std::shared_ptr
< Handler
>& rHandler
,
508 const EventSharedPtr
& rEvent
,
510 const Functor
& rRegistrationFunctor
)
512 ENSURE_OR_THROW( rEvent
,
513 "UserEventQueue::registerEvent(): Invalid event" );
517 rHandler
= std::make_shared
<Handler
>( mrEventQueue
);
519 // register handler on EventMultiplexer
520 rRegistrationFunctor( rHandler
);
523 rHandler
->addEvent( rEvent
, rArg
);
527 UserEventQueue::UserEventQueue( EventMultiplexer
& rMultiplexer
,
528 EventQueue
& rEventQueue
,
529 CursorManager
& rCursorManager
)
530 : mrMultiplexer( rMultiplexer
),
531 mrEventQueue( rEventQueue
),
532 mrCursorManager( rCursorManager
),
533 mpAnimationStartEventHandler(),
534 mpAnimationEndEventHandler(),
535 mpAudioStoppedEventHandler(),
536 mpClickEventHandler(),
537 mpSkipEffectEventHandler(),
538 mpMouseEnterHandler(),
539 mpMouseLeaveHandler(),
540 mbAdvanceOnClick( true )
544 UserEventQueue::~UserEventQueue()
548 // unregister all handlers
551 catch (const uno::Exception
&)
553 TOOLS_WARN_EXCEPTION("slideshow", "");
557 void UserEventQueue::clear()
559 // unregister and delete all handlers
560 if( mpAnimationStartEventHandler
) {
561 mrMultiplexer
.removeAnimationStartHandler(
562 mpAnimationStartEventHandler
);
563 mpAnimationStartEventHandler
.reset();
565 if( mpAnimationEndEventHandler
) {
566 mrMultiplexer
.removeAnimationEndHandler( mpAnimationEndEventHandler
);
567 mpAnimationEndEventHandler
.reset();
569 if( mpAudioStoppedEventHandler
) {
570 mrMultiplexer
.removeAudioStoppedHandler( mpAudioStoppedEventHandler
);
571 mpAudioStoppedEventHandler
.reset();
573 if( mpShapeClickEventHandler
) {
574 mrMultiplexer
.removeClickHandler( mpShapeClickEventHandler
);
575 mrMultiplexer
.removeMouseMoveHandler( mpShapeClickEventHandler
);
576 mpShapeClickEventHandler
.reset();
578 if( mpClickEventHandler
) {
579 mrMultiplexer
.removeClickHandler( mpClickEventHandler
);
580 mrMultiplexer
.removeNextEffectHandler( mpClickEventHandler
);
581 mpClickEventHandler
.reset();
583 if(mpSkipEffectEventHandler
) {
584 mrMultiplexer
.removeClickHandler( mpSkipEffectEventHandler
);
585 mrMultiplexer
.removeNextEffectHandler( mpSkipEffectEventHandler
);
586 mpSkipEffectEventHandler
.reset();
588 if( mpShapeDoubleClickEventHandler
) {
589 mrMultiplexer
.removeDoubleClickHandler( mpShapeDoubleClickEventHandler
);
590 mrMultiplexer
.removeMouseMoveHandler( mpShapeDoubleClickEventHandler
);
591 mpShapeDoubleClickEventHandler
.reset();
593 if( mpMouseEnterHandler
) {
594 mrMultiplexer
.removeMouseMoveHandler( mpMouseEnterHandler
);
595 mpMouseEnterHandler
.reset();
597 if( mpMouseLeaveHandler
) {
598 mrMultiplexer
.removeMouseMoveHandler( mpMouseLeaveHandler
);
599 mpMouseLeaveHandler
.reset();
603 void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick
)
605 mbAdvanceOnClick
= bAdvanceOnClick
;
607 // forward to handler, if existing. Otherwise, the handler
608 // creation will do the forwarding.
609 if( mpClickEventHandler
)
610 mpClickEventHandler
->setAdvanceOnClick( bAdvanceOnClick
);
613 void UserEventQueue::registerAnimationStartEvent(
614 const EventSharedPtr
& rEvent
,
615 const uno::Reference
< animations::XAnimationNode
>& xNode
)
617 registerEvent( mpAnimationStartEventHandler
,
620 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
621 { return this->mrMultiplexer
.addAnimationStartHandler( rHandler
); } );
624 void UserEventQueue::registerAnimationEndEvent(
625 const EventSharedPtr
& rEvent
,
626 const uno::Reference
<animations::XAnimationNode
>& xNode
)
628 registerEvent( mpAnimationEndEventHandler
,
631 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
632 { return this->mrMultiplexer
.addAnimationEndHandler( rHandler
); } );
635 void UserEventQueue::registerAudioStoppedEvent(
636 const EventSharedPtr
& rEvent
,
637 const uno::Reference
<animations::XAnimationNode
>& xNode
)
639 registerEvent( mpAudioStoppedEventHandler
,
642 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
643 { return this->mrMultiplexer
.addAudioStoppedHandler( rHandler
); } );
646 void UserEventQueue::registerShapeClickEvent( const EventSharedPtr
& rEvent
,
647 const ShapeSharedPtr
& rShape
)
651 "UserEventQueue::registerShapeClickEvent(): Invalid event" );
653 if( !mpShapeClickEventHandler
)
656 mpShapeClickEventHandler
=
657 std::make_shared
<ShapeClickEventHandler
>(mrCursorManager
,
660 // register handler on EventMultiplexer
661 mrMultiplexer
.addClickHandler( mpShapeClickEventHandler
, 1.0 );
662 mrMultiplexer
.addMouseMoveHandler( mpShapeClickEventHandler
, 1.0 );
665 mpShapeClickEventHandler
->addEvent( rEvent
, rShape
);
669 class ClickEventRegistrationFunctor
672 ClickEventRegistrationFunctor( EventMultiplexer
& rMultiplexer
,
674 bool bAdvanceOnClick
)
675 : mrMultiplexer( rMultiplexer
),
677 mbAdvanceOnClick( bAdvanceOnClick
) {}
679 void operator()( const std::shared_ptr
<ClickEventHandler
>& rHandler
)const
681 // register the handler on _two_ sources: we want the
682 // nextEffect events, e.g. space bar, to trigger clicks, as well!
683 mrMultiplexer
.addClickHandler( rHandler
, mnPrio
);
684 mrMultiplexer
.addNextEffectHandler( rHandler
, mnPrio
);
686 // forward advance-on-click state to newly
687 // generated handler (that's the only reason why
688 // we're called here)
689 rHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
693 EventMultiplexer
& mrMultiplexer
;
695 bool const mbAdvanceOnClick
;
699 void UserEventQueue::registerNextEffectEvent( const EventSharedPtr
& rEvent
)
701 // TODO: better name may be mpNextEffectEventHandler? then we have
702 // next effect (=> waiting to be started)
703 // skip effect (skipping the currently running one)
704 // rewind effect (rewinding back running one and waiting (again)
706 registerEvent( mpClickEventHandler
,
708 ClickEventRegistrationFunctor( mrMultiplexer
,
709 0.0 /* default prio */,
710 mbAdvanceOnClick
) );
713 void UserEventQueue::registerSkipEffectEvent(
714 EventSharedPtr
const & pEvent
,
715 const bool bSkipTriggersNextEffect
)
717 if(!mpSkipEffectEventHandler
)
719 mpSkipEffectEventHandler
=
720 std::make_shared
<SkipEffectEventHandler
>( mrEventQueue
, mrMultiplexer
);
721 // register the handler on _two_ sources: we want the
722 // nextEffect events, e.g. space bar, to trigger clicks, as well!
723 mrMultiplexer
.addClickHandler( mpSkipEffectEventHandler
,
724 -1.0 /* prio below default */ );
725 mrMultiplexer
.addNextEffectHandler( mpSkipEffectEventHandler
,
726 -1.0 /* prio below default */ );
727 // forward advance-on-click state to newly
728 // generated handler (that's the only reason why
729 // we're called here)
730 mpSkipEffectEventHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
732 mpSkipEffectEventHandler
->setSkipTriggersNextEffect(bSkipTriggersNextEffect
);
733 mpSkipEffectEventHandler
->addEvent( pEvent
);
736 void UserEventQueue::registerShapeDoubleClickEvent(
737 const EventSharedPtr
& rEvent
,
738 const ShapeSharedPtr
& rShape
)
742 "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
744 if( !mpShapeDoubleClickEventHandler
)
747 mpShapeDoubleClickEventHandler
=
748 std::make_shared
<ShapeClickEventHandler
>(mrCursorManager
,
751 // register handler on EventMultiplexer
752 mrMultiplexer
.addDoubleClickHandler( mpShapeDoubleClickEventHandler
,
754 mrMultiplexer
.addMouseMoveHandler( mpShapeDoubleClickEventHandler
,
758 mpShapeDoubleClickEventHandler
->addEvent( rEvent
, rShape
);
761 void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr
& rEvent
,
762 const ShapeSharedPtr
& rShape
)
764 registerEvent( mpMouseEnterHandler
,
767 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
768 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
771 void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr
& rEvent
,
772 const ShapeSharedPtr
& rShape
)
774 registerEvent( mpMouseLeaveHandler
,
777 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
778 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
781 void UserEventQueue::callSkipEffectEventHandler()
783 ::std::shared_ptr
<SkipEffectEventHandler
> pHandler (
784 ::std::dynamic_pointer_cast
<SkipEffectEventHandler
>(mpSkipEffectEventHandler
));
786 pHandler
->skipEffect();
789 } // namespace presentation
791 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */