/* -*- 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 #include #include #include #include #include #include #include #include using ::com::sun::star::uno::Reference; using namespace ::com::sun::star; namespace slideshow::internal { namespace { class RewinderEventHandler : public EventHandler { public: typedef ::std::function Action; explicit RewinderEventHandler (Action aAction) : maAction(std::move(aAction)) {} private: const Action maAction; virtual bool handleEvent() override { return maAction(); } }; class RewinderAnimationEventHandler : public AnimationEventHandler { public: typedef ::std::function Action; explicit RewinderAnimationEventHandler (Action aAction) : maAction(std::move(aAction)) {} private: const Action maAction; virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode) 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(), mxCurrentSlide(), mbNonUserTriggeredMainSequenceEffectSeen(false), mbHasAdvancedTimeSetting(false) { initialize(); } void EffectRewinder::initialize() { // 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 = std::make_shared( [this]( const AnimationNodeSharedPtr& pNode) { return this->notifyAnimationStart( pNode ); } ); mrEventMultiplexer.addAnimationStartHandler(mpAnimationStartHandler); mpSlideStartHandler = std::make_shared( [this]() { return this->resetEffectCount(); } ); mrEventMultiplexer.addSlideStartHandler(mpSlideStartHandler); mpSlideEndHandler = std::make_shared( [this]() { return this->resetEffectCount(); } ); mrEventMultiplexer.addSlideEndHandler(mpSlideEndHandler); } EffectRewinder::~EffectRewinder() { dispose(); } void EffectRewinder::dispose() { 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; } void EffectRewinder::setCurrentSlide ( const uno::Reference& xSlide) { mxCurrentSlide = xSlide; // Check if the current slide has advance time setting or not uno::Reference< beans::XPropertySet > xPropSet( mxCurrentSlide, uno::UNO_QUERY ); sal_Int32 nChange(0); if( xPropSet.is()) getPropertyValue( nChange, xPropSet, u"Change"_ustr); mbHasAdvancedTimeSetting = nChange; } bool EffectRewinder::rewind ( const ::std::shared_ptr& rpPaintLock, const ::std::function& rSlideRewindFunctor, const ::std::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(); if (!mbHasAdvancedTimeSetting) 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( ::std::bind( &EffectRewinder::asynchronousRewindToPreviousSlide, this, rPreviousSlideFunctor), u"EffectRewinder::asynchronousRewindToPreviousSlide"_ustr); } else { // The actual rewinding is done asynchronously so that we can safely // call other methods. mpAsynchronousRewindEvent = makeEvent( ::std::bind( &EffectRewinder::asynchronousRewind, this, nSkipCount, true, rSlideRewindFunctor), u"EffectRewinder::asynchronousRewind"_ustr); } if (mpAsynchronousRewindEvent) mrEventQueue.addEvent(mpAsynchronousRewindEvent); return bool(mpAsynchronousRewindEvent); } void EffectRewinder::skipAllMainSequenceEffects() { // Do not allow nested rewinds. if (mpAsynchronousRewindEvent) { OSL_ASSERT(!mpAsynchronousRewindEvent); return; } const int nTotalMainSequenceEffectCount (countMainSequenceEffects()); mpAsynchronousRewindEvent = makeEvent( ::std::bind( &EffectRewinder::asynchronousRewind, this, nTotalMainSequenceEffectCount, false, ::std::function()), u"EffectRewinder::asynchronousRewind"_ustr); mrEventQueue.addEvent(mpAsynchronousRewindEvent); } sal_Int32 EffectRewinder::countMainSequenceEffects() { // 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() { // This basically just starts the next effect and then skips over its // animation. mrEventMultiplexer.notifyNextEffect(); mrEventQueue.forceEmpty(); mrUserEventQueue.callSkipEffectEventHandler(); mrEventQueue.forceEmpty(); } bool EffectRewinder::resetEffectCount() { 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 (::std::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 std::function& rSlideRewindFunctor) { OSL_ASSERT(mpAsynchronousRewindEvent); if (bRedisplayCurrentSlide) { mpPaintLock->Activate(); // Re-display the current slide. if (rSlideRewindFunctor) rSlideRewindFunctor(); mpAsynchronousRewindEvent = makeEvent( ::std::bind( &EffectRewinder::asynchronousRewind, this, nEffectCount, false, rSlideRewindFunctor), u"EffectRewinder::asynchronousRewind"_ustr); mrEventQueue.addEvent(mpAsynchronousRewindEvent); } else { // Process initial events and skip any animations that are started // when the slide is shown. mbNonUserTriggeredMainSequenceEffectSeen = false; if (!mbHasAdvancedTimeSetting) mrEventQueue.forceEmpty(); if (mbNonUserTriggeredMainSequenceEffectSeen) { mrUserEventQueue.callSkipEffectEventHandler(); mrEventQueue.forceEmpty(); } while (--nEffectCount >= 0) skipSingleMainSequenceEffects(); mpAsynchronousRewindEvent.reset(); mpPaintLock.reset(); } } void EffectRewinder::asynchronousRewindToPreviousSlide ( const ::std::function& rSlideRewindFunctor) { OSL_ASSERT(mpAsynchronousRewindEvent); mpAsynchronousRewindEvent.reset(); rSlideRewindFunctor(); } } // end of namespace ::slideshow::internal /* vim:set shiftwidth=4 softtabstop=4 expandtab: */