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
.insert(
162 ImpAnimationEventMap::value_type( xNode
,
163 ImpEventVector() ) ).first
;
166 // add new event to queue
167 aIter
->second
.push_back( rEvent
);
171 EventQueue
& mrEventQueue
;
172 ImpAnimationEventMap maAnimationEventMap
;
175 class ClickEventHandler
: public MouseEventHandler_
,
177 public EventContainer
180 explicit ClickEventHandler( EventQueue
& rEventQueue
) :
182 mrEventQueue( rEventQueue
),
183 mbAdvanceOnClick( true )
186 void setAdvanceOnClick( bool bAdvanceOnClick
)
188 mbAdvanceOnClick
= bAdvanceOnClick
;
193 // triggered by API calls, e.g. space bar
194 virtual bool handleEvent() override
196 return handleEvent_impl();
199 // triggered by mouse release:
200 virtual bool handleMouseReleased( const awt::MouseEvent
& evt
) override
202 if(evt
.Buttons
!= awt::MouseButton::LEFT
)
205 if( mbAdvanceOnClick
) {
207 return handleEvent_impl();
210 return false; // advance-on-click disabled
214 // triggered by both:
215 virtual bool handleEvent_impl()
218 return fireSingleEvent( maEvents
, mrEventQueue
);
222 EventQueue
& mrEventQueue
;
223 bool mbAdvanceOnClick
;
226 class SkipEffectEventHandler
: public ClickEventHandler
229 SkipEffectEventHandler( EventQueue
& rEventQueue
,
230 EventMultiplexer
& rEventMultiplexer
)
231 : ClickEventHandler(rEventQueue
),
232 mrEventQueue(rEventQueue
),
233 mrEventMultiplexer(rEventMultiplexer
),
234 mbSkipTriggersNextEffect(true) {}
236 /** Remember to trigger (or not to trigger) the next effect after the
237 current effect is skipped.
239 void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect
)
240 { mbSkipTriggersNextEffect
= bSkipTriggersNextEffect
; }
242 /// Skip the current effect but do not trigger the next effect.
243 void skipEffect() { handleEvent_impl(false); }
246 virtual bool handleEvent_impl() override
248 return handleEvent_impl(true);
251 bool handleEvent_impl (bool bNotifyNextEffect
)
253 // fire all events, so animation nodes can register their
254 // next effect listeners:
255 if(fireAllEvents( maEvents
, mrEventQueue
))
257 if (mbSkipTriggersNextEffect
&& bNotifyNextEffect
)
259 // then simulate a next effect event: this skip effect
260 // handler is triggered upon next effect events (multiplexer
261 // prio=-1)! Posting a notifyNextEffect() here is only safe
262 // (we don't run into busy loop), because we assume that
263 // someone has registerered above for next effects
264 // (multiplexer prio=0) at the user event queue.
265 return mrEventQueue
.addEventWhenQueueIsEmpty(
266 makeEvent( [this] () {
267 this->mrEventMultiplexer
.notifyNextEffect();
268 }, "EventMultiplexer::notifyNextEffect") );
277 EventQueue
& mrEventQueue
;
278 EventMultiplexer
& mrEventMultiplexer
;
279 bool mbSkipTriggersNextEffect
;
282 /** Base class to share some common code between
283 ShapeClickEventHandler and MouseMoveHandler
285 @derive override necessary MouseEventHandler interface methods,
286 call sendEvent() method to actually process the event.
288 class MouseHandlerBase
: public MouseEventHandler_
291 explicit MouseHandlerBase( EventQueue
& rEventQueue
) :
292 mrEventQueue( rEventQueue
),
296 void addEvent( const EventSharedPtr
& rEvent
,
297 const ShapeSharedPtr
& rShape
)
299 ImpShapeEventMap::iterator aIter
;
300 if( (aIter
=maShapeEventMap
.find( rShape
)) == maShapeEventMap
.end() )
302 // no entry for this shape -> create one
303 aIter
= maShapeEventMap
.insert(
304 ImpShapeEventMap::value_type( rShape
,
305 ImpEventQueue() ) ).first
;
308 // add new event to queue
309 aIter
->second
.push( rEvent
);
313 bool hitTest( const awt::MouseEvent
& e
,
314 ImpShapeEventMap::reverse_iterator
& o_rHitShape
)
316 // find hit shape in map
317 const basegfx::B2DPoint
aPosition( e
.X
, e
.Y
);
319 // find matching shape (scan reversely, to coarsely match
321 ImpShapeEventMap::reverse_iterator
aCurrShape(maShapeEventMap
.rbegin());
322 const ImpShapeEventMap::reverse_iterator
aEndShape( maShapeEventMap
.rend() );
323 while( aCurrShape
!= aEndShape
)
325 // TODO(F2): Get proper geometry polygon from the
326 // shape, to avoid having areas outside the shape
327 // react on the mouse
328 if( aCurrShape
->first
->getBounds().isInside( aPosition
) &&
329 aCurrShape
->first
->isVisible() )
331 // shape hit, and shape is visible - report a
333 o_rHitShape
= aCurrShape
;
340 return false; // nothing hit
343 bool sendEvent( ImpShapeEventMap::reverse_iterator
& io_rHitShape
)
345 // take next event from queue
346 const bool bRet( fireSingleEvent( io_rHitShape
->second
,
349 // clear shape entry, if its queue is
350 // empty. This is important, since the shapes
351 // are held by shared ptr, and might otherwise
352 // not get released, even after their owning
353 // slide is long gone.
354 if( io_rHitShape
->second
.empty() )
356 // this looks funny, since ::std::map does
357 // provide an erase( iterator )
358 // method. Unfortunately, C++ does not
359 // declare the obvious erase(
360 // reverse_iterator ) needed here (missing
361 // orthogonality, eh?)
362 maShapeEventMap
.erase( io_rHitShape
->first
);
368 bool processEvent( const awt::MouseEvent
& e
)
370 ImpShapeEventMap::reverse_iterator aCurrShape
;
372 if( hitTest( e
, aCurrShape
) )
373 return sendEvent( aCurrShape
);
375 return false; // did not handle the event
379 EventQueue
& mrEventQueue
;
380 ImpShapeEventMap maShapeEventMap
;
383 class ShapeClickEventHandler
: public MouseHandlerBase
386 ShapeClickEventHandler( CursorManager
& rCursorManager
,
387 EventQueue
& rEventQueue
) :
388 MouseHandlerBase( rEventQueue
),
389 mrCursorManager( rCursorManager
)
392 virtual bool handleMouseReleased( const awt::MouseEvent
& e
) override
394 if(e
.Buttons
!= awt::MouseButton::LEFT
)
396 return processEvent( e
);
399 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
401 // TODO(P2): Maybe buffer last shape touched
403 // if we have a shape click event, and the mouse
404 // hovers over this shape, change cursor to hand
405 ImpShapeEventMap::reverse_iterator aDummy
;
406 if( hitTest( e
, aDummy
) )
407 mrCursorManager
.requestCursor( awt::SystemPointer::REFHAND
);
409 return false; // we don't /eat/ this event. Lower prio
410 // handler should see it, too.
414 CursorManager
& mrCursorManager
;
417 class MouseEnterHandler
: public MouseHandlerBase
420 explicit MouseEnterHandler( EventQueue
& rEventQueue
)
421 : MouseHandlerBase( rEventQueue
),
424 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
426 // TODO(P2): Maybe buffer last shape touched, and
427 // check against that _first_
429 ImpShapeEventMap::reverse_iterator aCurr
;
430 if( hitTest( e
, aCurr
) )
432 if( aCurr
->first
!= mpLastShape
)
434 // we actually hit a shape, and it's different
435 // from the previous one - thus we just
436 // entered it, raise event
438 mpLastShape
= aCurr
->first
;
443 // don't hit no shape - thus, last shape is NULL
447 return false; // we don't /eat/ this event. Lower prio
448 // handler should see it, too.
452 ShapeSharedPtr mpLastShape
;
455 class MouseLeaveHandler
: public MouseHandlerBase
458 explicit MouseLeaveHandler( EventQueue
& rEventQueue
)
459 : MouseHandlerBase( rEventQueue
),
462 virtual bool handleMouseMoved( const awt::MouseEvent
& e
) override
464 // TODO(P2): Maybe buffer last shape touched, and
465 // check against that _first_
467 ImpShapeEventMap::reverse_iterator aCurr
;
468 if( hitTest( e
, aCurr
) )
474 if( maLastIter
->first
)
476 // last time, we were over a shape, now we're
477 // not - we thus just left that shape, raise
479 sendEvent( maLastIter
);
482 // in any case, when we hit this else-branch: no
483 // shape hit, thus have to clear maLastIter
484 maLastIter
= ImpShapeEventMap::reverse_iterator();
487 return false; // we don't /eat/ this event. Lower prio
488 // handler should see it, too.
492 ImpShapeEventMap::reverse_iterator maLastIter
;
495 template< typename Handler
, typename Functor
>
496 void UserEventQueue::registerEvent(
497 std::shared_ptr
< Handler
>& rHandler
,
498 const EventSharedPtr
& rEvent
,
499 const Functor
& rRegistrationFunctor
)
501 ENSURE_OR_THROW( rEvent
,
502 "UserEventQueue::registerEvent(): Invalid event" );
506 rHandler
.reset( new Handler( mrEventQueue
) );
507 // register handler on EventMultiplexer
508 rRegistrationFunctor( rHandler
);
511 rHandler
->addEvent( rEvent
);
514 template< typename Handler
, typename Arg
, typename Functor
>
515 void UserEventQueue::registerEvent(
516 std::shared_ptr
< Handler
>& rHandler
,
517 const EventSharedPtr
& rEvent
,
519 const Functor
& rRegistrationFunctor
)
521 ENSURE_OR_THROW( rEvent
,
522 "UserEventQueue::registerEvent(): Invalid event" );
526 rHandler
.reset( new Handler( mrEventQueue
) );
528 // register handler on EventMultiplexer
529 rRegistrationFunctor( rHandler
);
532 rHandler
->addEvent( rEvent
, rArg
);
536 UserEventQueue::UserEventQueue( EventMultiplexer
& rMultiplexer
,
537 EventQueue
& rEventQueue
,
538 CursorManager
& rCursorManager
)
539 : mrMultiplexer( rMultiplexer
),
540 mrEventQueue( rEventQueue
),
541 mrCursorManager( rCursorManager
),
542 mpAnimationStartEventHandler(),
543 mpAnimationEndEventHandler(),
544 mpAudioStoppedEventHandler(),
545 mpClickEventHandler(),
546 mpSkipEffectEventHandler(),
547 mpDoubleClickEventHandler(),
548 mpMouseEnterHandler(),
549 mpMouseLeaveHandler(),
550 mbAdvanceOnClick( true )
554 UserEventQueue::~UserEventQueue()
558 // unregister all handlers
561 catch (uno::Exception
&) {
562 OSL_FAIL( OUStringToOString(
563 comphelper::anyToString(
564 cppu::getCaughtException() ),
565 RTL_TEXTENCODING_UTF8
).getStr() );
569 void UserEventQueue::clear()
571 // unregister and delete all handlers
572 if( mpAnimationStartEventHandler
) {
573 mrMultiplexer
.removeAnimationStartHandler(
574 mpAnimationStartEventHandler
);
575 mpAnimationStartEventHandler
.reset();
577 if( mpAnimationEndEventHandler
) {
578 mrMultiplexer
.removeAnimationEndHandler( mpAnimationEndEventHandler
);
579 mpAnimationEndEventHandler
.reset();
581 if( mpAudioStoppedEventHandler
) {
582 mrMultiplexer
.removeAudioStoppedHandler( mpAudioStoppedEventHandler
);
583 mpAudioStoppedEventHandler
.reset();
585 if( mpShapeClickEventHandler
) {
586 mrMultiplexer
.removeClickHandler( mpShapeClickEventHandler
);
587 mrMultiplexer
.removeMouseMoveHandler( mpShapeClickEventHandler
);
588 mpShapeClickEventHandler
.reset();
590 if( mpClickEventHandler
) {
591 mrMultiplexer
.removeClickHandler( mpClickEventHandler
);
592 mrMultiplexer
.removeNextEffectHandler( mpClickEventHandler
);
593 mpClickEventHandler
.reset();
595 if(mpSkipEffectEventHandler
) {
596 mrMultiplexer
.removeClickHandler( mpSkipEffectEventHandler
);
597 mrMultiplexer
.removeNextEffectHandler( mpSkipEffectEventHandler
);
598 mpSkipEffectEventHandler
.reset();
600 if( mpShapeDoubleClickEventHandler
) {
601 mrMultiplexer
.removeDoubleClickHandler( mpShapeDoubleClickEventHandler
);
602 mrMultiplexer
.removeMouseMoveHandler( mpShapeDoubleClickEventHandler
);
603 mpShapeDoubleClickEventHandler
.reset();
605 if( mpDoubleClickEventHandler
) {
606 mrMultiplexer
.removeDoubleClickHandler( mpDoubleClickEventHandler
);
607 mpDoubleClickEventHandler
.reset();
609 if( mpMouseEnterHandler
) {
610 mrMultiplexer
.removeMouseMoveHandler( mpMouseEnterHandler
);
611 mpMouseEnterHandler
.reset();
613 if( mpMouseLeaveHandler
) {
614 mrMultiplexer
.removeMouseMoveHandler( mpMouseLeaveHandler
);
615 mpMouseLeaveHandler
.reset();
619 void UserEventQueue::setAdvanceOnClick( bool bAdvanceOnClick
)
621 mbAdvanceOnClick
= bAdvanceOnClick
;
623 // forward to handler, if existing. Otherwise, the handler
624 // creation will do the forwarding.
625 if( mpClickEventHandler
)
626 mpClickEventHandler
->setAdvanceOnClick( bAdvanceOnClick
);
629 void UserEventQueue::registerAnimationStartEvent(
630 const EventSharedPtr
& rEvent
,
631 const uno::Reference
< animations::XAnimationNode
>& xNode
)
633 registerEvent( mpAnimationStartEventHandler
,
636 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
637 { return this->mrMultiplexer
.addAnimationStartHandler( rHandler
); } );
640 void UserEventQueue::registerAnimationEndEvent(
641 const EventSharedPtr
& rEvent
,
642 const uno::Reference
<animations::XAnimationNode
>& xNode
)
644 registerEvent( mpAnimationEndEventHandler
,
647 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
648 { return this->mrMultiplexer
.addAnimationEndHandler( rHandler
); } );
651 void UserEventQueue::registerAudioStoppedEvent(
652 const EventSharedPtr
& rEvent
,
653 const uno::Reference
<animations::XAnimationNode
>& xNode
)
655 registerEvent( mpAudioStoppedEventHandler
,
658 [this]( const AnimationEventHandlerSharedPtr
& rHandler
)
659 { return this->mrMultiplexer
.addAudioStoppedHandler( rHandler
); } );
662 void UserEventQueue::registerShapeClickEvent( const EventSharedPtr
& rEvent
,
663 const ShapeSharedPtr
& rShape
)
667 "UserEventQueue::registerShapeClickEvent(): Invalid event" );
669 if( !mpShapeClickEventHandler
)
672 mpShapeClickEventHandler
.reset(
673 new ShapeClickEventHandler(mrCursorManager
,
676 // register handler on EventMultiplexer
677 mrMultiplexer
.addClickHandler( mpShapeClickEventHandler
, 1.0 );
678 mrMultiplexer
.addMouseMoveHandler( mpShapeClickEventHandler
, 1.0 );
681 mpShapeClickEventHandler
->addEvent( rEvent
, rShape
);
685 class ClickEventRegistrationFunctor
688 ClickEventRegistrationFunctor( EventMultiplexer
& rMultiplexer
,
690 bool bAdvanceOnClick
)
691 : mrMultiplexer( rMultiplexer
),
693 mbAdvanceOnClick( bAdvanceOnClick
) {}
695 void operator()( const std::shared_ptr
<ClickEventHandler
>& rHandler
)const
697 // register the handler on _two_ sources: we want the
698 // nextEffect events, e.g. space bar, to trigger clicks, as well!
699 mrMultiplexer
.addClickHandler( rHandler
, mnPrio
);
700 mrMultiplexer
.addNextEffectHandler( rHandler
, mnPrio
);
702 // forward advance-on-click state to newly
703 // generated handler (that's the only reason why
704 // we're called here)
705 rHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
709 EventMultiplexer
& mrMultiplexer
;
711 bool const mbAdvanceOnClick
;
715 void UserEventQueue::registerNextEffectEvent( const EventSharedPtr
& rEvent
)
717 // TODO: better name may be mpNextEffectEventHandler? then we have
718 // next effect (=> waiting to be started)
719 // skip effect (skipping the currently running one)
720 // rewind effect (rewinding back running one and waiting (again)
722 registerEvent( mpClickEventHandler
,
724 ClickEventRegistrationFunctor( mrMultiplexer
,
725 0.0 /* default prio */,
726 mbAdvanceOnClick
) );
729 void UserEventQueue::registerSkipEffectEvent(
730 EventSharedPtr
const & pEvent
,
731 const bool bSkipTriggersNextEffect
)
733 if(!mpSkipEffectEventHandler
)
735 mpSkipEffectEventHandler
.reset(
736 new SkipEffectEventHandler( mrEventQueue
, mrMultiplexer
) );
737 // register the handler on _two_ sources: we want the
738 // nextEffect events, e.g. space bar, to trigger clicks, as well!
739 mrMultiplexer
.addClickHandler( mpSkipEffectEventHandler
,
740 -1.0 /* prio below default */ );
741 mrMultiplexer
.addNextEffectHandler( mpSkipEffectEventHandler
,
742 -1.0 /* prio below default */ );
743 // forward advance-on-click state to newly
744 // generated handler (that's the only reason why
745 // we're called here)
746 mpSkipEffectEventHandler
->setAdvanceOnClick( mbAdvanceOnClick
);
748 mpSkipEffectEventHandler
->setSkipTriggersNextEffect(bSkipTriggersNextEffect
);
749 mpSkipEffectEventHandler
->addEvent( pEvent
);
752 void UserEventQueue::registerShapeDoubleClickEvent(
753 const EventSharedPtr
& rEvent
,
754 const ShapeSharedPtr
& rShape
)
758 "UserEventQueue::registerShapeDoubleClickEvent(): Invalid event" );
760 if( !mpShapeDoubleClickEventHandler
)
763 mpShapeDoubleClickEventHandler
.reset(
764 new ShapeClickEventHandler(mrCursorManager
,
767 // register handler on EventMultiplexer
768 mrMultiplexer
.addDoubleClickHandler( mpShapeDoubleClickEventHandler
,
770 mrMultiplexer
.addMouseMoveHandler( mpShapeDoubleClickEventHandler
,
774 mpShapeDoubleClickEventHandler
->addEvent( rEvent
, rShape
);
777 void UserEventQueue::registerMouseEnterEvent( const EventSharedPtr
& rEvent
,
778 const ShapeSharedPtr
& rShape
)
780 registerEvent( mpMouseEnterHandler
,
783 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
784 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
787 void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr
& rEvent
,
788 const ShapeSharedPtr
& rShape
)
790 registerEvent( mpMouseLeaveHandler
,
793 [this]( const MouseEventHandlerSharedPtr
& rHandler
)
794 { return this->mrMultiplexer
.addMouseMoveHandler( rHandler
, 0.0 ); } );
797 void UserEventQueue::callSkipEffectEventHandler()
799 ::std::shared_ptr
<SkipEffectEventHandler
> pHandler (
800 ::std::dynamic_pointer_cast
<SkipEffectEventHandler
>(mpSkipEffectEventHandler
));
802 pHandler
->skipEffect();
805 } // namespace internal
806 } // namespace presentation
808 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */