/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "effectrewinder.hxx" #include "eventqueue.hxx" #include "usereventqueue.hxx" #include "mouseeventhandler.hxx" #include "animationnodes/basecontainernode.hxx" #include "delayevent.hxx" #include #include #include #include #include #include #include using ::com::sun::star::uno::Reference; using namespace ::com::sun::star; namespace slideshow { namespace internal { namespace { class RewinderEventHandler : public EventHandler { public: typedef ::boost::function Action; RewinderEventHandler (const Action& rAction) : maAction(rAction) {} virtual ~RewinderEventHandler (void) {} private: const Action maAction; virtual bool handleEvent (void) SAL_OVERRIDE { return maAction(); } }; class RewinderAnimationEventHandler : public AnimationEventHandler { public: typedef ::boost::function Action; RewinderAnimationEventHandler (const Action& rAction) : maAction(rAction) {} virtual ~RewinderAnimationEventHandler (void) {} private: const Action maAction; virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode) SAL_OVERRIDE { 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(), mbNonUserTriggeredMainSequenceEffectSeen(false) { 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& xRootNode) { mxCurrentAnimationRootNode = xRootNode; } bool EffectRewinder::rewind ( const ::boost::shared_ptr& rpPaintLock, const ::boost::function& rSlideRewindFunctor, const ::boost::function& 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), "EffectRewinder::asynchronousRewindToPreviousSlide"); } 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), "EffectRewinder::asynchronousRewind"); } 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()), "EffectRewinder::asynchronousRewind"); mrEventQueue.addEvent(mpAsynchronousRewindEvent); } sal_Int32 EffectRewinder::countMainSequenceEffects (void) { // Determine the number of main sequence effects. sal_Int32 nMainSequenceNodeCount (0); ::std::queue > aNodeQueue; aNodeQueue.push(mxCurrentAnimationRootNode); while ( ! aNodeQueue.empty()) { const uno::Reference 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 xEnumerationAccess (xNode, uno::UNO_QUERY); if (xEnumerationAccess.is()) { uno::Reference xEnumeration ( xEnumerationAccess->createEnumeration()); if (xEnumeration.is()) while (xEnumeration->hasMoreElements()) { aNodeQueue.push( uno::Reference( xEnumeration->nextElement(), uno::UNO_QUERY)); } } } return nMainSequenceNodeCount; } 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(rpNode)); if ( ! pBaseNode) return false; BaseContainerNodeSharedPtr pParent (pBaseNode->getParentNode()); if ( ! (pParent && pParent->isMainSequenceRootNode())) return false; // This notification is only relevant for us when the effect is user // triggered. bool bIsUserTriggered (false); Reference xNode (rpNode->getXAnimationNode()); if (xNode.is()) { animations::Event aEvent; if ((xNode->getBegin() >>= aEvent)) bIsUserTriggered = (aEvent.Trigger == animations::EventTrigger::ON_NEXT); } if (bIsUserTriggered) ++mnMainSequenceEffectCount; else mbNonUserTriggeredMainSequenceEffectSeen = true; return false; } void EffectRewinder::asynchronousRewind ( sal_Int32 nEffectCount, const bool bRedisplayCurrentSlide, const boost::function& 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), "EffectRewinder::asynchronousRewind"); mrEventQueue.addEvent(mpAsynchronousRewindEvent); } else { // Process initial events and skip any animations that are started // when the slide is shown. mbNonUserTriggeredMainSequenceEffectSeen = false; mrEventQueue.forceEmpty(); if (mbNonUserTriggeredMainSequenceEffectSeen) { mrUserEventQueue.callSkipEffectEventHandler(); mrEventQueue.forceEmpty(); } while (--nEffectCount >= 0) skipSingleMainSequenceEffects(); mpAsynchronousRewindEvent.reset(); mpPaintLock.reset(); } } void EffectRewinder::asynchronousRewindToPreviousSlide ( const ::boost::function& rSlideRewindFunctor) { OSL_ASSERT(mpAsynchronousRewindEvent); mpAsynchronousRewindEvent.reset(); rSlideRewindFunctor(); } } } // end of namespace ::slideshow::internal /* vim:set shiftwidth=4 softtabstop=4 expandtab: */