diff options
-rw-r--r-- | slideshow/source/engine/animationnodes/basenode.hxx | 6 | ||||
-rw-r--r-- | slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx | 6 | ||||
-rw-r--r-- | slideshow/source/engine/effectrewinder.cxx | 404 | ||||
-rw-r--r-- | slideshow/source/engine/effectrewinder.hxx | 183 | ||||
-rw-r--r-- | slideshow/source/engine/eventqueue.cxx | 47 | ||||
-rw-r--r-- | slideshow/source/engine/makefile.mk | 1 | ||||
-rw-r--r-- | slideshow/source/engine/screenupdater.cxx | 83 | ||||
-rw-r--r-- | slideshow/source/engine/slideshowimpl.cxx | 272 | ||||
-rw-r--r-- | slideshow/source/engine/usereventqueue.cxx | 54 | ||||
-rw-r--r-- | slideshow/source/inc/eventqueue.hxx | 8 | ||||
-rw-r--r-- | slideshow/source/inc/screenupdater.hxx | 21 | ||||
-rw-r--r-- | slideshow/source/inc/usereventqueue.hxx | 18 |
12 files changed, 1029 insertions, 74 deletions
diff --git a/slideshow/source/engine/animationnodes/basenode.hxx b/slideshow/source/engine/animationnodes/basenode.hxx index a25f6cb92..dbf2f1375 100644 --- a/slideshow/source/engine/animationnodes/basenode.hxx +++ b/slideshow/source/engine/animationnodes/basenode.hxx @@ -136,7 +136,9 @@ public: const AnimationNodeSharedPtr& rNotifee ); // nop: virtual void notifyDeactivating( const AnimationNodeSharedPtr& rNotifier ); - + + bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; } + protected: void scheduleDeactivationEvent( EventSharedPtr const& pEvent = EventSharedPtr() ); @@ -144,8 +146,6 @@ protected: SlideShowContext const& getContext() const { return maContext; } ::boost::shared_ptr<BaseNode> const& getSelf() const { return mpSelf; } - bool isMainSequenceRootNode() const { return mbIsMainSequenceRootNode; } - bool checkValidNode() const { ENSURE_OR_THROW( mpSelf, "no self ptr set!" ); bool const bRet = (meCurrState != INVALID); diff --git a/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx b/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx index de6332114..ab0c49237 100644 --- a/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx +++ b/slideshow/source/engine/animationnodes/sequentialtimecontainer.cxx @@ -88,7 +88,7 @@ void SequentialTimeContainer::skipEffect( if (isChildNode(pChildNode)) { // empty all events ignoring timings => until next effect getContext().mrEventQueue.forceEmpty(); - getContext().mrEventQueue.addEventForNextRound( + getContext().mrEventQueue.addEvent( makeEvent( boost::bind(&AnimationNode::deactivate, pChildNode) ) ); } else @@ -125,7 +125,8 @@ bool SequentialTimeContainer::resolveChild( // deactivate child node when skip event occurs: getContext().mrUserEventQueue.registerSkipEffectEvent( - mpCurrentSkipEvent ); + mpCurrentSkipEvent, + mnFinishedChildren+1<maChildren.size()); // rewind to previous child: getContext().mrUserEventQueue.registerRewindEffectEvent( mpCurrentRewindEvent ); @@ -136,6 +137,7 @@ bool SequentialTimeContainer::resolveChild( void SequentialTimeContainer::notifyDeactivating( AnimationNodeSharedPtr const& rNotifier ) { + OSL_TRACE(" SequentialTimeContainer::notifyDeactivating\r"); if (notifyDeactivatedChild( rNotifier )) return; diff --git a/slideshow/source/engine/effectrewinder.cxx b/slideshow/source/engine/effectrewinder.cxx new file mode 100644 index 000000000..aff21d30d --- /dev/null +++ b/slideshow/source/engine/effectrewinder.cxx @@ -0,0 +1,404 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: slideshowimpl.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "precompiled_slideshow.hxx" + +#include "EffectRewinder.hxx" +#include "eventqueue.hxx" +#include "usereventqueue.hxx" +#include "mouseeventhandler.hxx" +#include "animationnodes/basecontainernode.hxx" +#include "delayevent.hxx" + +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/animations/Event.hpp> +#include <com/sun/star/animations/EventTrigger.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> + +using ::com::sun::star::uno::Reference; +using namespace ::com::sun::star; + +namespace slideshow { namespace internal { + + +namespace { + +class RewinderEventHandler : public EventHandler +{ +public: + typedef ::boost::function<bool(void)> Action; + RewinderEventHandler (const Action& rAction) : maAction(rAction) {} + virtual ~RewinderEventHandler (void) {} +private: + const Action maAction; + virtual bool handleEvent (void) { return maAction(); } +}; + + + +class RewinderAnimationEventHandler : public AnimationEventHandler +{ +public: + typedef ::boost::function<bool(const AnimationNodeSharedPtr& rpNode)> Action; + RewinderAnimationEventHandler (const Action& rAction) : maAction(rAction) {} + virtual ~RewinderAnimationEventHandler (void) {} +private: + const Action maAction; + virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode) + { return maAction(rpNode); } +}; + + + +} // end of anonymous namespace + + +//----- EffectRewinder -------------------------------------------------------------- + +EffectRewinder::EffectRewinder ( + EventMultiplexer& rEventMultiplexer, + EventQueue& rEventQueue, + UserEventQueue& rUserEventQueue) + : mrEventMultiplexer(rEventMultiplexer), + mrEventQueue(rEventQueue), + mrUserEventQueue(rUserEventQueue), + mpSlideStartHandler(), + mpSlideEndHandler(), + mpAnimationStartHandler(), + mnMainSequenceEffectCount(0), + mpAsynchronousRewindEvent(), + mxCurrentAnimationRootNode() +{ + Initialize(); +} + + + + +void EffectRewinder::Initialize (void) +{ + // Add some event handlers so that we are informed when + // a) an animation is started (we then check whether that belongs to a + // main sequence effect and if so, increase the respective counter), + // b,c) a slide was started or ended (in which case the effect counter + // is reset. + + mpAnimationStartHandler.reset( + new RewinderAnimationEventHandler( + ::boost::bind(&EffectRewinder::NotifyAnimationStart, this, _1))); + mrEventMultiplexer.addAnimationStartHandler(mpAnimationStartHandler); + + mpSlideStartHandler.reset( + new RewinderEventHandler( + ::boost::bind(&EffectRewinder::ResetEffectCount, this))); + mrEventMultiplexer.addSlideStartHandler(mpSlideStartHandler); + + mpSlideEndHandler.reset( + new RewinderEventHandler( + ::boost::bind(&EffectRewinder::ResetEffectCount, this))); + mrEventMultiplexer.addSlideEndHandler(mpSlideEndHandler); +} + + + + +EffectRewinder::~EffectRewinder (void) +{ + Dispose(); +} + + + + +void EffectRewinder::Dispose (void) +{ + if (mpAsynchronousRewindEvent) + { + mpAsynchronousRewindEvent->dispose(); + mpAsynchronousRewindEvent.reset(); + } + + if (mpAnimationStartHandler) + { + mrEventMultiplexer.removeAnimationStartHandler(mpAnimationStartHandler); + mpAnimationStartHandler.reset(); + } + + if (mpSlideStartHandler) + { + mrEventMultiplexer.removeSlideStartHandler(mpSlideStartHandler); + mpSlideStartHandler.reset(); + } + + if (mpSlideEndHandler) + { + mrEventMultiplexer.removeSlideEndHandler(mpSlideEndHandler); + mpSlideEndHandler.reset(); + } +} + + + + +void EffectRewinder::SetRootAnimationNode ( + const uno::Reference<animations::XAnimationNode>& xRootNode) +{ + mxCurrentAnimationRootNode = xRootNode; +} + + + + +bool EffectRewinder::Rewind ( + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock, + const ::boost::function<void(void)>& rSlideRewindFunctor, + const ::boost::function<void(void)>& rPreviousSlideFunctor) +{ + mpPaintLock = rpPaintLock; + + // Do not allow nested rewinds. + if (mpAsynchronousRewindEvent) + { + OSL_ASSERT( ! mpAsynchronousRewindEvent); + return false; + } + + // Abort (and skip over the rest of) any currently active animation. + mrUserEventQueue.callSkipEffectEventHandler(); + mrEventQueue.forceEmpty(); + + const int nSkipCount (mnMainSequenceEffectCount - 1); + if (nSkipCount < 0) + { + if ( ! rPreviousSlideFunctor) + { + OSL_ASSERT(rPreviousSlideFunctor); + return false; + } + + // No main sequence effects to rewind on the current slide. + // Go back to the previous slide. + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::AsynchronousRewindToPreviousSlide, + this, + rPreviousSlideFunctor)); + } + else + { + // The actual rewinding is done asynchronously so that we can safely + // call other methods. + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::AsynchronousRewind, + this, + nSkipCount, + true, + rSlideRewindFunctor)); + } + + if (mpAsynchronousRewindEvent) + mrEventQueue.addEvent(mpAsynchronousRewindEvent); + + return mpAsynchronousRewindEvent.get()!=NULL; +} + + + + +void EffectRewinder::SkipAllMainSequenceEffects (void) +{ + // Do not allow nested rewinds. + if (mpAsynchronousRewindEvent) + { + OSL_ASSERT(!mpAsynchronousRewindEvent); + return; + } + + const int nTotalMainSequenceEffectCount (CountMainSequenceEffects()); + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::AsynchronousRewind, + this, + nTotalMainSequenceEffectCount, + false, + ::boost::function<void(void)>())); + mrEventQueue.addEvent(mpAsynchronousRewindEvent); +} + + + + +sal_Int32 EffectRewinder::CountMainSequenceEffects (void) +{ + // Determine the number of main sequence effects. + sal_Int32 nMainSequenceNodeCount (0); + + ::std::queue<uno::Reference<animations::XAnimationNode> > aNodeQueue; + aNodeQueue.push(mxCurrentAnimationRootNode); + while ( ! aNodeQueue.empty()) + { + const uno::Reference<animations::XAnimationNode> xNode (aNodeQueue.front()); + aNodeQueue.pop(); + + // Does the current node belong to the main sequence? + if (xNode.is()) + { + animations::Event aEvent; + if (xNode->getBegin() >>= aEvent) + if (aEvent.Trigger == animations::EventTrigger::ON_NEXT) + ++nMainSequenceNodeCount; + } + + // If the current node is a container then prepare its children for investigation. + uno::Reference<container::XEnumerationAccess> xEnumerationAccess (xNode, uno::UNO_QUERY); + if (xEnumerationAccess.is()) + { + uno::Reference<container::XEnumeration> xEnumeration ( + xEnumerationAccess->createEnumeration()); + if (xEnumeration.is()) + while (xEnumeration->hasMoreElements()) + { + aNodeQueue.push( + uno::Reference<animations::XAnimationNode>( + xEnumeration->nextElement(), uno::UNO_QUERY)); + } + } + } + + return nMainSequenceNodeCount; + + // // Skip all main sequence nodes. + // SkipSomeMainSequenceEffects(nMainSequenceNodeCount); +} + + + + +void EffectRewinder::SkipSomeMainSequenceEffects (sal_Int32 nSkipCount) +{ + while (--nSkipCount >= 0) + SkipSingleMainSequenceEffects(); +} + + + + +void EffectRewinder::SkipSingleMainSequenceEffects (void) +{ + // This basically just starts the next effect and then skips over its + // animation. + mrEventMultiplexer.notifyNextEffect(); + mrEventQueue.forceEmpty(); + mrUserEventQueue.callSkipEffectEventHandler(); + mrEventQueue.forceEmpty(); +} + + + + +bool EffectRewinder::ResetEffectCount (void) +{ + mnMainSequenceEffectCount = 0; + return false; +} + + + + +bool EffectRewinder::NotifyAnimationStart (const AnimationNodeSharedPtr& rpNode) +{ + // This notification is only relevant for us when the rpNode belongs to + // the main sequence. + BaseNodeSharedPtr pBaseNode (::boost::dynamic_pointer_cast<BaseNode>(rpNode)); + if (pBaseNode) + { + BaseContainerNodeSharedPtr pParent (pBaseNode->getParentNode()); + if (pParent && pParent->isMainSequenceRootNode()) + { + ++mnMainSequenceEffectCount; + } + } + return false; +} + + + + +void EffectRewinder::AsynchronousRewind ( + sal_Int32 nEffectCount, + const bool bRedisplayCurrentSlide, + const boost::function<void(void)>& rSlideRewindFunctor) +{ + OSL_ASSERT(mpAsynchronousRewindEvent); + + if (bRedisplayCurrentSlide) + { + mpPaintLock->Activate(); + // Re-display the current slide. + if (rSlideRewindFunctor) + rSlideRewindFunctor(); + mpAsynchronousRewindEvent = makeEvent( + ::boost::bind( + &EffectRewinder::AsynchronousRewind, + this, + nEffectCount, + false, + rSlideRewindFunctor)); + mrEventQueue.addEventForNextRound(mpAsynchronousRewindEvent); + } + else + { + mrEventQueue.forceEmpty(); + while (--nEffectCount >= 0) + SkipSingleMainSequenceEffects(); + + mpAsynchronousRewindEvent.reset(); + mpPaintLock.reset(); + } +} + + + + +void EffectRewinder::AsynchronousRewindToPreviousSlide ( + const ::boost::function<void(void)>& rSlideRewindFunctor) +{ + OSL_ASSERT(mpAsynchronousRewindEvent); + + mpAsynchronousRewindEvent.reset(); + rSlideRewindFunctor(); +} + + +} } // end of namespace ::slideshow::internal diff --git a/slideshow/source/engine/effectrewinder.hxx b/slideshow/source/engine/effectrewinder.hxx new file mode 100644 index 000000000..b9ff08a8c --- /dev/null +++ b/slideshow/source/engine/effectrewinder.hxx @@ -0,0 +1,183 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: slideshowimpl.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX +#define INCLUDED_SLIDESHOW_EFFECT_REWINDER_HXX + +#include "animationnode.hxx" +#include "eventhandler.hxx" +#include "animationeventhandler.hxx" +#include "event.hxx" +#include "screenupdater.hxx" + +#include <com/sun/star/presentation/XSlideShow.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/function.hpp> +#include <vector> + +namespace css = ::com::sun::star; + +namespace slideshow { namespace internal { + +class EventMultiplexer; +class EventQueue; +class UserEventQueue; + +/** Rewind single effects of the main effect sequence. A rewind is + initiated by calling the Rewind() method. Part of the processing is + done asynchronously. Multiple EventQueue::update() calls may be + necessary to finish a rewind. + + Remember to call SetRootAnimationNode() when switching to a different + slide so that the EffectRewinder can determine the number of main + sequence effects. +*/ +class EffectRewinder +{ +public: + EffectRewinder ( + EventMultiplexer& rEventMultiplexer, + EventQueue& rEventQueue, + UserEventQueue& rUserEventQueue); + ~EffectRewinder (void); + + /** Call Dispose() before the ownder of an EffectRewinder object dies so + that the EffectRewinder can release all references to the owner. + + */ + void Dispose (void); + + /** Store the root node of the animation tree. It is used in + CountMainSequenceEffects() to count the number of main sequence + effects (or effect groups.) + */ + void SetRootAnimationNode ( + const css::uno::Reference<css::animations::XAnimationNode>& xRootNode); + + /** Rewind one effect of the main effect sequence. When the current + slide has not effects or no main sequence effect has yet been played + then switch to the previous slide and replay all of its main + sequence effects. + The caller has to pass two functors that redisplay the current slide + or switch to the previous slide so that it does not have to expose + its internals to us. Only one of the two functors is called. + @param rpPaintLock + This paint lock is released after the whole asynchronous + procoess of rewinding the current effect is completed. It + prevents intermediate repaints that would show partial replay + of effects. + @param rSlideRewindFunctor + This functor is called when the current slide is to be + redisplayed. When it is called then the other functor is not + called. + @param rPreviousSlideFunctor + This functor is called to switch to the previous slide. When it + is called then the other functor is not called. + */ + bool Rewind ( + const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock, + const ::boost::function<void(void)>& rSlideRewindFunctor, + const ::boost::function<void(void)>& rPreviousSlideFunctor); + + /** Call this method after gotoPreviousEffect() triggered a slide change + to the previous slide. + */ + void SkipAllMainSequenceEffects (void); + +private: + EventMultiplexer& mrEventMultiplexer; + EventQueue& mrEventQueue; + UserEventQueue& mrUserEventQueue; + + EventHandlerSharedPtr mpSlideStartHandler; + EventHandlerSharedPtr mpSlideEndHandler; + AnimationEventHandlerSharedPtr mpAnimationStartHandler; + + /** The number off main sequence effects so far. + */ + sal_Int32 mnMainSequenceEffectCount; + + /** This is the currently scheduled event that executes the asynchronous + part of the effect rewinding. It is also used as flag that prevents + nested rewinds. + */ + EventSharedPtr mpAsynchronousRewindEvent; + + css::uno::Reference<css::animations::XAnimationNode> mxCurrentAnimationRootNode; + ::boost::shared_ptr<ScreenUpdater::UpdateLock> mpPaintLock; + + void Initialize (void); + + bool ResetEffectCount (void); + /** Called by listeners when an animation (not necessarily of a main + sequence effect) starts. + */ + bool NotifyAnimationStart (const AnimationNodeSharedPtr& rpNode); + + /** Count the number of effects (or effect groups) in the main effect + sequence. + */ + sal_Int32 CountMainSequenceEffects (void); + + /** Skip the next main sequence effect. + */ + void SkipSingleMainSequenceEffects (void); + + /** Skip the specified number of main sequence effects. + */ + void SkipSomeMainSequenceEffects (const sal_Int32 nSkipCount); + + /** Rewind the last effect of the main effect sequence by replaying all + previous effects. + @param nEffectCount + The number of main sequence effects to replay. + @param bRedisplayCurrentSlide + When <TRUE/> then the current slide is redisplayed before the + effects are replayed. + @param rSlideRewindFunctor + This functor is used to redisplay the current slide. + */ + void AsynchronousRewind ( + sal_Int32 nEffectCount, + const bool bRedisplayCurrentSlide, + const boost::function<void(void)>& rSlideRewindFunctor); + + /** Go to the previous slide and replay all of its main sequence effects + (or effect groups). + @param rPreviousSlideFunctor + This functor is used to go to the previous slide. + */ + void AsynchronousRewindToPreviousSlide ( + const ::boost::function<void(void)>& rPreviousSlideFunctor); +}; + +} } // end of namespace ::slideshow::internal + +#endif diff --git a/slideshow/source/engine/eventqueue.cxx b/slideshow/source/engine/eventqueue.cxx index cd1eb3789..087d4d5d7 100644 --- a/slideshow/source/engine/eventqueue.cxx +++ b/slideshow/source/engine/eventqueue.cxx @@ -66,6 +66,7 @@ namespace slideshow : maMutex(), maEvents(), maNextEvents(), + maNextNextEvents(), mpTimer( pPresTimer ) { } @@ -131,6 +132,22 @@ namespace slideshow mpTimer->getElapsedTime()) ) ); return true; } + + bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent) + { + ::osl::MutexGuard aGuard( maMutex ); + + ENSURE_OR_RETURN( + rpEvent.get() != NULL, + "EventQueue::addEvent: event ptr NULL"); + + maNextNextEvents.push( + EventEntry( + rpEvent, + rpEvent->getActivationTime(mpTimer->getElapsedTime()))); + + return true; + } void EventQueue::forceEmpty() { @@ -157,12 +174,23 @@ namespace slideshow maEvents.push(*iPos); } EventEntryVector().swap( maNextEvents ); - + // perform topmost, ready-to-execute event // ======================================= const double nCurrTime( mpTimer->getElapsedTime() ); - + + // When maEvents does not contain any events that are due now + // then process one event from maNextNextEvents. + if (!maNextNextEvents.empty() + && !bFireAllEvents + && (maEvents.empty() || maEvents.top().nTime > nCurrTime)) + { + const EventEntry aEvent (maNextNextEvents.top()); + maNextNextEvents.pop(); + maEvents.push(aEvent); + } + // process ready/elapsed events. Note that the 'perceived' // current time remains constant for this loop, thus we're // processing only those events which where ready when we @@ -243,7 +271,7 @@ namespace slideshow { ::osl::MutexGuard aGuard( maMutex ); - return maEvents.empty(); + return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty(); } double EventQueue::nextTimeout() const @@ -251,9 +279,16 @@ namespace slideshow ::osl::MutexGuard aGuard( maMutex ); // return time for next entry (if any) - return isEmpty() ? - ::std::numeric_limits<double>::max() : - maEvents.top().nTime - mpTimer->getElapsedTime(); + double nTimeout (::std::numeric_limits<double>::max()); + const double nCurrentTime (mpTimer->getElapsedTime()); + if ( ! maEvents.empty()) + nTimeout = maEvents.top().nTime - nCurrentTime; + if ( ! maNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime); + if ( ! maNextNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime); + + return nTimeout; } void EventQueue::clear() diff --git a/slideshow/source/engine/makefile.mk b/slideshow/source/engine/makefile.mk index ba00e028b..8316355c6 100644 --- a/slideshow/source/engine/makefile.mk +++ b/slideshow/source/engine/makefile.mk @@ -70,6 +70,7 @@ SLOFILES = $(SLO)$/activitiesqueue.obj \ $(SLO)$/attributemap.obj \ $(SLO)$/color.obj \ $(SLO)$/delayevent.obj \ + $(SLO)$/effectrewinder.obj \ $(SLO)$/eventmultiplexer.obj \ $(SLO)$/eventqueue.obj \ $(SLO)$/expressionnodefactory.obj \ diff --git a/slideshow/source/engine/screenupdater.cxx b/slideshow/source/engine/screenupdater.cxx index bf3ca0c5b..8cfaaddf2 100644 --- a/slideshow/source/engine/screenupdater.cxx +++ b/slideshow/source/engine/screenupdater.cxx @@ -36,6 +36,19 @@ #include <vector> #include <algorithm> +namespace { + class UpdateLock : public ::slideshow::internal::ScreenUpdater::UpdateLock + { + public: + UpdateLock (::slideshow::internal::ScreenUpdater& rUpdater, const bool bStartLocked); + virtual ~UpdateLock (void); + virtual void Activate (void); + private: + ::slideshow::internal::ScreenUpdater& mrUpdater; + bool mbIsActivated; + }; +} + namespace slideshow { namespace internal @@ -64,12 +77,16 @@ namespace internal /// True, if at least one notifyUpdate() call had bViewClobbered set bool mbViewClobbered; + /// The screen is updated only when mnLockCount==0 + sal_Int32 mnLockCount; + explicit ImplScreenUpdater( UnoViewContainer const& rViewContainer ) : maUpdaters(), maViewUpdateRequests(), mrViewContainer(rViewContainer), mbUpdateAllRequest(false), - mbViewClobbered(false) + mbViewClobbered(false), + mnLockCount(0) {} }; @@ -100,6 +117,9 @@ namespace internal void ScreenUpdater::commitUpdates() { + if (mpImpl->mnLockCount > 0) + return; + // cases: // // (a) no update necessary at all @@ -178,6 +198,9 @@ namespace internal void ScreenUpdater::requestImmediateUpdate() { + if (mpImpl->mnLockCount > 0) + return; + // TODO(F2): This will interfere with other updates, since it // happens out-of-sync with main animation loop. Might cause // artifacts. @@ -186,5 +209,63 @@ namespace internal boost::mem_fn(&View::updateScreen) ); } + void ScreenUpdater::lockUpdates (void) + { + ++mpImpl->mnLockCount; + OSL_ASSERT(mpImpl->mnLockCount>0); + } + + void ScreenUpdater::unlockUpdates (void) + { + OSL_ASSERT(mpImpl->mnLockCount>0); + if (mpImpl->mnLockCount > 0) + { + --mpImpl->mnLockCount; + if (mpImpl->mnLockCount) + commitUpdates(); + } + } + + ::boost::shared_ptr<ScreenUpdater::UpdateLock> ScreenUpdater::createLock (const bool bStartLocked) + { + return ::boost::shared_ptr<ScreenUpdater::UpdateLock>(new ::UpdateLock(*this, bStartLocked)); + } + + } // namespace internal } // namespace slideshow + +namespace { + +UpdateLock::UpdateLock ( + ::slideshow::internal::ScreenUpdater& rUpdater, + const bool bStartLocked) + : mrUpdater(rUpdater), + mbIsActivated(false) +{ + if (bStartLocked) + Activate(); +} + + + + +UpdateLock::~UpdateLock (void) +{ + if (mbIsActivated) + mrUpdater.unlockUpdates(); +} + + + + +void UpdateLock::Activate (void) +{ + if ( ! mbIsActivated) + { + mbIsActivated = true; + mrUpdater.lockUpdates(); + } +} + +} diff --git a/slideshow/source/engine/slideshowimpl.cxx b/slideshow/source/engine/slideshowimpl.cxx index 94f89a3f5..b608acf4e 100644 --- a/slideshow/source/engine/slideshowimpl.cxx +++ b/slideshow/source/engine/slideshowimpl.cxx @@ -92,6 +92,7 @@ #include "slidebitmap.hxx" #include "rehearsetimingsactivity.hxx" #include "waitsymbol.hxx" +#include "effectrewinder.hxx" #include <boost/noncopyable.hpp> #include <boost/bind.hpp> @@ -193,7 +194,7 @@ public: This method notifies the end of the third phase. */ - void notifySlideEnded(); + void notifySlideEnded (const bool bReverse); /** Notification from eventmultiplexer that a hyperlink has been clicked. @@ -208,6 +209,7 @@ public: private: // XSlideShow: virtual sal_Bool SAL_CALL nextEffect() throw (uno::RuntimeException); + virtual sal_Bool SAL_CALL previousEffect() throw (uno::RuntimeException); virtual sal_Bool SAL_CALL startShapeActivity( uno::Reference<drawing::XShape> const& xShape ) throw (uno::RuntimeException); @@ -258,6 +260,12 @@ private: virtual bool requestCursor( sal_Int16 nCursorShape ); virtual void resetCursor(); + /** This is somewhat similar to displaySlide when called for the current + slide. It has been simplified to take advantage of that no slide + change takes place. Furthermore it does not show the slide + transition. + */ + void redisplayCurrentSlide (void); protected: // WeakComponentImplHelperBase @@ -313,12 +321,32 @@ private: const SlideSharedPtr& rEnteringSlide, const EventSharedPtr& rTransitionEndEvent ); - /// Display/hide wait symbol on all views - void setWaitState( bool bOn ); + /** Request/release the wait symbol. The wait symbol is displayed when + there are more requests then releases. Locking the wait symbol + helps to avoid intermediate repaints. + + Do not call this method directly. Use WaitSymbolLock instead. + */ + void requestWaitSymbol (void); + void releaseWaitSymbol (void); + + class WaitSymbolLock {public: + WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl) + { mrSlideShowImpl.requestWaitSymbol(); } + ~WaitSymbolLock(void) + { mrSlideShowImpl.releaseWaitSymbol(); } + private: SlideShowImpl& mrSlideShowImpl; + }; + /// Filter requested cursor shape against hard slideshow cursors (wait, etc.) sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const; + /** This method is called asynchronously to finish the rewinding of an + effect to the previous slide that was initiated earlier. + */ + void rewindEffectToPreviousSlide (void); + /// all registered views UnoViewContainer maViewContainer; @@ -365,7 +393,7 @@ private: sal_Int16 mnCurrentCursor; - bool mbWaitState; + sal_Int32 mnWaitSymbolRequestCount; bool mbAutomaticAdvancementMode; bool mbImageAnimationsAllowed; bool mbNoSlideTransitions; @@ -374,6 +402,8 @@ private: bool mbShowPaused; bool mbSlideShowIdle; bool mbDisableAnimationZOrder; + + EffectRewinder maEffectRewinder; }; @@ -465,7 +495,7 @@ SlideShowImpl::SlideShowImpl( mxPrefetchSlide(), mxPrefetchAnimationNode(), mnCurrentCursor(awt::SystemPointer::ARROW), - mbWaitState(false), + mnWaitSymbolRequestCount(0), mbAutomaticAdvancementMode(false), mbImageAnimationsAllowed( true ), mbNoSlideTransitions( false ), @@ -473,7 +503,8 @@ SlideShowImpl::SlideShowImpl( mbForceManualAdvance( false ), mbShowPaused( false ), mbSlideShowIdle( true ), - mbDisableAnimationZOrder( false ) + mbDisableAnimationZOrder( false ), + maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue) { // keep care not constructing any UNO references to this inside ctor, // shift that code to create()! @@ -507,6 +538,8 @@ void SlideShowImpl::disposing() { osl::MutexGuard const guard( m_aMutex ); + maEffectRewinder.Dispose(); + // stop slide transition sound, if any: stopSlideTransitionSound(); @@ -607,7 +640,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition( const uno::Reference< drawing::XDrawPage >& xDrawPage, const SlideSharedPtr& rLeavingSlide, const SlideSharedPtr& rEnteringSlide, - const EventSharedPtr& rTransitionEndEvent ) + const EventSharedPtr& rTransitionEndEvent) { ENSURE_OR_THROW( !maViewContainer.empty(), "createSlideTransition(): No views" ); @@ -692,7 +725,7 @@ ActivitySharedPtr SlideShowImpl::createSlideTransition( bTransitionDirection, aTransitionFadeColor, resetSlideTransitionSound( aSound, bLoopSound ) )); - + if( !pTransition ) return ActivitySharedPtr(); // no transition effect has been // generated. Normally, that means @@ -778,20 +811,43 @@ SlideSharedPtr SlideShowImpl::makeSlide( return pSlide; } -void SlideShowImpl::setWaitState( bool bOn ) +void SlideShowImpl::requestWaitSymbol (void) { - mbWaitState = bOn; - if( !mpWaitSymbol ) // fallback to cursor - requestCursor(awt::SystemPointer::WAIT); - else if( mbWaitState ) - mpWaitSymbol->show(); - else - mpWaitSymbol->hide(); + ++mnWaitSymbolRequestCount; + OSL_ASSERT(mnWaitSymbolRequestCount>0); + + if (mnWaitSymbolRequestCount == 1) + { + if( !mpWaitSymbol ) + { + // fall back to cursor + requestCursor(calcActiveCursor(mnCurrentCursor)); + } + else + mpWaitSymbol->show(); + } +} + +void SlideShowImpl::releaseWaitSymbol (void) +{ + --mnWaitSymbolRequestCount; + OSL_ASSERT(mnWaitSymbolRequestCount>=0); + + if (mnWaitSymbolRequestCount == 0) + { + if( !mpWaitSymbol ) + { + // fall back to cursor + requestCursor(calcActiveCursor(mnCurrentCursor)); + } + else + mpWaitSymbol->hide(); + } } sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const { - if( mbWaitState && !mpWaitSymbol ) // enforce wait cursor + if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor nCursorShape = awt::SystemPointer::WAIT; else if( !mbMouseVisible ) // enforce INVISIBLE nCursorShape = awt::SystemPointer::INVISIBLE; @@ -835,10 +891,19 @@ void SlideShowImpl::stopShow() } } -struct SlideShowImpl::PrefetchPropertiesFunc + + +class SlideShowImpl::PrefetchPropertiesFunc { - SlideShowImpl *const that; - PrefetchPropertiesFunc( SlideShowImpl * that_ ) : that(that_) {} +public: + PrefetchPropertiesFunc( SlideShowImpl * that_, + bool& rbSkipAllMainSequenceEffects, + bool& rbSkipSlideTransition) + : mpSlideShowImpl(that_), + mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects), + mrbSkipSlideTransition(rbSkipSlideTransition) + {} + void operator()( beans::PropertyValue const& rProperty ) const { if (rProperty.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("Prefetch") )) @@ -846,16 +911,30 @@ struct SlideShowImpl::PrefetchPropertiesFunc uno::Sequence<uno::Any> seq; if ((rProperty.Value >>= seq) && seq.getLength() == 2) { - seq[0] >>= that->mxPrefetchSlide; - seq[1] >>= that->mxPrefetchAnimationNode; + seq[0] >>= mpSlideShowImpl->mxPrefetchSlide; + seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode; } } + else if (rProperty.Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") )) + { + rProperty.Value >>= mrbSkipAllMainSequenceEffects; + } + else if (rProperty.Name.equalsAsciiL( + RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") )) + { + rProperty.Value >>= mrbSkipSlideTransition; + } else { OSL_ENSURE( false, rtl::OUStringToOString( rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() ); } } +private: + SlideShowImpl *const mpSlideShowImpl; + bool& mrbSkipAllMainSequenceEffects; + bool& mrbSkipSlideTransition; }; void SlideShowImpl::displaySlide( @@ -868,7 +947,9 @@ void SlideShowImpl::displaySlide( if (isDisposed()) return; - + + maEffectRewinder.SetRootAnimationNode(xRootNode); + // precondition: must only be called from the main thread! DBG_TESTSOLARMUTEX(); @@ -880,20 +961,20 @@ void SlideShowImpl::displaySlide( // unconditionally. Otherwise, genuine // shape animations (drawing layer and // GIF) will not be stopped. - + + bool bSkipAllMainSequenceEffects (false); + bool bSkipSlideTransition (false); std::for_each( rProperties.getConstArray(), rProperties.getConstArray() + rProperties.getLength(), - PrefetchPropertiesFunc(this) ); + PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) ); OSL_ENSURE( !maViewContainer.empty(), "### no views!" ); if (maViewContainer.empty()) return; - + // this here might take some time { - comphelper::ScopeGuard const scopeGuard( - boost::bind( &SlideShowImpl::setWaitState, this, false ) ); - setWaitState(true); + WaitSymbolLock aLock (*this); mpPreviousSlide = mpCurrentSlide; mpCurrentSlide.reset(); @@ -935,15 +1016,25 @@ void SlideShowImpl::displaySlide( // create slide transition, and add proper end event // (which then starts the slide effects // via CURRENT_SLIDE.show()) - ActivitySharedPtr const pSlideChangeActivity( - createSlideTransition( mpCurrentSlide->getXDrawPage(), - mpPreviousSlide, - mpCurrentSlide, - makeEvent( - boost::bind( - &SlideShowImpl::notifySlideTransitionEnded, - this, - false )))); + ActivitySharedPtr pSlideChangeActivity ( + createSlideTransition( + mpCurrentSlide->getXDrawPage(), + mpPreviousSlide, + mpCurrentSlide, + makeEvent( + boost::bind( + &SlideShowImpl::notifySlideTransitionEnded, + this, + false )))); + + if (bSkipSlideTransition) + { + // The transition activity was created for the side effects + // (like sound transitions). Because we want to skip the + // acutual transition animation we do not need the activity + // anymore. + pSlideChangeActivity.reset(); + } if (pSlideChangeActivity) { @@ -967,6 +1058,43 @@ void SlideShowImpl::displaySlide( maEventMultiplexer.notifySlideTransitionStarted(); maListenerContainer.forEach<presentation::XSlideShowListener>( boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) ); + + // We are currently rewinding an effect. This lead us from the next + // slide to this one. To complete this we have to play back all main + // sequence effects on this slide. + if (bSkipAllMainSequenceEffects) + maEffectRewinder.SkipAllMainSequenceEffects(); +} + +void SlideShowImpl::redisplayCurrentSlide (void) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return; + + // precondition: must only be called from the main thread! + DBG_TESTSOLARMUTEX(); + stopShow(); + bool bSkipAllMainSequenceEffects (false); + bool bSkipSlideTransition (true); + + OSL_ENSURE( !maViewContainer.empty(), "### no views!" ); + if (maViewContainer.empty()) + return; + + // No transition effect on this slide - schedule slide + // effect start event right away. + maEventQueue.addEvent( + makeEvent( + boost::bind( + &SlideShowImpl::notifySlideTransitionEnded, + this, + true ))); + + maEventMultiplexer.notifySlideTransitionStarted(); + maListenerContainer.forEach<presentation::XSlideShowListener>( + boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) ); } sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException) @@ -985,6 +1113,50 @@ sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException) return maEventMultiplexer.notifyNextEffect(); } + +sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException) +{ + osl::MutexGuard const guard( m_aMutex ); + + if (isDisposed()) + return false; + + // precondition: must only be called from the main thread! + DBG_TESTSOLARMUTEX(); + + if (mbShowPaused) + return true; + else + { + return maEffectRewinder.Rewind( + maScreenUpdater.createLock(false), + ::boost::bind(&SlideShowImpl::redisplayCurrentSlide, this), + ::boost::bind(&SlideShowImpl::rewindEffectToPreviousSlide, this)); + } +} + +void SlideShowImpl::rewindEffectToPreviousSlide (void) +{ + // Show the wait symbol now and prevent it from showing temporary slide + // content while effects are played back. + WaitSymbolLock aLock (*this); + + // A previous call to EffectRewinder::Rewind could not rewind the current + // effect because there are no effects on the current slide or none has + // yet been displayed. Go to the previous slide. + notifySlideEnded(true); + + // Process pending events once more in order to have the following + // screen update show the last effect. Not sure whether this should be + // necessary. + maEventQueue.forceEmpty(); + + // We have to call the screen updater before the wait symbol is turned + // off. Otherwise the wait symbol would force the display of an + // intermediate state of the slide (before the effects are replayed.) + maScreenUpdater.commitUpdates(); +} + sal_Bool SlideShowImpl::startShapeActivity( uno::Reference<drawing::XShape> const& /*xShape*/ ) throw (uno::RuntimeException) @@ -1658,7 +1830,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() // schedule a slide end event, with automatic mode's // delay aNotificationEvents = makeInterruptableDelay( - boost::bind( &SlideShowImpl::notifySlideEnded, this ), + boost::bind( &SlideShowImpl::notifySlideEnded, this, false ), maEventMultiplexer.getAutomaticTimeout() ); } else @@ -1683,7 +1855,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() bHasAutomaticNextSlide ) { aNotificationEvents = makeInterruptableDelay( - boost::bind( &SlideShowImpl::notifySlideEnded, this ), + boost::bind( &SlideShowImpl::notifySlideEnded, this, false ), nAutomaticNextSlideTimeout); // TODO(F2): Provide a mechanism to let the user override @@ -1700,7 +1872,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() // timeout involved. aNotificationEvents.mpImmediateEvent = makeEvent( boost::bind( - &SlideShowImpl::notifySlideEnded, this ) ); + &SlideShowImpl::notifySlideEnded, this, false ) ); } } @@ -1721,9 +1893,7 @@ void SlideShowImpl::notifySlideAnimationsEnded() // change setup time a lot). Show the wait cursor, this // indeed might take some seconds. { - comphelper::ScopeGuard const scopeGuard( - boost::bind( &SlideShowImpl::setWaitState, this, false ) ); - setWaitState(true); + WaitSymbolLock aLock (*this); if (! matches( mpPrefetchSlide, mxPrefetchSlide, mxPrefetchAnimationNode )) @@ -1745,13 +1915,13 @@ void SlideShowImpl::notifySlideAnimationsEnded() boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded ) ); } -void SlideShowImpl::notifySlideEnded() +void SlideShowImpl::notifySlideEnded (const bool bReverse) { osl::MutexGuard const guard( m_aMutex ); OSL_ENSURE( !isDisposed(), "### already disposed!" ); - if (mpRehearseTimingsActivity) + if (mpRehearseTimingsActivity && !bReverse) { const double time = mpRehearseTimingsActivity->stop(); if (mpRehearseTimingsActivity->hasBeenClicked()) @@ -1771,8 +1941,9 @@ void SlideShowImpl::notifySlideEnded() } } } - - maEventMultiplexer.notifySlideEndEvent(); + + if (bReverse) + maEventMultiplexer.notifySlideEndEvent(); stopShow(); // MUST call that: results in // maUserEventQueue.clear(). What's more, @@ -1784,7 +1955,10 @@ void SlideShowImpl::notifySlideEnded() // GIF) will not be stopped. maListenerContainer.forEach<presentation::XSlideShowListener>( - boost::mem_fn( &presentation::XSlideShowListener::slideEnded ) ); + boost::bind( + &presentation::XSlideShowListener::slideEnded, + _1, + bReverse) ); } bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString const& hyperLink ) diff --git a/slideshow/source/engine/usereventqueue.cxx b/slideshow/source/engine/usereventqueue.cxx index 1ead45a9d..774fd0acb 100644 --- a/slideshow/source/engine/usereventqueue.cxx +++ b/slideshow/source/engine/usereventqueue.cxx @@ -306,26 +306,46 @@ public: EventMultiplexer & rEventMultiplexer ) : ClickEventHandler(rEventQueue), mrEventQueue(rEventQueue), - mrEventMultiplexer(rEventMultiplexer) {} + mrEventMultiplexer(rEventMultiplexer), + mbSkipTriggersNextEffect(true) {} + + /** Remember to trigger (or not to trigger) the next effect after the + current effect is skiped. + */ + void setSkipTriggersNextEffect (const bool bSkipTriggersNextEffect) + { mbSkipTriggersNextEffect = bSkipTriggersNextEffect; } + + /// Skip the current effect but do not triggere the next effect. + void skipEffect (void) { handleEvent_impl(false); } private: virtual bool handleEvent_impl() { + return handleEvent_impl(true); + } + + bool handleEvent_impl (bool bNotifyNextEffect) + { // fire all events, so animation nodes can register their // next effect listeners: if(fireAllEvents( maEvents, mrEventQueue )) { - // then simulate a next effect event: - // this skip effect handler is triggered upon next effect - // events (multiplexer prio=-1)! - // Posting a notifyNextEffect() here is only safe - // (we don't run into busy loop), because we assume that - // someone has registerered above for next effects - // (multiplexer prio=0) at the user event queue. - return mrEventQueue.addEventForNextRound( - makeEvent( boost::bind( + makeEvent(::boost::bind(&EventQueue::forceEmpty, ::boost::ref(mrEventQueue))); + if (mbSkipTriggersNextEffect && bNotifyNextEffect) + { + // then simulate a next effect event: this skip effect + // handler is triggered upon next effect events (multiplexer + // prio=-1)! Posting a notifyNextEffect() here is only safe + // (we don't run into busy loop), because we assume that + // someone has registerered above for next effects + // (multiplexer prio=0) at the user event queue. + return mrEventQueue.addEventWhenQueueIsEmpty( + makeEvent( boost::bind( &EventMultiplexer::notifyNextEffect, boost::ref(mrEventMultiplexer) ) ) ); + } + else + return true; } return false; } @@ -333,6 +353,7 @@ private: private: EventQueue & mrEventQueue; EventMultiplexer & mrEventMultiplexer; + bool mbSkipTriggersNextEffect; }; class RewindEffectEventHandler : public MouseEventHandler_, @@ -888,7 +909,9 @@ void UserEventQueue::registerNextEffectEvent( const EventSharedPtr& rEvent ) mbAdvanceOnClick ) ); } -void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent ) +void UserEventQueue::registerSkipEffectEvent( + EventSharedPtr const & pEvent, + const bool bSkipTriggersNextEffect) { if(!mpSkipEffectEventHandler) { @@ -905,6 +928,7 @@ void UserEventQueue::registerSkipEffectEvent( EventSharedPtr const & pEvent ) // we're called here) mpSkipEffectEventHandler->setAdvanceOnClick( mbAdvanceOnClick ); } + mpSkipEffectEventHandler->setSkipTriggersNextEffect(bSkipTriggersNextEffect); mpSkipEffectEventHandler->addEvent( pEvent ); } @@ -973,6 +997,14 @@ void UserEventQueue::registerMouseLeaveEvent( const EventSharedPtr& rEvent, 0.0 /* default prio */ ) ); } +void UserEventQueue::callSkipEffectEventHandler (void) +{ + ::boost::shared_ptr<SkipEffectEventHandler> pHandler ( + ::boost::dynamic_pointer_cast<SkipEffectEventHandler>(mpSkipEffectEventHandler)); + if (pHandler) + pHandler->skipEffect(); +} + } // namespace internal } // namespace presentation diff --git a/slideshow/source/inc/eventqueue.hxx b/slideshow/source/inc/eventqueue.hxx index 3d71887e8..9390ce0a2 100644 --- a/slideshow/source/inc/eventqueue.hxx +++ b/slideshow/source/inc/eventqueue.hxx @@ -71,6 +71,13 @@ namespace slideshow process() are postponed to next process(). */ bool addEventForNextRound( const EventSharedPtr& event ); + + /** Another way to control the order of asynchronous event + exeqution. Use this method to schedule events that are to + be executed after all regular events that have no delay, + even when they schedule new regular events without delay. + */ + bool addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent); /** Process the event queue. @@ -138,6 +145,7 @@ namespace slideshow ImplQueueType maEvents; typedef ::std::vector<EventEntry> EventEntryVector; EventEntryVector maNextEvents; + ImplQueueType maNextNextEvents; void process_( bool bFireAllEvents ); // perform timing of events via relative time diff --git a/slideshow/source/inc/screenupdater.hxx b/slideshow/source/inc/screenupdater.hxx index 26c40a8ef..10d500b88 100644 --- a/slideshow/source/inc/screenupdater.hxx +++ b/slideshow/source/inc/screenupdater.hxx @@ -111,9 +111,30 @@ namespace slideshow */ void requestImmediateUpdate(); + class UpdateLock {public: virtual void Activate (void) = 0; }; + + /** Call this method to create a lock instead of calling + lockUpdates() and unlockUpdates() directly. + @param bStartLocked + When <TRUE/> then the UpdateLock is created already + locked. When <FALSE/> then Activate() has to be called in order + to lock the lock. + */ + ::boost::shared_ptr<UpdateLock> createLock (const bool bStartLocked); + + /** Lock updates to prevent intermediate repaints. + */ + void lockUpdates (void); + + /** When called as often as lockUpdates() then commitUpdates() + is called. + */ + void unlockUpdates (void); + private: struct ImplScreenUpdater; boost::scoped_ptr<ImplScreenUpdater> mpImpl; + }; } } diff --git a/slideshow/source/inc/usereventqueue.hxx b/slideshow/source/inc/usereventqueue.hxx index fb8026d67..c9613faa9 100644 --- a/slideshow/source/inc/usereventqueue.hxx +++ b/slideshow/source/inc/usereventqueue.hxx @@ -188,8 +188,16 @@ public: Then, all registered events are fired and removed from this queue. After firing, a next effect event is issued to this queue to start the next effect. + @param pEvent + The event to execute when skipping the current effect. + @param bSkipTriggersNextEffect + When <TRUE/> then after skipping the current effect the next + effect is triggered. When <FALSE/> then the next effect is not + triggered. */ - void registerSkipEffectEvent( EventSharedPtr const& pEvent ); + void registerSkipEffectEvent( + EventSharedPtr const& pEvent, + const bool bSkipTriggersNextEffect); /** Registes an event that is fired when the current effects(s) are rewound, .e.g. when the right mouse button is pressed. @@ -262,7 +270,13 @@ public: */ void registerMouseLeaveEvent( const EventSharedPtr& rEvent, const ShapeSharedPtr& rShape ); - + + /** Typically skipping the current effect is triggered by mouse clicks + or key presses that trigger the next effect. This method allows the + skipping of effects to be triggered programatically. + */ + void callSkipEffectEventHandler (void); + private: /** Generically register an event on one of the handlers. |