1 Updated slideshow effect rewind feature from CWS slideshow1
3 From: Thorsten Behrens <thb@openoffice.org>
8 offapi/com/sun/star/presentation/XSlideShow.idl | 37 ++
9 .../sun/star/presentation/XSlideShowController.idl | 8
10 .../sun/star/presentation/XSlideShowListener.idl | 6
11 sd/source/ui/slideshow/slideshowimpl.cxx | 120 +++++-
12 sd/source/ui/slideshow/slideshowimpl.hxx | 8
13 sdext/source/presenter/PresenterController.cxx | 11 -
14 .../presenter/PresenterCurrentSlideObserver.cxx | 4
15 .../presenter/PresenterCurrentSlideObserver.hxx | 2
16 sdext/source/presenter/description.xml | 4
17 .../source/engine/animationnodes/basenode.hxx | 6
18 .../animationnodes/sequentialtimecontainer.cxx | 6
19 slideshow/source/engine/effectrewinder.cxx | 432 ++++++++++++++++++++
20 slideshow/source/engine/effectrewinder.hxx | 185 +++++++++
21 slideshow/source/engine/eventmultiplexer.cxx | 10
22 slideshow/source/engine/eventqueue.cxx | 47 ++
23 slideshow/source/engine/makefile.mk | 1
24 slideshow/source/engine/screenupdater.cxx | 83 ++++
25 slideshow/source/engine/shapes/drawshape.cxx | 6
26 slideshow/source/engine/slide/layermanager.cxx | 105 ++---
27 slideshow/source/engine/slide/layermanager.hxx | 35 --
28 slideshow/source/engine/slideshowimpl.cxx | 270 ++++++++++---
29 slideshow/source/engine/usereventqueue.cxx | 54 ++-
30 slideshow/source/inc/eventqueue.hxx | 8
31 slideshow/source/inc/screenupdater.hxx | 21 +
32 slideshow/source/inc/usereventqueue.hxx | 18 +
33 25 files changed, 1303 insertions(+), 184 deletions(-)
34 create mode 100644 slideshow/source/engine/effectrewinder.cxx
35 create mode 100644 slideshow/source/engine/effectrewinder.hxx
38 diff --git offapi/com/sun/star/presentation/XSlideShow.idl offapi/com/sun/star/presentation/XSlideShow.idl
39 index 4f4f021..272bf9d 100644
40 --- offapi/com/sun/star/presentation/XSlideShow.idl
41 +++ offapi/com/sun/star/presentation/XSlideShow.idl
42 @@ -93,6 +93,25 @@ interface XSlideShow : ::com::sun::star::uno::XInterface
46 + /** Undo the last effect in the main sequence of the slideshow.<p>
48 + The current slide is displayed as if the last user-triggered effect
49 + has never been triggered. If there is no previous effect on the
50 + current slide then slideEnded(true) is called at the registered
51 + XSlideShowListener objects, which can then trigger a change to the
52 + previous slide. Note that this command is executed asynchronously.
53 + Multiple calls to update() may be necessary to complete its execution.
54 + If there is currently no slideshow running, this method does
57 + @return <TRUE/>, if the previous effect was successfully
58 + triggered. This method returns <FALSE/>, if there is no show
59 + running, the first effect on the first slide was not yet
60 + triggered, or the implementation failed to trigger the previous
63 + boolean previousEffect();
65 /** Start a shape-intrinsic animation or activity.<p>
67 This method starts an animation or activity intrinsic to the
68 @@ -145,8 +164,24 @@ interface XSlideShow : ::com::sun::star::uno::XInterface
69 a different slide, this will still work but will not have any performance
72 + <li>name: SkipAllMainSequenceEffects, value: boolean.
73 + When <TRUE/> then all main sequence effects on the new slide
74 + are triggered. This is typically used when going back one
75 + effect leads to the previous slide. On that slide all
76 + effects have to be shown in order to continue the backward
78 + When <FALSE/>, the default, then no main sequence effect is
81 + <li>name: SkipSlideTransition, value: boolean.
82 + When <TRUE/> then the slide transition animation, if there
83 + is any, is not displayed. This is typically used when going
84 + back one effect leads to the previous slide. Typically used
85 + together with SkipAllMainSequenceEffects also being <TRUE/>.
86 + When <FALSE/>, the default, then the slide transition
87 + effect, if it exists, is played.
93 [in] ::com::sun::star::drawing::XDrawPage xSlide,
94 diff --git offapi/com/sun/star/presentation/XSlideShowController.idl offapi/com/sun/star/presentation/XSlideShowController.idl
95 index 46989ec..d203a74 100644
96 --- offapi/com/sun/star/presentation/XSlideShowController.idl
97 +++ offapi/com/sun/star/presentation/XSlideShowController.idl
98 @@ -121,6 +121,14 @@ interface XSlideShowController
100 //-------------------------------------------------------------------------
102 + /** undo the last effects that where triggered by a generic trigger.
103 + <p>If there is no previous effect that can be undone then the
104 + previous slide will be displayed.
106 + void gotoPreviousEffect();
108 + //-------------------------------------------------------------------------
110 /** goto and display first slide */
111 void gotoFirstSlide();
113 diff --git offapi/com/sun/star/presentation/XSlideShowListener.idl offapi/com/sun/star/presentation/XSlideShowListener.idl
114 index 74767f0..6266042 100644
115 --- offapi/com/sun/star/presentation/XSlideShowListener.idl
116 +++ offapi/com/sun/star/presentation/XSlideShowListener.idl
117 @@ -65,8 +65,12 @@ interface XSlideShowListener : ::com::sun::star::animations::XAnimationListener
118 /** Notify that the current slide has ended,
119 e.g. the user has clicked on the slide.
120 Calling displaySlide() twice will not issue this event.
122 + For the default order (forward) this flag is <FALSE/>.
123 + When the main sequence was traversed in reverse order then this
127 + void slideEnded( [in] boolean reverse );
129 /** Notifies that a hyperlink has been clicked.
130 @param hyperLink hyperlink URL
131 diff --git sd/source/ui/slideshow/slideshowimpl.cxx sd/source/ui/slideshow/slideshowimpl.cxx
132 index de96eac..c8d46f4 100644
133 --- sd/source/ui/slideshow/slideshowimpl.cxx
134 +++ sd/source/ui/slideshow/slideshowimpl.cxx
135 @@ -184,7 +184,9 @@ public:
137 bool previousSlide();
139 - void displayCurrentSlide( const Reference< XSlideShow >& xShow );
140 + void displayCurrentSlide(
141 + const Reference< XSlideShow >& xShow,
142 + const bool bSkipAllMainSequenceEffects);
144 sal_Int32 getNextSlideIndex() const;
145 sal_Int32 getPreviousSlideIndex() const;
146 @@ -481,29 +483,58 @@ bool AnimationSlideController::previousSlide()
147 return jumpToSlideIndex( getPreviousSlideIndex() );
150 -void AnimationSlideController::displayCurrentSlide( const Reference< XSlideShow >& xShow )
151 +void AnimationSlideController::displayCurrentSlide(
152 + const Reference< XSlideShow >& xShow,
153 + const bool bSkipAllMainSequenceEffects)
155 const sal_Int32 nCurrentSlideNumber = getCurrentSlideNumber();
157 if( xShow.is() && (nCurrentSlideNumber != -1 ) )
159 - Sequence< PropertyValue > aProperties;
160 Reference< XDrawPage > xSlide;
161 Reference< XAnimationNode > xAnimNode;
163 + ::std::vector<PropertyValue> aProperties;
165 const sal_Int32 nNextSlideNumber = getNextSlideNumber();
166 if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode ) )
168 Sequence< Any > aValue(2);
169 aValue[0] <<= xSlide;
170 aValue[1] <<= xAnimNode;
171 - aProperties.realloc(1);
172 - aProperties[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "Prefetch" ) );
173 - aProperties[0].Value <<= aValue;
174 + aProperties.push_back(
176 + OUString( RTL_CONSTASCII_USTRINGPARAM( "Prefetch" ) ),
179 + PropertyState_DIRECT_VALUE));
181 + if (bSkipAllMainSequenceEffects)
183 + // Add one property that prevents the slide transition from being
184 + // shown (to speed up the transition to the previous slide) and
185 + // one to show all main sequence effects so that the user can
186 + // continue to undo effects.
187 + aProperties.push_back(
189 + OUString( RTL_CONSTASCII_USTRINGPARAM("SkipAllMainSequenceEffects")),
192 + PropertyState_DIRECT_VALUE));
193 + aProperties.push_back(
195 + OUString( RTL_CONSTASCII_USTRINGPARAM("SkipSlideTransition")),
198 + PropertyState_DIRECT_VALUE));
201 + // Convert vector into uno Sequence.
202 + Sequence< PropertyValue > aPropertySequence (aProperties.size());
203 + for (int nIndex=0,nCount=aProperties.size();nIndex<nCount; ++nIndex)
204 + aPropertySequence[nIndex] = aProperties[nIndex];
206 if( getSlideAPI( nCurrentSlideNumber, xSlide, xAnimNode ) )
207 - xShow->displaySlide( xSlide, xAnimNode, aProperties );
208 + xShow->displaySlide( xSlide, xAnimNode, aPropertySequence );
212 @@ -1251,9 +1282,12 @@ void SAL_CALL SlideshowImpl::removeSlideShowListener( const Reference< XSlideSho
214 // ---------------------------------------------------------
216 -void SlideshowImpl::slideEnded()
217 +void SlideshowImpl::slideEnded(const bool bReverse)
221 + gotoPreviousSlide(true);
226 // ---------------------------------------------------------
227 @@ -1403,14 +1437,14 @@ void SlideshowImpl::registerShapeEvents( Reference< XShapes >& xShapes ) throw(
229 // ---------------------------------------------------------
231 -void SlideshowImpl::displayCurrentSlide()
232 +void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects)
237 if( mpSlideController.get() && mxShow.is() )
239 - mpSlideController->displayCurrentSlide( mxShow );
240 + mpSlideController->displayCurrentSlide( mxShow, bSkipAllMainSequenceEffects );
241 registerShapeEvents(mpSlideController->getCurrentSlideNumber());
244 @@ -1984,11 +2018,17 @@ bool SlideshowImpl::keyInput(const KeyEvent& rKEvt)
248 + if(rKEvt.GetKeyCode().IsMod2())
250 + gotoPreviousSlide();
253 + // warning, fall through!
258 - gotoPreviousSlide();
259 + gotoPreviousEffect();
263 @@ -3081,6 +3121,30 @@ void SAL_CALL SlideshowImpl::gotoNextEffect( ) throw (RuntimeException)
265 // --------------------------------------------------------------------
267 +void SAL_CALL SlideshowImpl::gotoPreviousEffect( ) throw (RuntimeException)
269 + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
271 + if( mxShow.is() && mpSlideController.get() && mpShowWindow )
276 + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
277 + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
279 + mpShowWindow->RestartShow();
283 + mxShow->previousEffect();
289 +// --------------------------------------------------------------------
291 void SAL_CALL SlideshowImpl::gotoFirstSlide( ) throw (RuntimeException)
293 ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
294 @@ -3201,6 +3265,11 @@ void SAL_CALL SlideshowImpl::gotoNextSlide( ) throw (RuntimeException)
296 void SAL_CALL SlideshowImpl::gotoPreviousSlide( ) throw (RuntimeException)
298 + gotoPreviousSlide(false);
301 +void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects)
303 ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
305 if( mxShow.is() && mpSlideController.get() ) try
306 @@ -3221,8 +3290,22 @@ void SAL_CALL SlideshowImpl::gotoPreviousSlide( ) throw (RuntimeException)
310 - if( mpSlideController->previousSlide() )
311 - displayCurrentSlide();
312 + if( mpSlideController->previousSlide())
313 + displayCurrentSlide(bSkipAllMainSequenceEffects);
314 + else if (bSkipAllMainSequenceEffects)
316 + // We could not go to the previous slide (probably because
317 + // the current slide is already the first one). We still
318 + // have to call displayCurrentSlide because the calling
319 + // slideshow can not determine whether there is a previous
320 + // slide or not and has already prepared for a slide change.
321 + // This slide change has to be completed now, even when
322 + // changing to the same slide.
323 + // Note that in this special case we do NOT pass
324 + // bSkipAllMainSequenceEffects because we display the same
325 + // slide as before and do not want to show all its effects.
326 + displayCurrentSlide(false);
330 catch( Exception& e )
331 @@ -3673,19 +3756,20 @@ void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded( ) throw (::com::sun
333 // ---------------------------------------------------------
335 -void SlideShowListenerProxy::slideEnded() throw (RuntimeException)
336 +void SlideShowListenerProxy::slideEnded(sal_Bool bReverse) throw (RuntimeException)
339 ::osl::MutexGuard aGuard( m_aMutex );
341 if( maListeners.getLength() >= 0 )
342 - maListeners.forEach<XSlideShowListener>( boost::mem_fn( &XSlideShowListener::slideEnded ) );
343 + maListeners.forEach<XSlideShowListener>(
344 + boost::bind( &XSlideShowListener::slideEnded, _1, bReverse) );
348 ::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
349 if( mxController.is() )
350 - mxController->slideEnded();
351 + mxController->slideEnded(bReverse);
355 diff --git sd/source/ui/slideshow/slideshowimpl.hxx sd/source/ui/slideshow/slideshowimpl.hxx
356 index c297d4d..0219f11 100644
357 --- sd/source/ui/slideshow/slideshowimpl.hxx
358 +++ sd/source/ui/slideshow/slideshowimpl.hxx
359 @@ -166,7 +166,7 @@ public:
360 virtual void SAL_CALL slideTransitionStarted() throw (css::uno::RuntimeException);
361 virtual void SAL_CALL slideTransitionEnded() throw (css::uno::RuntimeException);
362 virtual void SAL_CALL slideAnimationsEnded() throw (css::uno::RuntimeException);
363 - virtual void SAL_CALL slideEnded() throw (css::uno::RuntimeException);
364 + virtual void SAL_CALL slideEnded(sal_Bool bReverse) throw (css::uno::RuntimeException);
365 virtual void SAL_CALL hyperLinkClicked(const ::rtl::OUString & hyperLink) throw (css::uno::RuntimeException);
367 // css::lang::XEventListener:
368 @@ -210,6 +210,7 @@ public:
369 virtual void SAL_CALL addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) throw (css::uno::RuntimeException);
370 virtual void SAL_CALL removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) throw (css::uno::RuntimeException);
371 virtual void SAL_CALL gotoNextEffect( ) throw (css::uno::RuntimeException);
372 + virtual void SAL_CALL gotoPreviousEffect( ) throw (css::uno::RuntimeException);
373 virtual void SAL_CALL gotoFirstSlide( ) throw (css::uno::RuntimeException);
374 virtual void SAL_CALL gotoNextSlide( ) throw (css::uno::RuntimeException);
375 virtual void SAL_CALL gotoPreviousSlide( ) throw (css::uno::RuntimeException);
376 @@ -239,7 +240,7 @@ public:
377 virtual ::sal_Bool SAL_CALL hasElements( ) throw (::com::sun::star::uno::RuntimeException);
379 // will be called from the SlideShowListenerProxy when this event is fired from the XSlideShow
381 + void slideEnded(const bool bReverse);
382 void hyperLinkClicked(const ::rtl::OUString & hyperLink) throw (css::uno::RuntimeException);
383 void click(const css::uno::Reference< css::drawing::XShape > & xShape, const css::awt::MouseEvent & aOriginalEvent);
385 @@ -280,7 +281,7 @@ private:
387 void createSlideList( bool bAll, bool bStartWithActualSlide, const String& rPresSlide );
389 - void displayCurrentSlide();
390 + void displayCurrentSlide (const bool bSkipAllMainSequenceEffects = false);
392 void displaySlideNumber( sal_Int32 nSlide );
393 void displaySlideIndex( sal_Int32 nIndex );
394 @@ -332,6 +333,7 @@ private:
395 css::uno::Reference< css::presentation::XSlideShow > createSlideShow() const;
397 void setAutoSaveState( bool bOn );
398 + void gotoPreviousSlide (const bool bSkipAllMainSequenceEffects);
400 css::uno::Reference< css::presentation::XSlideShow > mxShow;
401 comphelper::ImplementationReference< ::sd::SlideShowView, css::presentation::XSlideShowView > mxView;
402 diff --git slideshow/source/engine/animationnodes/basenode.hxx slideshow/source/engine/animationnodes/basenode.hxx
403 index a25f6cb..dbf2f13 100644
404 --- slideshow/source/engine/animationnodes/basenode.hxx
405 +++ slideshow/source/engine/animationnodes/basenode.hxx
406 @@ -136,7 +136,9 @@ public:
407 const AnimationNodeSharedPtr& rNotifee );
409 virtual void notifyDeactivating( const AnimationNodeSharedPtr& rNotifier );
412 + bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; }
415 void scheduleDeactivationEvent( EventSharedPtr const& pEvent =
417 @@ -144,8 +146,6 @@ protected:
418 SlideShowContext const& getContext() const { return maContext; }
419 ::boost::shared_ptr<BaseNode> const& getSelf() const { return mpSelf; }
421 - bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; }
423 bool checkValidNode() const {
424 ENSURE_OR_THROW( mpSelf, "no self ptr set!" );
425 bool const bRet = (meCurrState != INVALID);
426 diff --git slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx
427 index de63321..ab0c492 100644
428 --- slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx
429 +++ slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx
430 @@ -88,7 +88,7 @@ void SequentialTimeContainer::skipEffect(
431 if (isChildNode(pChildNode)) {
432 // empty all events ignoring timings => until next effect
433 getContext().mrEventQueue.forceEmpty();
434 - getContext().mrEventQueue.addEventForNextRound(
435 + getContext().mrEventQueue.addEvent(
436 makeEvent( boost::bind(&AnimationNode::deactivate, pChildNode) ) );
439 @@ -125,7 +125,8 @@ bool SequentialTimeContainer::resolveChild(
441 // deactivate child node when skip event occurs:
442 getContext().mrUserEventQueue.registerSkipEffectEvent(
443 - mpCurrentSkipEvent );
444 + mpCurrentSkipEvent,
445 + mnFinishedChildren+1<maChildren.size());
446 // rewind to previous child:
447 getContext().mrUserEventQueue.registerRewindEffectEvent(
448 mpCurrentRewindEvent );
449 @@ -136,6 +137,7 @@ bool SequentialTimeContainer::resolveChild(
450 void SequentialTimeContainer::notifyDeactivating(
451 AnimationNodeSharedPtr const& rNotifier )
453 + OSL_TRACE(" SequentialTimeContainer::notifyDeactivating\r");
454 if (notifyDeactivatedChild( rNotifier ))
457 diff --git slideshow/source/engine/effectrewinder.cxx slideshow/source/engine/effectrewinder.cxx
459 index 0000000..b7db667
461 +++ slideshow/source/engine/effectrewinder.cxx
463 +/*************************************************************************
465 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
467 + * Copyright 2008 by Sun Microsystems, Inc.
469 + * OpenOffice.org - a multi-platform office productivity suite
471 + * $RCSfile: slideshowimpl.cxx,v $
472 + * $Revision: 1.10 $
474 + * This file is part of OpenOffice.org.
476 + * OpenOffice.org is free software: you can redistribute it and/or modify
477 + * it under the terms of the GNU Lesser General Public License version 3
478 + * only, as published by the Free Software Foundation.
480 + * OpenOffice.org is distributed in the hope that it will be useful,
481 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
482 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
483 + * GNU Lesser General Public License version 3 for more details
484 + * (a copy is included in the LICENSE file that accompanied this code).
486 + * You should have received a copy of the GNU Lesser General Public License
487 + * version 3 along with OpenOffice.org. If not, see
488 + * <http://www.openoffice.org/license.html>
489 + * for a copy of the LGPLv3 License.
491 + ************************************************************************/
493 +#include "precompiled_slideshow.hxx"
495 +#include "effectrewinder.hxx"
496 +#include "eventqueue.hxx"
497 +#include "usereventqueue.hxx"
498 +#include "mouseeventhandler.hxx"
499 +#include "animationnodes/basecontainernode.hxx"
500 +#include "delayevent.hxx"
502 +#include <com/sun/star/awt/MouseEvent.hpp>
503 +#include <com/sun/star/animations/Event.hpp>
504 +#include <com/sun/star/animations/EventTrigger.hpp>
505 +#include <com/sun/star/container/XEnumerationAccess.hpp>
506 +#include <boost/function.hpp>
507 +#include <boost/bind.hpp>
508 +#include <boost/enable_shared_from_this.hpp>
510 +using ::com::sun::star::uno::Reference;
511 +using namespace ::com::sun::star;
513 +namespace slideshow { namespace internal {
518 +class RewinderEventHandler : public EventHandler
521 + typedef ::boost::function<bool(void)> Action;
522 + RewinderEventHandler (const Action& rAction) : maAction(rAction) {}
523 + virtual ~RewinderEventHandler (void) {}
525 + const Action maAction;
526 + virtual bool handleEvent (void) { return maAction(); }
531 +class RewinderAnimationEventHandler : public AnimationEventHandler
534 + typedef ::boost::function<bool(const AnimationNodeSharedPtr& rpNode)> Action;
535 + RewinderAnimationEventHandler (const Action& rAction) : maAction(rAction) {}
536 + virtual ~RewinderAnimationEventHandler (void) {}
538 + const Action maAction;
539 + virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode)
540 + { return maAction(rpNode); }
545 +} // end of anonymous namespace
548 +//----- EffectRewinder --------------------------------------------------------------
550 +EffectRewinder::EffectRewinder (
551 + EventMultiplexer& rEventMultiplexer,
552 + EventQueue& rEventQueue,
553 + UserEventQueue& rUserEventQueue)
554 + : mrEventMultiplexer(rEventMultiplexer),
555 + mrEventQueue(rEventQueue),
556 + mrUserEventQueue(rUserEventQueue),
557 + mpSlideStartHandler(),
558 + mpSlideEndHandler(),
559 + mpAnimationStartHandler(),
560 + mnMainSequenceEffectCount(0),
561 + mpAsynchronousRewindEvent(),
562 + mxCurrentAnimationRootNode(),
563 + mbNonUserTriggeredMainSequenceEffectSeen(false)
571 +void EffectRewinder::initialize (void)
573 + // Add some event handlers so that we are informed when
574 + // a) an animation is started (we then check whether that belongs to a
575 + // main sequence effect and if so, increase the respective counter),
576 + // b,c) a slide was started or ended (in which case the effect counter
579 + mpAnimationStartHandler.reset(
580 + new RewinderAnimationEventHandler(
581 + ::boost::bind(&EffectRewinder::notifyAnimationStart, this, _1)));
582 + mrEventMultiplexer.addAnimationStartHandler(mpAnimationStartHandler);
584 + mpSlideStartHandler.reset(
585 + new RewinderEventHandler(
586 + ::boost::bind(&EffectRewinder::resetEffectCount, this)));
587 + mrEventMultiplexer.addSlideStartHandler(mpSlideStartHandler);
589 + mpSlideEndHandler.reset(
590 + new RewinderEventHandler(
591 + ::boost::bind(&EffectRewinder::resetEffectCount, this)));
592 + mrEventMultiplexer.addSlideEndHandler(mpSlideEndHandler);
598 +EffectRewinder::~EffectRewinder (void)
606 +void EffectRewinder::dispose (void)
608 + if (mpAsynchronousRewindEvent)
610 + mpAsynchronousRewindEvent->dispose();
611 + mpAsynchronousRewindEvent.reset();
614 + if (mpAnimationStartHandler)
616 + mrEventMultiplexer.removeAnimationStartHandler(mpAnimationStartHandler);
617 + mpAnimationStartHandler.reset();
620 + if (mpSlideStartHandler)
622 + mrEventMultiplexer.removeSlideStartHandler(mpSlideStartHandler);
623 + mpSlideStartHandler.reset();
626 + if (mpSlideEndHandler)
628 + mrEventMultiplexer.removeSlideEndHandler(mpSlideEndHandler);
629 + mpSlideEndHandler.reset();
636 +void EffectRewinder::setRootAnimationNode (
637 + const uno::Reference<animations::XAnimationNode>& xRootNode)
639 + mxCurrentAnimationRootNode = xRootNode;
645 +bool EffectRewinder::rewind (
646 + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock,
647 + const ::boost::function<void(void)>& rSlideRewindFunctor,
648 + const ::boost::function<void(void)>& rPreviousSlideFunctor)
650 + mpPaintLock = rpPaintLock;
652 + // Do not allow nested rewinds.
653 + if (mpAsynchronousRewindEvent)
655 + OSL_ASSERT( ! mpAsynchronousRewindEvent);
659 + // Abort (and skip over the rest of) any currently active animation.
660 + mrUserEventQueue.callSkipEffectEventHandler();
661 + mrEventQueue.forceEmpty();
663 + const int nSkipCount (mnMainSequenceEffectCount - 1);
664 + if (nSkipCount < 0)
666 + if ( ! rPreviousSlideFunctor)
668 + OSL_ASSERT(rPreviousSlideFunctor);
672 + // No main sequence effects to rewind on the current slide.
673 + // Go back to the previous slide.
674 + mpAsynchronousRewindEvent = makeEvent(
676 + &EffectRewinder::asynchronousRewindToPreviousSlide,
678 + rPreviousSlideFunctor));
682 + // The actual rewinding is done asynchronously so that we can safely
683 + // call other methods.
684 + mpAsynchronousRewindEvent = makeEvent(
686 + &EffectRewinder::asynchronousRewind,
690 + rSlideRewindFunctor));
693 + if (mpAsynchronousRewindEvent)
694 + mrEventQueue.addEvent(mpAsynchronousRewindEvent);
696 + return mpAsynchronousRewindEvent.get()!=NULL;
702 +void EffectRewinder::skipAllMainSequenceEffects (void)
704 + // Do not allow nested rewinds.
705 + if (mpAsynchronousRewindEvent)
707 + OSL_ASSERT(!mpAsynchronousRewindEvent);
711 + const int nTotalMainSequenceEffectCount (countMainSequenceEffects());
712 + mpAsynchronousRewindEvent = makeEvent(
714 + &EffectRewinder::asynchronousRewind,
716 + nTotalMainSequenceEffectCount,
718 + ::boost::function<void(void)>()));
719 + mrEventQueue.addEvent(mpAsynchronousRewindEvent);
725 +sal_Int32 EffectRewinder::countMainSequenceEffects (void)
727 + // Determine the number of main sequence effects.
728 + sal_Int32 nMainSequenceNodeCount (0);
730 + ::std::queue<uno::Reference<animations::XAnimationNode> > aNodeQueue;
731 + aNodeQueue.push(mxCurrentAnimationRootNode);
732 + while ( ! aNodeQueue.empty())
734 + const uno::Reference<animations::XAnimationNode> xNode (aNodeQueue.front());
737 + // Does the current node belong to the main sequence?
740 + animations::Event aEvent;
741 + if (xNode->getBegin() >>= aEvent)
742 + if (aEvent.Trigger == animations::EventTrigger::ON_NEXT)
743 + ++nMainSequenceNodeCount;
746 + // If the current node is a container then prepare its children for investigation.
747 + uno::Reference<container::XEnumerationAccess> xEnumerationAccess (xNode, uno::UNO_QUERY);
748 + if (xEnumerationAccess.is())
750 + uno::Reference<container::XEnumeration> xEnumeration (
751 + xEnumerationAccess->createEnumeration());
752 + if (xEnumeration.is())
753 + while (xEnumeration->hasMoreElements())
756 + uno::Reference<animations::XAnimationNode>(
757 + xEnumeration->nextElement(), uno::UNO_QUERY));
762 + return nMainSequenceNodeCount;
764 + // // Skip all main sequence nodes.
765 + // SkipSomeMainSequenceEffects(nMainSequenceNodeCount);
771 +void EffectRewinder::skipSomeMainSequenceEffects (sal_Int32 nSkipCount)
773 + while (--nSkipCount >= 0)
774 + skipSingleMainSequenceEffects();
780 +void EffectRewinder::skipSingleMainSequenceEffects (void)
782 + // This basically just starts the next effect and then skips over its
784 + mrEventMultiplexer.notifyNextEffect();
785 + mrEventQueue.forceEmpty();
786 + mrUserEventQueue.callSkipEffectEventHandler();
787 + mrEventQueue.forceEmpty();
793 +bool EffectRewinder::resetEffectCount (void)
795 + mnMainSequenceEffectCount = 0;
802 +bool EffectRewinder::notifyAnimationStart (const AnimationNodeSharedPtr& rpNode)
804 + // This notification is only relevant for us when the rpNode belongs to
805 + // the main sequence.
806 + BaseNodeSharedPtr pBaseNode (::boost::dynamic_pointer_cast<BaseNode>(rpNode));
810 + BaseContainerNodeSharedPtr pParent (pBaseNode->getParentNode());
811 + if ( ! (pParent && pParent->isMainSequenceRootNode()))
814 + // This notification is only relevant for us when the effect is user
816 + bool bIsUserTriggered (false);
818 + Reference<animations::XAnimationNode> xNode (rpNode->getXAnimationNode());
821 + animations::Event aEvent;
822 + if ((xNode->getBegin() >>= aEvent))
823 + bIsUserTriggered = (aEvent.Trigger == animations::EventTrigger::ON_NEXT);
826 + if (bIsUserTriggered)
827 + ++mnMainSequenceEffectCount;
829 + mbNonUserTriggeredMainSequenceEffectSeen = true;
837 +void EffectRewinder::asynchronousRewind (
838 + sal_Int32 nEffectCount,
839 + const bool bRedisplayCurrentSlide,
840 + const boost::function<void(void)>& rSlideRewindFunctor)
842 + OSL_ASSERT(mpAsynchronousRewindEvent);
844 + if (bRedisplayCurrentSlide)
846 + mpPaintLock->Activate();
847 + // Re-display the current slide.
848 + if (rSlideRewindFunctor)
849 + rSlideRewindFunctor();
850 + mpAsynchronousRewindEvent = makeEvent(
852 + &EffectRewinder::asynchronousRewind,
856 + rSlideRewindFunctor));
857 + mrEventQueue.addEventForNextRound(mpAsynchronousRewindEvent);
861 + // Process initial events and skip any animations that are started
862 + // when the slide is shown.
863 + mbNonUserTriggeredMainSequenceEffectSeen = false;
864 + mrEventQueue.forceEmpty();
865 + if (mbNonUserTriggeredMainSequenceEffectSeen)
867 + mrUserEventQueue.callSkipEffectEventHandler();
868 + mrEventQueue.forceEmpty();
871 + while (--nEffectCount >= 0)
872 + skipSingleMainSequenceEffects();
874 + mpAsynchronousRewindEvent.reset();
875 + mpPaintLock.reset();
882 +void EffectRewinder::asynchronousRewindToPreviousSlide (
883 + const ::boost::function<void(void)>& rSlideRewindFunctor)
885 + OSL_ASSERT(mpAsynchronousRewindEvent);
887 + mpAsynchronousRewindEvent.reset();
888 + rSlideRewindFunctor();
894 +} } // end of namespace ::slideshow::internal
895 diff --git slideshow/source/engine/effectrewinder.hxx slideshow/source/engine/effectrewinder.hxx
897 index 0000000..ee49127
899 +++ slideshow/source/engine/effectrewinder.hxx
901 +/*************************************************************************
903 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
905 + * Copyright 2008 by Sun Microsystems, Inc.
907 + * OpenOffice.org - a multi-platform office productivity suite
909 + * $RCSfile: slideshowimpl.cxx,v $
910 + * $Revision: 1.10 $
912 + * This file is part of OpenOffice.org.
914 + * OpenOffice.org is free software: you can redistribute it and/or modify
915 + * it under the terms of the GNU Lesser General Public License version 3
916 + * only, as published by the Free Software Foundation.
918 + * OpenOffice.org is distributed in the hope that it will be useful,
919 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
920 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
921 + * GNU Lesser General Public License version 3 for more details
922 + * (a copy is included in the LICENSE file that accompanied this code).
924 + * You should have received a copy of the GNU Lesser General Public License
925 + * version 3 along with OpenOffice.org. If not, see
926 + * <http://www.openoffice.org/license.html>
927 + * for a copy of the LGPLv3 License.
929 + ************************************************************************/
931 +#ifndef INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX
932 +#define INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX
934 +#include "animationnode.hxx"
935 +#include "eventhandler.hxx"
936 +#include "animationeventhandler.hxx"
937 +#include "event.hxx"
938 +#include "screenupdater.hxx"
940 +#include <com/sun/star/presentation/XSlideShow.hpp>
941 +#include <boost/scoped_ptr.hpp>
942 +#include <boost/function.hpp>
945 +namespace css = ::com::sun::star;
947 +namespace slideshow { namespace internal {
949 +class EventMultiplexer;
951 +class UserEventQueue;
953 +/** Rewind single effects of the main effect sequence. A rewind is
954 + initiated by calling the Rewind() method. Part of the processing is
955 + done asynchronously. Multiple EventQueue::update() calls may be
956 + necessary to finish a rewind.
958 + Remember to call SetRootAnimationNode() when switching to a different
959 + slide so that the EffectRewinder can determine the number of main
962 +class EffectRewinder
966 + EventMultiplexer& rEventMultiplexer,
967 + EventQueue& rEventQueue,
968 + UserEventQueue& rUserEventQueue);
969 + ~EffectRewinder (void);
971 + /** Call Dispose() before the ownder of an EffectRewinder object dies so
972 + that the EffectRewinder can release all references to the owner.
975 + void dispose (void);
977 + /** Store the root node of the animation tree. It is used in
978 + CountMainSequenceEffects() to count the number of main sequence
979 + effects (or effect groups.)
981 + void setRootAnimationNode (
982 + const css::uno::Reference<css::animations::XAnimationNode>& xRootNode);
984 + /** Rewind one effect of the main effect sequence. When the current
985 + slide has not effects or no main sequence effect has yet been played
986 + then switch to the previous slide and replay all of its main
988 + The caller has to pass two functors that redisplay the current slide
989 + or switch to the previous slide so that it does not have to expose
990 + its internals to us. Only one of the two functors is called.
992 + This paint lock is released after the whole asynchronous
993 + procoess of rewinding the current effect is completed. It
994 + prevents intermediate repaints that would show partial replay
996 + @param rSlideRewindFunctor
997 + This functor is called when the current slide is to be
998 + redisplayed. When it is called then the other functor is not
1000 + @param rPreviousSlideFunctor
1001 + This functor is called to switch to the previous slide. When it
1002 + is called then the other functor is not called.
1005 + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock,
1006 + const ::boost::function<void(void)>& rSlideRewindFunctor,
1007 + const ::boost::function<void(void)>& rPreviousSlideFunctor);
1009 + /** Call this method after gotoPreviousEffect() triggered a slide change
1010 + to the previous slide.
1012 + void skipAllMainSequenceEffects (void);
1015 + EventMultiplexer& mrEventMultiplexer;
1016 + EventQueue& mrEventQueue;
1017 + UserEventQueue& mrUserEventQueue;
1019 + EventHandlerSharedPtr mpSlideStartHandler;
1020 + EventHandlerSharedPtr mpSlideEndHandler;
1021 + AnimationEventHandlerSharedPtr mpAnimationStartHandler;
1023 + /** The number off main sequence effects so far.
1025 + sal_Int32 mnMainSequenceEffectCount;
1027 + /** This is the currently scheduled event that executes the asynchronous
1028 + part of the effect rewinding. It is also used as flag that prevents
1031 + EventSharedPtr mpAsynchronousRewindEvent;
1033 + css::uno::Reference<css::animations::XAnimationNode> mxCurrentAnimationRootNode;
1034 + ::boost::shared_ptr<ScreenUpdater::UpdateLock> mpPaintLock;
1036 + bool mbNonUserTriggeredMainSequenceEffectSeen;
1038 + void initialize (void);
1040 + bool resetEffectCount (void);
1041 + /** Called by listeners when an animation (not necessarily of a main
1042 + sequence effect) starts.
1044 + bool notifyAnimationStart (const AnimationNodeSharedPtr& rpNode);
1046 + /** Count the number of effects (or effect groups) in the main effect
1049 + sal_Int32 countMainSequenceEffects (void);
1051 + /** Skip the next main sequence effect.
1053 + void skipSingleMainSequenceEffects (void);
1055 + /** Skip the specified number of main sequence effects.
1057 + void skipSomeMainSequenceEffects (const sal_Int32 nSkipCount);
1059 + /** Rewind the last effect of the main effect sequence by replaying all
1061 + @param nEffectCount
1062 + The number of main sequence effects to replay.
1063 + @param bRedisplayCurrentSlide
1064 + When <TRUE/> then the current slide is redisplayed before the
1065 + effects are replayed.
1066 + @param rSlideRewindFunctor
1067 + This functor is used to redisplay the current slide.
1069 + void asynchronousRewind (
1070 + sal_Int32 nEffectCount,
1071 + const bool bRedisplayCurrentSlide,
1072 + const boost::function<void(void)>& rSlideRewindFunctor);
1074 + /** Go to the previous slide and replay all of its main sequence effects
1075 + (or effect groups).
1076 + @param rPreviousSlideFunctor
1077 + This functor is used to go to the previous slide.
1079 + void asynchronousRewindToPreviousSlide (
1080 + const ::boost::function<void(void)>& rPreviousSlideFunctor);
1083 +} } // end of namespace ::slideshow::internal
1086 diff --git slideshow/source/engine/eventmultiplexer.cxx slideshow/source/engine/eventmultiplexer.cxx
1087 index 87c9f44..b36d2c7 100644
1088 --- slideshow/source/engine/eventmultiplexer.cxx
1089 +++ slideshow/source/engine/eventmultiplexer.cxx
1090 @@ -448,7 +448,15 @@ void EventMultiplexerImpl::forEachView( XSlideShowViewFunc pViewMethod )
1091 for( UnoViewVector::const_iterator aIter( mrViewContainer.begin() ),
1092 aEnd( mrViewContainer.end() ); aIter != aEnd; ++aIter )
1094 - ((*aIter)->getUnoView().get()->*pViewMethod)( mxListener.get() );
1095 + uno::Reference<presentation::XSlideShowView> xView ((*aIter)->getUnoView());
1098 + (xView.get()->*pViewMethod)( mxListener.get() );
1102 + OSL_ASSERT(xView.is());
1107 diff --git slideshow/source/engine/eventqueue.cxx slideshow/source/engine/eventqueue.cxx
1108 index cd1eb37..087d4d5 100644
1109 --- slideshow/source/engine/eventqueue.cxx
1110 +++ slideshow/source/engine/eventqueue.cxx
1111 @@ -66,6 +66,7 @@ namespace slideshow
1115 + maNextNextEvents(),
1116 mpTimer( pPresTimer )
1119 @@ -131,6 +132,22 @@ namespace slideshow
1120 mpTimer->getElapsedTime()) ) );
1124 + bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent)
1126 + ::osl::MutexGuard aGuard( maMutex );
1129 + rpEvent.get() != NULL,
1130 + "EventQueue::addEvent: event ptr NULL");
1132 + maNextNextEvents.push(
1135 + rpEvent->getActivationTime(mpTimer->getElapsedTime())));
1140 void EventQueue::forceEmpty()
1142 @@ -157,12 +174,23 @@ namespace slideshow
1143 maEvents.push(*iPos);
1145 EventEntryVector().swap( maNextEvents );
1148 // perform topmost, ready-to-execute event
1149 // =======================================
1151 const double nCurrTime( mpTimer->getElapsedTime() );
1154 + // When maEvents does not contain any events that are due now
1155 + // then process one event from maNextNextEvents.
1156 + if (!maNextNextEvents.empty()
1157 + && !bFireAllEvents
1158 + && (maEvents.empty() || maEvents.top().nTime > nCurrTime))
1160 + const EventEntry aEvent (maNextNextEvents.top());
1161 + maNextNextEvents.pop();
1162 + maEvents.push(aEvent);
1165 // process ready/elapsed events. Note that the 'perceived'
1166 // current time remains constant for this loop, thus we're
1167 // processing only those events which where ready when we
1168 @@ -243,7 +271,7 @@ namespace slideshow
1170 ::osl::MutexGuard aGuard( maMutex );
1172 - return maEvents.empty();
1173 + return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty();
1176 double EventQueue::nextTimeout() const
1177 @@ -251,9 +279,16 @@ namespace slideshow
1178 ::osl::MutexGuard aGuard( maMutex );
1180 // return time for next entry (if any)
1181 - return isEmpty() ?
1182 - ::std::numeric_limits<double>::max() :
1183 - maEvents.top().nTime - mpTimer->getElapsedTime();
1184 + double nTimeout (::std::numeric_limits<double>::max());
1185 + const double nCurrentTime (mpTimer->getElapsedTime());
1186 + if ( ! maEvents.empty())
1187 + nTimeout = maEvents.top().nTime - nCurrentTime;
1188 + if ( ! maNextEvents.empty())
1189 + nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime);
1190 + if ( ! maNextNextEvents.empty())
1191 + nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime);
1196 void EventQueue::clear()
1197 diff --git slideshow/source/engine/makefile.mk slideshow/source/engine/makefile.mk
1198 index 1d45d2a..04c29db 100644
1199 --- slideshow/source/engine/makefile.mk
1200 +++ slideshow/source/engine/makefile.mk
1201 @@ -70,6 +70,7 @@ SLOFILES = $(SLO)$/activitiesqueue.obj \
1202 $(SLO)$/attributemap.obj \
1204 $(SLO)$/delayevent.obj \
1205 + $(SLO)$/effectrewinder.obj \
1206 $(SLO)$/eventmultiplexer.obj \
1207 $(SLO)$/eventqueue.obj \
1208 $(SLO)$/expressionnodefactory.obj \
1209 diff --git slideshow/source/engine/screenupdater.cxx slideshow/source/engine/screenupdater.cxx
1210 index bf3ca0c..8cfaadd 100644
1211 --- slideshow/source/engine/screenupdater.cxx
1212 +++ slideshow/source/engine/screenupdater.cxx
1215 #include <algorithm>
1218 + class UpdateLock : public ::slideshow::internal::ScreenUpdater::UpdateLock
1221 + UpdateLock (::slideshow::internal::ScreenUpdater& rUpdater, const bool bStartLocked);
1222 + virtual ~UpdateLock (void);
1223 + virtual void Activate (void);
1225 + ::slideshow::internal::ScreenUpdater& mrUpdater;
1226 + bool mbIsActivated;
1233 @@ -64,12 +77,16 @@ namespace internal
1234 /// True, if at least one notifyUpdate() call had bViewClobbered set
1235 bool mbViewClobbered;
1237 + /// The screen is updated only when mnLockCount==0
1238 + sal_Int32 mnLockCount;
1240 explicit ImplScreenUpdater( UnoViewContainer const& rViewContainer ) :
1242 maViewUpdateRequests(),
1243 mrViewContainer(rViewContainer),
1244 mbUpdateAllRequest(false),
1245 - mbViewClobbered(false)
1246 + mbViewClobbered(false),
1251 @@ -100,6 +117,9 @@ namespace internal
1253 void ScreenUpdater::commitUpdates()
1255 + if (mpImpl->mnLockCount > 0)
1260 // (a) no update necessary at all
1261 @@ -178,6 +198,9 @@ namespace internal
1263 void ScreenUpdater::requestImmediateUpdate()
1265 + if (mpImpl->mnLockCount > 0)
1268 // TODO(F2): This will interfere with other updates, since it
1269 // happens out-of-sync with main animation loop. Might cause
1271 @@ -186,5 +209,63 @@ namespace internal
1272 boost::mem_fn(&View::updateScreen) );
1275 + void ScreenUpdater::lockUpdates (void)
1277 + ++mpImpl->mnLockCount;
1278 + OSL_ASSERT(mpImpl->mnLockCount>0);
1281 + void ScreenUpdater::unlockUpdates (void)
1283 + OSL_ASSERT(mpImpl->mnLockCount>0);
1284 + if (mpImpl->mnLockCount > 0)
1286 + --mpImpl->mnLockCount;
1287 + if (mpImpl->mnLockCount)
1292 + ::boost::shared_ptr<ScreenUpdater::UpdateLock> ScreenUpdater::createLock (const bool bStartLocked)
1294 + return ::boost::shared_ptr<ScreenUpdater::UpdateLock>(new ::UpdateLock(*this, bStartLocked));
1298 } // namespace internal
1299 } // namespace slideshow
1303 +UpdateLock::UpdateLock (
1304 + ::slideshow::internal::ScreenUpdater& rUpdater,
1305 + const bool bStartLocked)
1306 + : mrUpdater(rUpdater),
1307 + mbIsActivated(false)
1316 +UpdateLock::~UpdateLock (void)
1318 + if (mbIsActivated)
1319 + mrUpdater.unlockUpdates();
1325 +void UpdateLock::Activate (void)
1327 + if ( ! mbIsActivated)
1329 + mbIsActivated = true;
1330 + mrUpdater.lockUpdates();
1335 diff --git slideshow/source/engine/shapes/drawshape.cxx slideshow/source/engine/shapes/drawshape.cxx
1336 index c203c9d..8894073 100644
1337 --- slideshow/source/engine/shapes/drawshape.cxx
1338 +++ slideshow/source/engine/shapes/drawshape.cxx
1339 @@ -262,7 +262,8 @@ namespace slideshow
1341 void DrawShape::updateStateIds() const
1343 - // update the states, we've just redrawn
1344 + // Update the states, we've just redrawn or created a new
1345 + // attribute layer.
1346 if( mpAttributeLayer )
1348 mnAttributeTransformationState = mpAttributeLayer->getTransformationState();
1349 @@ -1277,6 +1278,9 @@ namespace slideshow
1350 // create new layer, with last as its new child
1351 mpAttributeLayer.reset( new ShapeAttributeLayer( mpAttributeLayer ) );
1353 + // Update the local state ids to reflect those of the new layer.
1356 return mpAttributeLayer;
1359 diff --git slideshow/source/engine/slide/layermanager.cxx slideshow/source/engine/slide/layermanager.cxx
1360 index 999d098..f55bef6 100644
1361 --- slideshow/source/engine/slide/layermanager.cxx
1362 +++ slideshow/source/engine/slide/layermanager.cxx
1363 @@ -67,11 +67,11 @@ namespace slideshow
1365 LayerSharedPtr pCurrLayer;
1366 ViewLayerSharedPtr pCurrViewLayer;
1367 - LayerShapeSet::const_iterator aIter( maAllShapes.begin() );
1368 - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() );
1369 + LayerShapeMap::const_iterator aIter( maAllShapes.begin() );
1370 + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() );
1371 while( aIter != aEnd )
1373 - LayerSharedPtr pLayer = aIter->mpLayer.lock();
1374 + LayerSharedPtr pLayer = aIter->second.lock();
1375 if( pLayer && pLayer != pCurrLayer )
1377 pCurrLayer = pLayer;
1378 @@ -79,7 +79,7 @@ namespace slideshow
1381 if( pCurrViewLayer )
1382 - shapeFunc(aIter->mpShape,pCurrViewLayer);
1383 + shapeFunc(aIter->first,pCurrViewLayer);
1387 @@ -164,8 +164,17 @@ namespace slideshow
1388 std::for_each(maAllShapes.begin(),
1390 boost::bind( &Shape::clearAllViewLayers,
1391 - boost::bind( &ShapeEntry::getShape,
1392 + boost::bind( std::select1st<LayerShapeMap::value_type>(),
1395 + for (LayerShapeMap::iterator
1396 + iShape (maAllShapes.begin()),
1397 + iEnd (maAllShapes.end());
1401 + iShape->second.reset();
1404 if( bMoreThanOneLayer )
1405 maLayers.erase(maLayers.begin()+1,
1406 @@ -265,8 +274,7 @@ namespace slideshow
1407 std::for_each( maAllShapes.begin(),
1409 boost::bind(&Shape::render,
1410 - boost::bind(&ShapeEntry::getShape,
1412 + boost::bind( ::std::select1st<LayerShapeMap::value_type>(), _1)) );
1415 void LayerManager::addShape( const ShapeSharedPtr& rShape )
1416 @@ -287,13 +295,11 @@ namespace slideshow
1417 implAddShape( rShape );
1420 - void LayerManager::putShape2BackgroundLayer( const ShapeEntry& rShapeEntry )
1421 + void LayerManager::putShape2BackgroundLayer( LayerShapeMap::value_type& rShapeEntry )
1423 LayerSharedPtr& rBgLayer( maLayers.front() );
1424 - rBgLayer->setShapeViews(rShapeEntry.mpShape);
1425 - // changing a part of the ShapeEntry irrelevant for the
1427 - const_cast<ShapeEntry&>(rShapeEntry).mpLayer = rBgLayer;
1428 + rBgLayer->setShapeViews(rShapeEntry.first);
1429 + rShapeEntry.second = rBgLayer;
1432 void LayerManager::implAddShape( const ShapeSharedPtr& rShape )
1433 @@ -301,16 +307,16 @@ namespace slideshow
1434 OSL_ASSERT( !maLayers.empty() ); // always at least background layer
1435 ENSURE_OR_THROW( rShape, "LayerManager::implAddShape(): invalid Shape" );
1437 - ShapeEntry aShapeEntry(rShape);
1438 + LayerShapeMap::value_type aValue (rShape, LayerWeakPtr());
1440 - OSL_ASSERT( maAllShapes.find(aShapeEntry) == maAllShapes.end() ); // shape must not be added already
1441 + OSL_ASSERT( maAllShapes.find(rShape) == maAllShapes.end() ); // shape must not be added already
1442 mbLayerAssociationDirty = true;
1444 if( mbDisableAnimationZOrder )
1445 putShape2BackgroundLayer(
1446 - *maAllShapes.insert(aShapeEntry).first );
1447 + *maAllShapes.insert(aValue).first );
1449 - maAllShapes.insert(aShapeEntry);
1450 + maAllShapes.insert(aValue);
1452 // update shape, it's just added and not yet painted
1453 if( rShape->isVisible() )
1454 @@ -323,8 +329,7 @@ namespace slideshow
1455 if( maXShapeHash.erase( rShape->getXShape() ) == 0 )
1456 return false; // shape not in map
1458 - OSL_ASSERT( maAllShapes.find(
1459 - ShapeEntry(rShape)) != maAllShapes.end() );
1460 + OSL_ASSERT( maAllShapes.find(rShape) != maAllShapes.end() );
1462 implRemoveShape( rShape );
1464 @@ -336,9 +341,7 @@ namespace slideshow
1465 OSL_ASSERT( !maLayers.empty() ); // always at least background layer
1466 ENSURE_OR_THROW( rShape, "LayerManager::implRemoveShape(): invalid Shape" );
1468 - const LayerShapeSet::iterator aShapeEntry(
1470 - ShapeEntry(rShape)) );
1471 + const LayerShapeMap::iterator aShapeEntry( maAllShapes.find(rShape) );
1473 if( aShapeEntry == maAllShapes.end() )
1475 @@ -354,7 +357,7 @@ namespace slideshow
1476 (rShape->isVisible() &&
1477 !rShape->isBackgroundDetached()) )
1479 - LayerSharedPtr pLayer = aShapeEntry->mpLayer.lock();
1480 + LayerSharedPtr pLayer = aShapeEntry->second.lock();
1483 // store area early, once the shape is removed from
1484 @@ -419,8 +422,7 @@ namespace slideshow
1486 if( rOrigShape->revokeSubset( rSubsetShape ) )
1488 - OSL_ASSERT( maAllShapes.find(
1489 - ShapeEntry(rSubsetShape)) != maAllShapes.end() );
1490 + OSL_ASSERT( maAllShapes.find(rSubsetShape) != maAllShapes.end() );
1492 implRemoveShape( rSubsetShape );
1494 @@ -584,11 +586,11 @@ namespace slideshow
1495 bool bIsCurrLayerUpdating(false);
1496 Layer::EndUpdater aEndUpdater;
1497 LayerSharedPtr pCurrLayer;
1498 - LayerShapeSet::const_iterator aIter( maAllShapes.begin() );
1499 - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() );
1500 + LayerShapeMap::const_iterator aIter( maAllShapes.begin() );
1501 + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() );
1502 while( aIter != aEnd )
1504 - LayerSharedPtr pLayer = aIter->mpLayer.lock();
1505 + LayerSharedPtr pLayer = aIter->second.lock();
1506 if( pLayer != pCurrLayer )
1508 pCurrLayer = pLayer;
1509 @@ -599,10 +601,10 @@ namespace slideshow
1512 if( bIsCurrLayerUpdating &&
1513 - !aIter->mpShape->isBackgroundDetached() &&
1514 - pCurrLayer->isInsideUpdateArea(aIter->mpShape) )
1515 + !aIter->first->isBackgroundDetached() &&
1516 + pCurrLayer->isInsideUpdateArea(aIter->first) )
1518 - if( !aIter->mpShape->render() )
1519 + if( !aIter->first->render() )
1523 @@ -694,8 +696,8 @@ namespace slideshow
1525 ViewLayerSharedPtr pTmpLayer( new DummyLayer( rTargetCanvas ) );
1527 - LayerShapeSet::const_iterator aIter( maAllShapes.begin() );
1528 - const LayerShapeSet::const_iterator aEnd ( maAllShapes.end() );
1529 + LayerShapeMap::const_iterator aIter( maAllShapes.begin() );
1530 + const LayerShapeMap::const_iterator aEnd ( maAllShapes.end() );
1531 while( aIter != aEnd )
1534 @@ -705,11 +707,11 @@ namespace slideshow
1535 // ViewLayer. Since we add the shapes in the
1536 // maShapeSet order (which is also the render order),
1537 // this is equivalent to a subsequent render() call)
1538 - aIter->mpShape->addViewLayer( pTmpLayer,
1540 + aIter->first->addViewLayer( pTmpLayer,
1543 // and remove again, this is only temporary
1544 - aIter->mpShape->removeViewLayer( pTmpLayer );
1545 + aIter->first->removeViewLayer( pTmpLayer );
1547 catch( uno::Exception& )
1549 @@ -735,21 +737,19 @@ namespace slideshow
1550 OSL_ASSERT( !maLayers.empty() ); // always at least background layer
1551 ENSURE_OR_THROW( rShape, "LayerManager::addUpdateArea(): invalid Shape" );
1553 - const LayerShapeSet::const_iterator aShapeEntry(
1555 - ShapeEntry(rShape)) );
1556 + const LayerShapeMap::const_iterator aShapeEntry( maAllShapes.find(rShape) );
1558 if( aShapeEntry == maAllShapes.end() )
1561 - LayerSharedPtr pLayer = aShapeEntry->mpLayer.lock();
1562 + LayerSharedPtr pLayer = aShapeEntry->second.lock();
1564 pLayer->addUpdateRange( rShape->getUpdateArea() );
1567 void LayerManager::commitLayerChanges( std::size_t nCurrLayerIndex,
1568 - LayerShapeSet::const_iterator aFirstLayerShape,
1569 - LayerShapeSet::const_iterator aEndLayerShapes )
1570 + LayerShapeMap::const_iterator aFirstLayerShape,
1571 + LayerShapeMap::const_iterator aEndLayerShapes )
1573 const bool bLayerExists( maLayers.size() > nCurrLayerIndex );
1575 @@ -768,8 +768,8 @@ namespace slideshow
1576 // render and remove from update set
1577 while( aFirstLayerShape != aEndLayerShapes )
1579 - maUpdateShapes.erase(aFirstLayerShape->mpShape);
1580 - aFirstLayerShape->mpShape->render();
1581 + maUpdateShapes.erase(aFirstLayerShape->first);
1582 + aFirstLayerShape->first->render();
1586 @@ -825,13 +825,13 @@ namespace slideshow
1587 std::size_t nCurrLayerIndex(0);
1588 bool bIsBackgroundLayer(true);
1589 bool bLastWasBackgroundDetached(false); // last shape sprite state
1590 - LayerShapeSet::iterator aCurrShapeEntry( maAllShapes.begin() );
1591 - LayerShapeSet::iterator aCurrLayerFirstShapeEntry( maAllShapes.begin() );
1592 - const LayerShapeSet::iterator aEndShapeEntry ( maAllShapes.end() );
1593 + LayerShapeMap::iterator aCurrShapeEntry( maAllShapes.begin() );
1594 + LayerShapeMap::iterator aCurrLayerFirstShapeEntry( maAllShapes.begin() );
1595 + const LayerShapeMap::iterator aEndShapeEntry ( maAllShapes.end() );
1596 ShapeUpdateSet aUpdatedShapes; // shapes that need update
1597 while( aCurrShapeEntry != aEndShapeEntry )
1599 - const ShapeSharedPtr pCurrShape( aCurrShapeEntry->mpShape );
1600 + const ShapeSharedPtr pCurrShape( aCurrShapeEntry->first );
1601 const bool bThisIsBackgroundDetached(
1602 pCurrShape->isBackgroundDetached() );
1604 @@ -851,7 +851,7 @@ namespace slideshow
1605 bIsBackgroundLayer = false;
1607 if( aWeakLayers.size() <= nCurrLayerIndex ||
1608 - aWeakLayers.at(nCurrLayerIndex) != aCurrShapeEntry->mpLayer )
1609 + aWeakLayers.at(nCurrLayerIndex) != aCurrShapeEntry->second )
1611 // no more layers left, or shape was not
1612 // member of this layer - create a new one
1613 @@ -868,7 +868,7 @@ namespace slideshow
1614 // above invalidates iterators
1615 LayerSharedPtr& rCurrLayer( maLayers.at(nCurrLayerIndex) );
1616 LayerWeakPtr& rCurrWeakLayer( aWeakLayers.at(nCurrLayerIndex) );
1617 - if( rCurrWeakLayer != aCurrShapeEntry->mpLayer )
1618 + if( rCurrWeakLayer != aCurrShapeEntry->second )
1620 // mismatch: shape is not contained in current
1621 // layer - move shape to that layer, then.
1622 @@ -879,7 +879,7 @@ namespace slideshow
1624 if( !bThisIsBackgroundDetached && pCurrShape->isVisible() )
1626 - LayerSharedPtr pOldLayer( aCurrShapeEntry->mpLayer.lock() );
1627 + LayerSharedPtr pOldLayer( aCurrShapeEntry->second.lock() );
1630 // old layer still valid? then we need to
1631 @@ -894,10 +894,7 @@ namespace slideshow
1632 maUpdateShapes.insert( pCurrShape );
1635 - // std::set iterators are const for a reason - but
1636 - // here, we need modify an aspect of the
1637 - // ShapeEntry that has no influence on sort order
1638 - const_cast<ShapeEntry&>(*aCurrShapeEntry).mpLayer = rCurrWeakLayer;
1639 + aCurrShapeEntry->second = rCurrWeakLayer;
1642 // update layerbounds regardless of the fact that the
1643 diff --git slideshow/source/engine/slide/layermanager.hxx slideshow/source/engine/slide/layermanager.hxx
1644 index f2eb316..b435186 100644
1645 --- slideshow/source/engine/slide/layermanager.hxx
1646 +++ slideshow/source/engine/slide/layermanager.hxx
1647 @@ -254,33 +254,18 @@ namespace slideshow
1648 hash< ::com::sun::star::uno::Reference<
1649 ::com::sun::star::drawing::XShape > > > XShapeHash;
1651 - /** Element of all-shapes set
1654 + class ShapeComparator
1656 - /// Shape this entry stands for
1657 - ShapeSharedPtr mpShape;
1659 - /// Layer this shape is currently displayed on
1660 - LayerWeakPtr mpLayer;
1662 - explicit ShapeEntry( ShapeSharedPtr const& rShape ) :
1667 - ShapeSharedPtr const& getShape() const { return mpShape; }
1669 - bool operator<( const ShapeEntry& rRHS ) const
1671 + bool operator() (const ShapeSharedPtr& rpS1, const ShapeSharedPtr& rpS2 ) const
1673 - return Shape::lessThanShape::compare(mpShape.get(),
1674 - rRHS.mpShape.get());
1675 + return Shape::lessThanShape::compare(rpS1.get(), rpS2.get());
1679 /** Set of all shapes
1681 - typedef ::std::set< ShapeEntry > LayerShapeSet;
1683 + typedef ::std::map< ShapeSharedPtr, LayerWeakPtr, ShapeComparator > LayerShapeMap;
1684 typedef ::std::set< ShapeSharedPtr > ShapeUpdateSet;
1687 @@ -309,12 +294,12 @@ namespace slideshow
1688 denoting one-behind-the-last shape of nCurrLayerIndex
1690 void commitLayerChanges( std::size_t nCurrLayerIndex,
1691 - LayerShapeSet::const_iterator aFirstLayerShape,
1692 - LayerShapeSet::const_iterator aEndLayerShapes );
1693 + LayerShapeMap::const_iterator aFirstLayerShape,
1694 + LayerShapeMap::const_iterator aEndLayerShapes );
1696 /** Init Shape layers with background layer.
1698 - void putShape2BackgroundLayer( const ShapeEntry& rShapeEntry );
1699 + void putShape2BackgroundLayer( LayerShapeMap::value_type& rShapeEntry );
1701 /** Commits any pending layer reorg, due to shapes either
1702 entering or leaving animation mode
1703 @@ -364,7 +349,7 @@ namespace slideshow
1704 for buffering animation enable/disable changes, and
1705 shape update requests.
1707 - LayerShapeSet maAllShapes;
1708 + LayerShapeMap maAllShapes;
1710 /** Set of shapes that have requested an update
1712 diff --git slideshow/source/engine/slideshowimpl.cxx slideshow/source/engine/slideshowimpl.cxx
1713 index 4245e07..92a1562 100644
1714 --- slideshow/source/engine/slideshowimpl.cxx
1715 +++ slideshow/source/engine/slideshowimpl.cxx
1717 #include "slidebitmap.hxx"
1718 #include "rehearsetimingsactivity.hxx"
1719 #include "waitsymbol.hxx"
1720 +#include "effectrewinder.hxx"
1721 #include "framerate.hxx"
1723 #include <boost/noncopyable.hpp>
1724 @@ -194,7 +195,7 @@ public:
1726 This method notifies the end of the third phase.
1728 - void notifySlideEnded();
1729 + void notifySlideEnded (const bool bReverse);
1731 /** Notification from eventmultiplexer that a hyperlink
1733 @@ -209,6 +210,7 @@ public:
1736 virtual sal_Bool SAL_CALL nextEffect() throw (uno::RuntimeException);
1737 + virtual sal_Bool SAL_CALL previousEffect() throw (uno::RuntimeException);
1738 virtual sal_Bool SAL_CALL startShapeActivity(
1739 uno::Reference<drawing::XShape> const& xShape )
1740 throw (uno::RuntimeException);
1741 @@ -259,6 +261,12 @@ private:
1742 virtual bool requestCursor( sal_Int16 nCursorShape );
1743 virtual void resetCursor();
1745 + /** This is somewhat similar to displaySlide when called for the current
1746 + slide. It has been simplified to take advantage of that no slide
1747 + change takes place. Furthermore it does not show the slide
1750 + void redisplayCurrentSlide (void);
1753 // WeakComponentImplHelperBase
1754 @@ -314,12 +322,32 @@ private:
1755 const SlideSharedPtr& rEnteringSlide,
1756 const EventSharedPtr& rTransitionEndEvent );
1758 - /// Display/hide wait symbol on all views
1759 - void setWaitState( bool bOn );
1760 + /** Request/release the wait symbol. The wait symbol is displayed when
1761 + there are more requests then releases. Locking the wait symbol
1762 + helps to avoid intermediate repaints.
1764 + Do not call this method directly. Use WaitSymbolLock instead.
1766 + void requestWaitSymbol (void);
1767 + void releaseWaitSymbol (void);
1769 + class WaitSymbolLock {public:
1770 + WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
1771 + { mrSlideShowImpl.requestWaitSymbol(); }
1772 + ~WaitSymbolLock(void)
1773 + { mrSlideShowImpl.releaseWaitSymbol(); }
1774 + private: SlideShowImpl& mrSlideShowImpl;
1778 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
1779 sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
1781 + /** This method is called asynchronously to finish the rewinding of an
1782 + effect to the previous slide that was initiated earlier.
1784 + void rewindEffectToPreviousSlide (void);
1786 /// all registered views
1787 UnoViewContainer maViewContainer;
1789 @@ -368,7 +396,7 @@ private:
1791 sal_Int16 mnCurrentCursor;
1794 + sal_Int32 mnWaitSymbolRequestCount;
1795 bool mbAutomaticAdvancementMode;
1796 bool mbImageAnimationsAllowed;
1797 bool mbNoSlideTransitions;
1798 @@ -377,6 +405,8 @@ private:
1800 bool mbSlideShowIdle;
1801 bool mbDisableAnimationZOrder;
1803 + EffectRewinder maEffectRewinder;
1807 @@ -469,7 +499,7 @@ SlideShowImpl::SlideShowImpl(
1809 mxPrefetchAnimationNode(),
1810 mnCurrentCursor(awt::SystemPointer::ARROW),
1811 - mbWaitState(false),
1812 + mnWaitSymbolRequestCount(0),
1813 mbAutomaticAdvancementMode(false),
1814 mbImageAnimationsAllowed( true ),
1815 mbNoSlideTransitions( false ),
1816 @@ -477,7 +507,8 @@ SlideShowImpl::SlideShowImpl(
1817 mbForceManualAdvance( false ),
1818 mbShowPaused( false ),
1819 mbSlideShowIdle( true ),
1820 - mbDisableAnimationZOrder( false )
1821 + mbDisableAnimationZOrder( false ),
1822 + maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue)
1824 // keep care not constructing any UNO references to this inside ctor,
1825 // shift that code to create()!
1826 @@ -511,6 +542,8 @@ void SlideShowImpl::disposing()
1828 osl::MutexGuard const guard( m_aMutex );
1830 + maEffectRewinder.dispose();
1832 // stop slide transition sound, if any:
1833 stopSlideTransitionSound();
1835 @@ -611,7 +644,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition(
1836 const uno::Reference< drawing::XDrawPage >& xDrawPage,
1837 const SlideSharedPtr& rLeavingSlide,
1838 const SlideSharedPtr& rEnteringSlide,
1839 - const EventSharedPtr& rTransitionEndEvent )
1840 + const EventSharedPtr& rTransitionEndEvent)
1842 ENSURE_OR_THROW( !maViewContainer.empty(),
1843 "createSlideTransition(): No views" );
1844 @@ -696,7 +729,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition(
1845 bTransitionDirection,
1846 aTransitionFadeColor,
1847 resetSlideTransitionSound( aSound, bLoopSound ) ));
1851 return ActivitySharedPtr(); // no transition effect has been
1852 // generated. Normally, that means
1853 @@ -783,20 +816,43 @@ SlideSharedPtr SlideShowImpl::makeSlide(
1857 -void SlideShowImpl::setWaitState( bool bOn )
1858 +void SlideShowImpl::requestWaitSymbol (void)
1860 - mbWaitState = bOn;
1861 - if( !mpWaitSymbol ) // fallback to cursor
1862 - requestCursor(awt::SystemPointer::WAIT);
1863 - else if( mbWaitState )
1864 - mpWaitSymbol->show();
1866 - mpWaitSymbol->hide();
1867 + ++mnWaitSymbolRequestCount;
1868 + OSL_ASSERT(mnWaitSymbolRequestCount>0);
1870 + if (mnWaitSymbolRequestCount == 1)
1872 + if( !mpWaitSymbol )
1874 + // fall back to cursor
1875 + requestCursor(calcActiveCursor(mnCurrentCursor));
1878 + mpWaitSymbol->show();
1882 +void SlideShowImpl::releaseWaitSymbol (void)
1884 + --mnWaitSymbolRequestCount;
1885 + OSL_ASSERT(mnWaitSymbolRequestCount>=0);
1887 + if (mnWaitSymbolRequestCount == 0)
1889 + if( !mpWaitSymbol )
1891 + // fall back to cursor
1892 + requestCursor(calcActiveCursor(mnCurrentCursor));
1895 + mpWaitSymbol->hide();
1899 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
1901 - if( mbWaitState && !mpWaitSymbol ) // enforce wait cursor
1902 + if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
1903 nCursorShape = awt::SystemPointer::WAIT;
1904 else if( !mbMouseVisible ) // enforce INVISIBLE
1905 nCursorShape = awt::SystemPointer::INVISIBLE;
1906 @@ -840,10 +896,19 @@ void SlideShowImpl::stopShow()
1910 -struct SlideShowImpl::PrefetchPropertiesFunc
1913 +class SlideShowImpl::PrefetchPropertiesFunc
1915 - SlideShowImpl *const that;
1916 - PrefetchPropertiesFunc( SlideShowImpl * that_ ) : that(that_) {}
1918 + PrefetchPropertiesFunc( SlideShowImpl * that_,
1919 + bool& rbSkipAllMainSequenceEffects,
1920 + bool& rbSkipSlideTransition)
1921 + : mpSlideShowImpl(that_),
1922 + mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1923 + mrbSkipSlideTransition(rbSkipSlideTransition)
1926 void operator()( beans::PropertyValue const& rProperty ) const {
1927 if (rProperty.Name.equalsAsciiL(
1928 RTL_CONSTASCII_STRINGPARAM("Prefetch") ))
1929 @@ -851,16 +916,30 @@ struct SlideShowImpl::PrefetchPropertiesFunc
1930 uno::Sequence<uno::Any> seq;
1931 if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1933 - seq[0] >>= that->mxPrefetchSlide;
1934 - seq[1] >>= that->mxPrefetchAnimationNode;
1935 + seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1936 + seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1939 + else if (rProperty.Name.equalsAsciiL(
1940 + RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") ))
1942 + rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1944 + else if (rProperty.Name.equalsAsciiL(
1945 + RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") ))
1947 + rProperty.Value >>= mrbSkipSlideTransition;
1951 OSL_ENSURE( false, rtl::OUStringToOString(
1952 rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() );
1956 + SlideShowImpl *const mpSlideShowImpl;
1957 + bool& mrbSkipAllMainSequenceEffects;
1958 + bool& mrbSkipSlideTransition;
1961 void SlideShowImpl::displaySlide(
1962 @@ -873,7 +952,9 @@ void SlideShowImpl::displaySlide(
1968 + maEffectRewinder.setRootAnimationNode(xRootNode);
1970 // precondition: must only be called from the main thread!
1971 DBG_TESTSOLARMUTEX();
1973 @@ -885,20 +966,20 @@ void SlideShowImpl::displaySlide(
1974 // unconditionally. Otherwise, genuine
1975 // shape animations (drawing layer and
1976 // GIF) will not be stopped.
1979 + bool bSkipAllMainSequenceEffects (false);
1980 + bool bSkipSlideTransition (false);
1981 std::for_each( rProperties.getConstArray(),
1982 rProperties.getConstArray() + rProperties.getLength(),
1983 - PrefetchPropertiesFunc(this) );
1984 + PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1986 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1987 if (maViewContainer.empty())
1991 // this here might take some time
1993 - comphelper::ScopeGuard const scopeGuard(
1994 - boost::bind( &SlideShowImpl::setWaitState, this, false ) );
1995 - setWaitState(true);
1996 + WaitSymbolLock aLock (*this);
1998 mpPreviousSlide = mpCurrentSlide;
1999 mpCurrentSlide.reset();
2000 @@ -940,15 +1021,25 @@ void SlideShowImpl::displaySlide(
2001 // create slide transition, and add proper end event
2002 // (which then starts the slide effects
2003 // via CURRENT_SLIDE.show())
2004 - ActivitySharedPtr const pSlideChangeActivity(
2005 - createSlideTransition( mpCurrentSlide->getXDrawPage(),
2010 - &SlideShowImpl::notifySlideTransitionEnded,
2013 + ActivitySharedPtr pSlideChangeActivity (
2014 + createSlideTransition(
2015 + mpCurrentSlide->getXDrawPage(),
2020 + &SlideShowImpl::notifySlideTransitionEnded,
2024 + if (bSkipSlideTransition)
2026 + // The transition activity was created for the side effects
2027 + // (like sound transitions). Because we want to skip the
2028 + // acutual transition animation we do not need the activity
2030 + pSlideChangeActivity.reset();
2033 if (pSlideChangeActivity)
2035 @@ -972,6 +1063,41 @@ void SlideShowImpl::displaySlide(
2036 maEventMultiplexer.notifySlideTransitionStarted();
2037 maListenerContainer.forEach<presentation::XSlideShowListener>(
2038 boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
2040 + // We are currently rewinding an effect. This lead us from the next
2041 + // slide to this one. To complete this we have to play back all main
2042 + // sequence effects on this slide.
2043 + if (bSkipAllMainSequenceEffects)
2044 + maEffectRewinder.skipAllMainSequenceEffects();
2047 +void SlideShowImpl::redisplayCurrentSlide (void)
2049 + osl::MutexGuard const guard( m_aMutex );
2054 + // precondition: must only be called from the main thread!
2055 + DBG_TESTSOLARMUTEX();
2058 + OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
2059 + if (maViewContainer.empty())
2062 + // No transition effect on this slide - schedule slide
2063 + // effect start event right away.
2064 + maEventQueue.addEvent(
2067 + &SlideShowImpl::notifySlideTransitionEnded,
2071 + maEventMultiplexer.notifySlideTransitionStarted();
2072 + maListenerContainer.forEach<presentation::XSlideShowListener>(
2073 + boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
2076 sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException)
2077 @@ -990,6 +1116,50 @@ sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException)
2078 return maEventMultiplexer.notifyNextEffect();
2082 +sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException)
2084 + osl::MutexGuard const guard( m_aMutex );
2089 + // precondition: must only be called from the main thread!
2090 + DBG_TESTSOLARMUTEX();
2096 + return maEffectRewinder.rewind(
2097 + maScreenUpdater.createLock(false),
2098 + ::boost::bind(&SlideShowImpl::redisplayCurrentSlide, this),
2099 + ::boost::bind(&SlideShowImpl::rewindEffectToPreviousSlide, this));
2103 +void SlideShowImpl::rewindEffectToPreviousSlide (void)
2105 + // Show the wait symbol now and prevent it from showing temporary slide
2106 + // content while effects are played back.
2107 + WaitSymbolLock aLock (*this);
2109 + // A previous call to EffectRewinder::Rewind could not rewind the current
2110 + // effect because there are no effects on the current slide or none has
2111 + // yet been displayed. Go to the previous slide.
2112 + notifySlideEnded(true);
2114 + // Process pending events once more in order to have the following
2115 + // screen update show the last effect. Not sure whether this should be
2117 + maEventQueue.forceEmpty();
2119 + // We have to call the screen updater before the wait symbol is turned
2120 + // off. Otherwise the wait symbol would force the display of an
2121 + // intermediate state of the slide (before the effects are replayed.)
2122 + maScreenUpdater.commitUpdates();
2125 sal_Bool SlideShowImpl::startShapeActivity(
2126 uno::Reference<drawing::XShape> const& /*xShape*/ )
2127 throw (uno::RuntimeException)
2128 @@ -1692,7 +1862,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
2129 // schedule a slide end event, with automatic mode's
2131 aNotificationEvents = makeInterruptableDelay(
2132 - boost::bind( &SlideShowImpl::notifySlideEnded, this ),
2133 + boost::bind( &SlideShowImpl::notifySlideEnded, this, false ),
2134 maEventMultiplexer.getAutomaticTimeout() );
2137 @@ -1717,7 +1887,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
2138 bHasAutomaticNextSlide )
2140 aNotificationEvents = makeInterruptableDelay(
2141 - boost::bind( &SlideShowImpl::notifySlideEnded, this ),
2142 + boost::bind( &SlideShowImpl::notifySlideEnded, this, false ),
2143 nAutomaticNextSlideTimeout);
2145 // TODO(F2): Provide a mechanism to let the user override
2146 @@ -1734,7 +1904,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
2147 // timeout involved.
2148 aNotificationEvents.mpImmediateEvent =
2149 makeEvent( boost::bind(
2150 - &SlideShowImpl::notifySlideEnded, this ) );
2151 + &SlideShowImpl::notifySlideEnded, this, false ) );
2155 @@ -1755,9 +1925,7 @@ void SlideShowImpl::notifySlideAnimationsEnded()
2156 // change setup time a lot). Show the wait cursor, this
2157 // indeed might take some seconds.
2159 - comphelper::ScopeGuard const scopeGuard(
2160 - boost::bind( &SlideShowImpl::setWaitState, this, false ) );
2161 - setWaitState(true);
2162 + WaitSymbolLock aLock (*this);
2164 if (! matches( mpPrefetchSlide,
2165 mxPrefetchSlide, mxPrefetchAnimationNode ))
2166 @@ -1779,13 +1947,13 @@ void SlideShowImpl::notifySlideAnimationsEnded()
2167 boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded ) );
2170 -void SlideShowImpl::notifySlideEnded()
2171 +void SlideShowImpl::notifySlideEnded (const bool bReverse)
2173 osl::MutexGuard const guard( m_aMutex );
2175 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2177 - if (mpRehearseTimingsActivity)
2178 + if (mpRehearseTimingsActivity && !bReverse)
2180 const double time = mpRehearseTimingsActivity->stop();
2181 if (mpRehearseTimingsActivity->hasBeenClicked())
2182 @@ -1805,8 +1973,9 @@ void SlideShowImpl::notifySlideEnded()
2187 - maEventMultiplexer.notifySlideEndEvent();
2190 + maEventMultiplexer.notifySlideEndEvent();
2192 stopShow(); // MUST call that: results in
2193 // maUserEventQueue.clear(). What's more,
2194 @@ -1818,7 +1987,10 @@ void SlideShowImpl::notifySlideEnded()
2195 // GIF) will not be stopped.
2197 maListenerContainer.forEach<presentation::XSlideShowListener>(
2198 - boost::mem_fn( &presentation::XSlideShowListener::slideEnded ) );
2200 + &presentation::XSlideShowListener::slideEnded,
2205 bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString const& hyperLink )
2206 diff --git slideshow/source/engine/usereventqueue.cxx slideshow/source/engine/usereventqueue.cxx
2207 index 1ead45a..774fd0a 100644
2208 --- slideshow/source/engine/usereventqueue.cxx
2209 +++ slideshow/source/engine/usereventqueue.cxx
2210 @@ -306,26 +306,46 @@ public:
2211 EventMultiplexer & rEventMultiplexer )
2212 : ClickEventHandler(rEventQueue),
2213 mrEventQueue(rEventQueue),
2214 - mrEventMultiplexer(rEventMultiplexer) {}
2215 + mrEventMultiplexer(rEventMultiplexer),
2216 + mbSkipTriggersNextEffect(true) {}
2218 + /** Remember to trigger (or not to trigger) the next effect after the
2219 + current effect is skiped.
2221 + void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect)
2222 + { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; }
2224 + /// Skip the current effect but do not triggere the next effect.
2225 + void skipEffect (void) { handleEvent_impl(false); }
2228 virtual bool handleEvent_impl()
2230 + return handleEvent_impl(true);
2233 + bool handleEvent_impl (bool bNotifyNextEffect)
2235 // fire all events, so animation nodes can register their
2236 // next effect listeners:
2237 if(fireAllEvents( maEvents, mrEventQueue ))
2239 - // then simulate a next effect event:
2240 - // this skip effect handler is triggered upon next effect
2241 - // events (multiplexer prio=-1)!
2242 - // Posting a notifyNextEffect() here is only safe
2243 - // (we don't run into busy loop), because we assume that
2244 - // someone has registerered above for next effects
2245 - // (multiplexer prio=0) at the user event queue.
2246 - return mrEventQueue.addEventForNextRound(
2247 - makeEvent( boost::bind(
2248 + makeEvent(::boost::bind(&EventQueue::forceEmpty, ::boost::ref(mrEventQueue)));
2249 + if (mbSkipTriggersNextEffect && bNotifyNextEffect)
2251 + // then simulate a next effect event: this skip effect
2252 + // handler is triggered upon next effect events (multiplexer
2253 + // prio=-1)! Posting a notifyNextEffect() here is only safe
2254 + // (we don't run into busy loop), because we assume that
2255 + // someone has registerered above for next effects
2256 + // (multiplexer prio=0) at the user event queue.
2257 + return mrEventQueue.addEventWhenQueueIsEmpty(
2258 + makeEvent( boost::bind(
2259 &EventMultiplexer::notifyNextEffect,
2260 boost::ref(mrEventMultiplexer) ) ) );
2267 @@ -333,6 +353,7 @@ private:
2269 EventQueue & mrEventQueue;
2270 EventMultiplexer & mrEventMultiplexer;
2271 + bool mbSkipTriggersNextEffect;
2274 class RewindEffectEventHandler : public MouseEventHandler_,
2275 @@ -888,7 +909,9 @@ void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent )
2276 mbAdvanceOnClick ) );
2279 -void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent )
2280 +void UserEventQueue::registerSkipEffectEvent(
2281 + EventSharedPtr const & pEvent,
2282 + const bool bSkipTriggersNextEffect)
2284 if(!mpSkipEffectEventHandler)
2286 @@ -905,6 +928,7 @@ void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent )
2287 // we're called here)
2288 mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick );
2290 + mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect);
2291 mpSkipEffectEventHandler->addEvent( pEvent );
2294 @@ -973,6 +997,14 @@ void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent,
2295 0.0 /* default prio */ ) );
2298 +void UserEventQueue::callSkipEffectEventHandler (void)
2300 + ::boost::shared_ptr<SkipEffectEventHandler> pHandler (
2301 + ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler));
2303 + pHandler->skipEffect();
2306 } // namespace internal
2307 } // namespace presentation
2309 diff --git slideshow/source/inc/eventqueue.hxx slideshow/source/inc/eventqueue.hxx
2310 index de6fe5e..2657fcf 100644
2311 --- slideshow/source/inc/eventqueue.hxx
2312 +++ slideshow/source/inc/eventqueue.hxx
2313 @@ -71,6 +71,13 @@ namespace slideshow
2314 process() are postponed to next process().
2316 bool addEventForNextRound( const EventSharedPtr& event );
2318 + /** Another way to control the order of asynchronous event
2319 + exeqution. Use this method to schedule events that are to
2320 + be executed after all regular events that have no delay,
2321 + even when they schedule new regular events without delay.
2323 + bool addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent);
2325 /** Process the event queue.
2327 @@ -138,6 +145,7 @@ namespace slideshow
2328 ImplQueueType maEvents;
2329 typedef ::std::vector<EventEntry> EventEntryVector;
2330 EventEntryVector maNextEvents;
2331 + ImplQueueType maNextNextEvents;
2332 void process_( bool bFireAllEvents );
2334 // perform timing of events via relative time
2335 diff --git slideshow/source/inc/screenupdater.hxx slideshow/source/inc/screenupdater.hxx
2336 index 26c40a8..10d500b 100644
2337 --- slideshow/source/inc/screenupdater.hxx
2338 +++ slideshow/source/inc/screenupdater.hxx
2339 @@ -111,9 +111,30 @@ namespace slideshow
2341 void requestImmediateUpdate();
2343 + class UpdateLock {public: virtual void Activate (void) = 0; };
2345 + /** Call this method to create a lock instead of calling
2346 + lockUpdates() and unlockUpdates() directly.
2347 + @param bStartLocked
2348 + When <TRUE/> then the UpdateLock is created already
2349 + locked. When <FALSE/> then Activate() has to be called in order
2352 + ::boost::shared_ptr<UpdateLock> createLock (const bool bStartLocked);
2354 + /** Lock updates to prevent intermediate repaints.
2356 + void lockUpdates (void);
2358 + /** When called as often as lockUpdates() then commitUpdates()
2361 + void unlockUpdates (void);
2364 struct ImplScreenUpdater;
2365 boost::scoped_ptr<ImplScreenUpdater> mpImpl;
2370 diff --git slideshow/source/inc/usereventqueue.hxx slideshow/source/inc/usereventqueue.hxx
2371 index fb8026d..c9613fa 100644
2372 --- slideshow/source/inc/usereventqueue.hxx
2373 +++ slideshow/source/inc/usereventqueue.hxx
2374 @@ -188,8 +188,16 @@ public:
2375 Then, all registered events are fired and removed from this
2376 queue. After firing, a next effect event is issued to this
2377 queue to start the next effect.
2379 + The event to execute when skipping the current effect.
2380 + @param bSkipTriggersNextEffect
2381 + When <TRUE/> then after skipping the current effect the next
2382 + effect is triggered. When <FALSE/> then the next effect is not
2385 - void registerSkipEffectEvent( EventSharedPtr const& pEvent );
2386 + void registerSkipEffectEvent(
2387 + EventSharedPtr const& pEvent,
2388 + const bool bSkipTriggersNextEffect);
2390 /** Registes an event that is fired when the current effects(s)
2391 are rewound, .e.g. when the right mouse button is pressed.
2392 @@ -262,7 +270,13 @@ public:
2394 void registerMouseLeaveEvent( const EventSharedPtr& rEvent,
2395 const ShapeSharedPtr& rShape );
2398 + /** Typically skipping the current effect is triggered by mouse clicks
2399 + or key presses that trigger the next effect. This method allows the
2400 + skipping of effects to be triggered programatically.
2402 + void callSkipEffectEventHandler (void);
2405 /** Generically register an event on one of the handlers.