diff options
Diffstat (limited to 'slideshow/source/engine/shapes/drawinglayeranimation.cxx')
-rw-r--r-- | slideshow/source/engine/shapes/drawinglayeranimation.cxx | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/slideshow/source/engine/shapes/drawinglayeranimation.cxx b/slideshow/source/engine/shapes/drawinglayeranimation.cxx new file mode 100644 index 000000000..0b37071fb --- /dev/null +++ b/slideshow/source/engine/shapes/drawinglayeranimation.cxx @@ -0,0 +1,932 @@ +/* -*- 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 <tools/diagnose_ex.h> +#include <tools/gen.hxx> +#include <tools/helpers.hxx> +#include <canvas/elapsedtime.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +#include <vcl/canvastools.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/TextAnimationKind.hpp> +#include <com/sun/star/drawing/TextAnimationDirection.hpp> + +#include <activity.hxx> +#include <wakeupevent.hxx> +#include <eventqueue.hxx> +#include "drawinglayeranimation.hxx" +#include "drawshapesubsetting.hxx" +#include "drawshape.hxx" +#include <shapeattributelayerholder.hxx> +#include <slideshowcontext.hxx> +#include <subsettableshapemanager.hxx> +#include <tools.hxx> +#include "gdimtftools.hxx" +#include <intrinsicanimationeventhandler.hxx> + +#include <vector> +#include <memory> + +using namespace com::sun::star; +using namespace ::slideshow::internal; + +namespace { + +class ScrollTextAnimNode +{ + sal_uInt32 mnDuration; // single duration + sal_uInt32 mnRepeat; // 0 -> endless + double mfStart; + double mfStop; + sal_uInt32 mnFrequency; // in ms + // forth and back change at mnRepeat%2: + bool mbAlternate; + +public: + ScrollTextAnimNode( + sal_uInt32 nDuration, sal_uInt32 nRepeat, double fStart, double fStop, + sal_uInt32 nFrequency, bool bAlternate) + : mnDuration(nDuration), + mnRepeat(nRepeat), + mfStart(fStart), + mfStop(fStop), + mnFrequency(nFrequency), + mbAlternate(bAlternate) + {} + + sal_uInt32 GetRepeat() const { return mnRepeat; } + sal_uInt32 GetFullTime() const { return mnDuration * mnRepeat; } + double GetStop() const { return mfStop; } + sal_uInt32 GetFrequency() const { return mnFrequency; } + bool DoAlternate() const { return mbAlternate; } + + double GetStateAtRelativeTime(sal_uInt32 nRelativeTime) const; +}; + +double ScrollTextAnimNode::GetStateAtRelativeTime( + sal_uInt32 nRelativeTime) const +{ + // Avoid division by zero. + if( mnDuration == 0 ) + return mfStop; + + if(mnRepeat) + { + // ending + const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration); + sal_uInt32 nFrameTime(nRelativeTime - (nRepeatCount * mnDuration)); + + if(DoAlternate() && (nRepeatCount + 1) % 2L) + nFrameTime = mnDuration - nFrameTime; + + return mfStart + ((mfStop - mfStart) * + (double(nFrameTime) / mnDuration)); + } + else + { + // endless + sal_uInt32 nFrameTime(nRelativeTime % mnDuration); + + if(DoAlternate()) + { + const sal_uInt32 nRepeatCount(nRelativeTime / mnDuration); + + if((nRepeatCount + 1) % 2L) + nFrameTime = mnDuration - nFrameTime; + } + + return mfStart + ((mfStop - mfStart) * (double(nFrameTime) / mnDuration)); + } +} + +class ActivityImpl : public Activity +{ +public: + ActivityImpl( + SlideShowContext const& rContext, + std::shared_ptr<WakeupEvent> const& pWakeupEvent, + std::shared_ptr<DrawShape> const& pDrawShape ); + + ActivityImpl(const ActivityImpl&) = delete; + ActivityImpl& operator=(const ActivityImpl&) = delete; + + bool enableAnimations(); + + // Disposable: + virtual void dispose() override; + // Activity: + virtual double calcTimeLag() const override; + virtual bool perform() override; + virtual bool isActive() const override; + virtual void dequeued() override; + virtual void end() override; + +private: + void updateShapeAttributes( double fTime, + basegfx::B2DRectangle const& parentBounds ); + + // scroll horizontal? if sal_False, scroll is vertical. + bool ScrollHorizontal() const { + return (drawing::TextAnimationDirection_LEFT == meDirection || + drawing::TextAnimationDirection_RIGHT == meDirection); + } + + // Access to StepWidth in logical units + sal_uInt32 GetStepWidthLogic() const; + + // is the animation direction opposite? + bool DoScrollForward() const { + return (drawing::TextAnimationDirection_RIGHT == meDirection || + drawing::TextAnimationDirection_DOWN == meDirection); + } + + // do alternate text directions? + bool DoAlternate() const { return mbAlternate; } + + // do scroll in? + bool DoScrollIn() const { return mbScrollIn; } + + // Scroll helper methods + void ImpForceScrollTextAnimNodes(); + ScrollTextAnimNode* ImpGetScrollTextAnimNode( + sal_uInt32 nTime, sal_uInt32& rRelativeTime ); + sal_uInt32 ImpRegisterAgainScrollTextMixerState( + sal_uInt32 nTime); + + // calculate the MixerState value for given time + double GetMixerState(sal_uInt32 nTime); + + + SlideShowContext maContext; + std::shared_ptr<WakeupEvent> mpWakeupEvent; + std::weak_ptr<DrawShape> mpParentDrawShape; + DrawShapeSharedPtr mpDrawShape; + ShapeAttributeLayerHolder maShapeAttrLayer; + GDIMetaFileSharedPtr mpMetaFile; + IntrinsicAnimationEventHandlerSharedPtr mpListener; + canvas::tools::ElapsedTime maTimer; + double mfRotationAngle; + bool mbIsShapeAnimated; + bool mbIsDisposed; + bool mbIsActive; + drawing::TextAnimationKind meAnimKind; + + // The blink frequency in ms + sal_uInt32 mnFrequency; + + // The repeat count, init to 0L which means endless + sal_uInt32 mnRepeat; + + // Flag to decide if text will be shown when animation has ended + bool mbVisibleWhenStopped; + bool mbVisibleWhenStarted; + + // Flag decides if TextScroll alternates. Default is sal_False. + bool mbAlternate; + + // Flag to remember if this is a simple scrolling text + bool mbScrollIn; + + // The AnimationDirection + drawing::TextAnimationDirection meDirection; + + // Get width per Step. Negative means pixel, positive logical units + sal_Int32 mnStepWidth; + + // The single anim steps + std::vector< ScrollTextAnimNode > maVector; + + // the scroll rectangle + tools::Rectangle maScrollRectangleLogic; + + // the paint rectangle + tools::Rectangle maPaintRectangleLogic; +}; + + +class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler +{ +public: + explicit IntrinsicAnimationListener( ActivityImpl& rActivity ) : + mrActivity( rActivity ) + {} + + IntrinsicAnimationListener(const IntrinsicAnimationListener&) = delete; + IntrinsicAnimationListener& operator=(const IntrinsicAnimationListener&) = delete; + +private: + + virtual bool enableAnimations() override { return mrActivity.enableAnimations(); } + virtual bool disableAnimations() override { mrActivity.end(); return true; } + + ActivityImpl& mrActivity; +}; + + +double ActivityImpl::GetMixerState( sal_uInt32 nTime ) +{ + if( meAnimKind == drawing::TextAnimationKind_BLINK ) + { + // from AInfoBlinkText: + double fRetval(0.0); + bool bDone(false); + const sal_uInt32 nLoopTime(2 * mnFrequency); + + if(mnRepeat) + { + const sal_uInt32 nEndTime(mnRepeat * nLoopTime); + + if(nTime >= nEndTime) + { + if(mbVisibleWhenStopped) + fRetval = 0.0; + else + fRetval = 1.0; + + bDone = true; + } + } + + if(!bDone) + { + sal_uInt32 nTimeInLoop(nTime % nLoopTime); + fRetval = double(nTimeInLoop) / nLoopTime; + } + + return fRetval; + } + else + { + // from AInfoScrollText: + double fRetval(0.0); + ImpForceScrollTextAnimNodes(); + + if(!maVector.empty()) + { + sal_uInt32 nRelativeTime; + ScrollTextAnimNode* pNode = + ImpGetScrollTextAnimNode(nTime, nRelativeTime); + + if(pNode) + { + // use node + fRetval = pNode->GetStateAtRelativeTime(nRelativeTime); + } + else + { + // end of animation, take last entry's end + fRetval = maVector[maVector.size() - 1].GetStop(); + } + } + + return fRetval; + } +} + +// Access to StepWidth in logical units +sal_uInt32 ActivityImpl::GetStepWidthLogic() const +{ + // #i69847# Assuming higher DPI + constexpr sal_uInt32 PIXEL_TO_LOGIC = 30; + + sal_uInt32 nRetval(0); + + if(mnStepWidth < 0) + { + // is in pixels, convert to logical units + nRetval = (-mnStepWidth * PIXEL_TO_LOGIC); + } + else if(mnStepWidth > 0) + { + // is in logical units + nRetval = mnStepWidth; + } + + if(0 == nRetval) + { + // step 1 pixel, canned value + + // with very high DPIs like in PDF export, this can + // still get zero. for that cases, set a default, too (taken + // from ainfoscrolltext.cxx) + nRetval = 100; + } + + return nRetval; +} + +void ActivityImpl::ImpForceScrollTextAnimNodes() +{ + if(!maVector.empty()) + return; + + // prepare values + sal_uInt32 nLoopTime; + double fZeroLogic, fOneLogic, fInitLogic, fDistanceLogic; + double fZeroLogicAlternate = 0.0, fOneLogicAlternate = 0.0; + double fZeroRelative, fOneRelative, fInitRelative; + + if(ScrollHorizontal()) + { + if(DoAlternate()) + { + if(maPaintRectangleLogic.GetWidth() > + maScrollRectangleLogic.GetWidth()) + { + fZeroLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth(); + fOneLogicAlternate = maScrollRectangleLogic.Left(); + } + else + { + fZeroLogicAlternate = maScrollRectangleLogic.Left(); + fOneLogicAlternate = maScrollRectangleLogic.Right() - maPaintRectangleLogic.GetWidth(); + } + } + + fZeroLogic = maScrollRectangleLogic.Left() - maPaintRectangleLogic.GetWidth(); + fOneLogic = maScrollRectangleLogic.Right(); + fInitLogic = maPaintRectangleLogic.Left(); + } + else + { + if(DoAlternate()) + { + if(maPaintRectangleLogic.GetHeight() > maScrollRectangleLogic.GetHeight()) + { + fZeroLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight(); + fOneLogicAlternate = maScrollRectangleLogic.Top(); + } + else + { + fZeroLogicAlternate = maScrollRectangleLogic.Top(); + fOneLogicAlternate = maScrollRectangleLogic.Bottom() - maPaintRectangleLogic.GetHeight(); + } + } + + fZeroLogic = maScrollRectangleLogic.Top() - maPaintRectangleLogic.GetHeight(); + fOneLogic = maScrollRectangleLogic.Bottom(); + fInitLogic = maPaintRectangleLogic.Top(); + } + + fDistanceLogic = fOneLogic - fZeroLogic; + fInitRelative = (fInitLogic - fZeroLogic) / fDistanceLogic; + + if(DoAlternate()) + { + fZeroRelative = + (fZeroLogicAlternate - fZeroLogic) / fDistanceLogic; + fOneRelative = + (fOneLogicAlternate - fZeroLogic) / fDistanceLogic; + } + else + { + fZeroRelative = 0.0; + fOneRelative = 1.0; + } + + if(mbVisibleWhenStarted) + { + double fRelativeStartValue, fRelativeEndValue,fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fInitRelative; + fRelativeEndValue = fOneRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fInitRelative; + fRelativeEndValue = fZeroRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + // init loop + ScrollTextAnimNode aInitNode( + nLoopTime, 1, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, false); + maVector.push_back(aInitNode); + } + + // prepare main loop values + { + double fRelativeStartValue, fRelativeEndValue, fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fZeroRelative; + fRelativeEndValue = fOneRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fOneRelative; + fRelativeEndValue = fZeroRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + if(0 == mnRepeat) + { + if(!DoScrollIn()) + { + // endless main loop + ScrollTextAnimNode aMainNode( + nLoopTime, 0, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, DoAlternate()); + maVector.push_back(aMainNode); + } + } + else + { + sal_uInt32 nNumRepeat(mnRepeat); + + if(DoAlternate() && (nNumRepeat + 1) % 2L) + nNumRepeat += 1; + + // ending main loop + ScrollTextAnimNode aMainNode( + nLoopTime, nNumRepeat, + fRelativeStartValue, fRelativeEndValue, + mnFrequency, DoAlternate()); + maVector.push_back(aMainNode); + } + } + + if(!mbVisibleWhenStopped) + return; + + double fRelativeStartValue, fRelativeEndValue, fRelativeDistance; + + if(DoScrollForward()) + { + fRelativeStartValue = fZeroRelative; + fRelativeEndValue = fInitRelative; + fRelativeDistance = fRelativeEndValue - fRelativeStartValue; + } + else + { + fRelativeStartValue = fOneRelative; + fRelativeEndValue = fInitRelative; + fRelativeDistance = fRelativeStartValue - fRelativeEndValue; + } + + const double fNumberSteps = + (fRelativeDistance * fDistanceLogic) / GetStepWidthLogic(); + nLoopTime = FRound(fNumberSteps * mnFrequency); + + // exit loop + ScrollTextAnimNode aExitNode( + nLoopTime, 1, + fRelativeStartValue, fRelativeEndValue, mnFrequency, false); + maVector.push_back(aExitNode); +} + +ScrollTextAnimNode* ActivityImpl::ImpGetScrollTextAnimNode( + sal_uInt32 nTime, sal_uInt32& rRelativeTime ) +{ + ScrollTextAnimNode* pRetval = nullptr; + ImpForceScrollTextAnimNodes(); + + if(!maVector.empty()) + { + rRelativeTime = nTime; + + for(ScrollTextAnimNode & rNode: maVector) + { + if(!rNode.GetRepeat()) + { + // endless loop, use it + pRetval = &rNode; + } + else if(rNode.GetFullTime() > rRelativeTime) + { + // ending node + pRetval = &rNode; + } + else + { + // look at next + rRelativeTime -= rNode.GetFullTime(); + } + } + } + + return pRetval; +} + +sal_uInt32 ActivityImpl::ImpRegisterAgainScrollTextMixerState(sal_uInt32 nTime) +{ + sal_uInt32 nRetval(0); + ImpForceScrollTextAnimNodes(); + + if(!maVector.empty()) + { + sal_uInt32 nRelativeTime; + ScrollTextAnimNode* pNode = ImpGetScrollTextAnimNode(nTime, nRelativeTime); + + if(pNode) + { + // take register time + nRetval = pNode->GetFrequency(); + } + } + else + { + // #i38135# not initialized, return default + nRetval = mnFrequency; + } + + return nRetval; +} + +void ActivityImpl::updateShapeAttributes( + double fTime, basegfx::B2DRectangle const& parentBounds ) +{ + OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE ); + if( meAnimKind == drawing::TextAnimationKind_NONE ) + return; + + double const fMixerState = GetMixerState( + static_cast<sal_uInt32>(fTime * 1000.0) ); + + if( meAnimKind == drawing::TextAnimationKind_BLINK ) + { + // show/hide text: + maShapeAttrLayer.get()->setVisibility( fMixerState < 0.5 ); + } + else if(mpMetaFile) // scroll mode: + { + + // keep care: the below code is highly sensible to changes... + + + // rectangle of the pure text: + double const fPaintWidth = maPaintRectangleLogic.GetWidth(); + double const fPaintHeight = maPaintRectangleLogic.GetHeight(); + // rectangle where the scrolling takes place (-> clipping): + double const fScrollWidth = maScrollRectangleLogic.GetWidth(); + double const fScrollHeight = maScrollRectangleLogic.GetHeight(); + + basegfx::B2DPoint pos, clipPos; + + if(ScrollHorizontal()) + { + double const fOneEquiv( fScrollWidth ); + double const fZeroEquiv( -fPaintWidth ); + + pos.setX( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) ); + + clipPos.setX( -pos.getX() ); + clipPos.setY( -pos.getY() ); + + // #i69844# Compensation for text-wider-than-shape case + if( fPaintWidth > fScrollWidth ) + pos.setX( pos.getX() + (fPaintWidth-fScrollWidth) / 2.0 ); + } + else + { + // scroll vertical: + double const fOneEquiv( fScrollHeight ); + double const fZeroEquiv( -fPaintHeight ); + + pos.setY( fZeroEquiv + (fMixerState * (fOneEquiv - fZeroEquiv)) ); + + clipPos.setX( -pos.getX() ); + clipPos.setY( -pos.getY() ); + + // #i69844# Compensation for text-higher-than-shape case + if( fPaintHeight > fScrollHeight ) + pos.setY( pos.getY() + (fPaintHeight-fScrollHeight) / 2.0 ); + } + + basegfx::B2DPolygon clipPoly( + basegfx::utils::createPolygonFromRect( + basegfx::B2DRectangle( clipPos.getX(), + clipPos.getY(), + clipPos.getX() + fScrollWidth, + clipPos.getY() + fScrollHeight ) ) ); + + if( !::basegfx::fTools::equalZero( mfRotationAngle )) + { + maShapeAttrLayer.get()->setRotationAngle( mfRotationAngle ); + double const fRotate = basegfx::deg2rad(mfRotationAngle); + basegfx::B2DHomMatrix aTransform; + // position: + aTransform.rotate( fRotate ); + pos *= aTransform; + } + + pos += parentBounds.getCenter(); + maShapeAttrLayer.get()->setPosition( pos ); + maShapeAttrLayer.get()->setClip( basegfx::B2DPolyPolygon(clipPoly) ); + } +} + +bool ActivityImpl::perform() +{ + if( !isActive() ) + return false; + + ENSURE_OR_RETURN_FALSE( + mpDrawShape, + "ActivityImpl::perform(): still active, but NULL draw shape" ); + + DrawShapeSharedPtr const pParentDrawShape( mpParentDrawShape ); + if( !pParentDrawShape ) + return false; // parent has vanished + + if( pParentDrawShape->isVisible() ) + { + if( !mbIsShapeAnimated ) + { + mpDrawShape->setVisibility(true); // shape may be initially hidden + maContext.mpSubsettableShapeManager->enterAnimationMode( mpDrawShape ); + maTimer.reset(); + mbIsShapeAnimated = true; + } + // update attributes related to current time: + basegfx::B2DRectangle const parentBounds( + pParentDrawShape->getBounds() ); + + const double nCurrTime( maTimer.getElapsedTime() ); + updateShapeAttributes( nCurrTime, parentBounds ); + + const sal_uInt32 nFrequency( + ImpRegisterAgainScrollTextMixerState( + static_cast<sal_uInt32>(nCurrTime * 1000.0)) ); + + if(nFrequency) + { + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( + std::max(0.1,nFrequency/1000.0) ); + maContext.mrEventQueue.addEvent( mpWakeupEvent ); + + if( mpDrawShape->isContentChanged() ) + maContext.mpSubsettableShapeManager->notifyShapeUpdate( mpDrawShape ); + } + // else: finished, not need to wake up again. + } + else + { + // busy-wait, until parent shape gets visible + mpWakeupEvent->start(); + mpWakeupEvent->setNextTimeout( 2.0 ); + } + + // don't reinsert, WakeupEvent will perform that after the given timeout: + return false; +} + +ActivityImpl::ActivityImpl( + SlideShowContext const& rContext, + std::shared_ptr<WakeupEvent> const& pWakeupEvent, + std::shared_ptr<DrawShape> const& pParentDrawShape ) + : maContext(rContext), + mpWakeupEvent(pWakeupEvent), + mpParentDrawShape(pParentDrawShape), + mpListener( std::make_shared<IntrinsicAnimationListener>(*this) ), + maTimer(rContext.mrEventQueue.getTimer()), + mfRotationAngle(0.0), + mbIsShapeAnimated(false), + mbIsDisposed(false), + mbIsActive(true), + meAnimKind(drawing::TextAnimationKind_NONE), + mbVisibleWhenStopped(false), + mbVisibleWhenStarted(false), + mnStepWidth(0) +{ + // get doctreenode: + sal_Int32 const nNodes = pParentDrawShape->getNumberOfTreeNodes( + DocTreeNode::NodeType::LogicalParagraph ); + + DocTreeNode scrollTextNode( + pParentDrawShape->getTreeNode( + 0, DocTreeNode::NodeType::LogicalParagraph )); + // xxx todo: remove this hack + if( nNodes > 1 ) + scrollTextNode.setEndIndex( + pParentDrawShape->getTreeNode( + nNodes - 1, + DocTreeNode::NodeType::LogicalParagraph ).getEndIndex()); + + // TODO(Q3): Doing this manually, instead of using + // ShapeSubset. This is because of lifetime issues (ShapeSubset + // generates circular references to parent shape) + mpDrawShape = std::dynamic_pointer_cast<DrawShape>( + maContext.mpSubsettableShapeManager->getSubsetShape( + pParentDrawShape, + scrollTextNode )); + + mpMetaFile = mpDrawShape->forceScrollTextMetaFile(); + + // make scroll text invisible for slide transition bitmaps + mpDrawShape->setVisibility(false); + + basegfx::B2DRectangle aScrollRect, aPaintRect; + ENSURE_OR_THROW( getRectanglesFromScrollMtf( aScrollRect, + aPaintRect, + mpMetaFile ), + "ActivityImpl::ActivityImpl(): Could not extract " + "scroll anim rectangles from mtf" ); + + maScrollRectangleLogic = vcl::unotools::rectangleFromB2DRectangle( + aScrollRect ); + maPaintRectangleLogic = vcl::unotools::rectangleFromB2DRectangle( + aPaintRect ); + + maShapeAttrLayer.createAttributeLayer(mpDrawShape); + + uno::Reference<drawing::XShape> const xShape( mpDrawShape->getXShape() ); + uno::Reference<beans::XPropertySet> const xProps( xShape, uno::UNO_QUERY_THROW ); + + getPropertyValue( meAnimKind, xProps, "TextAnimationKind" ); + OSL_ASSERT( meAnimKind != drawing::TextAnimationKind_NONE ); + mbAlternate = (meAnimKind == drawing::TextAnimationKind_ALTERNATE); + mbScrollIn = (meAnimKind == drawing::TextAnimationKind_SLIDE); + + // adopted from in AInfoBlinkText::ImplInit(): + sal_Int16 nRepeat(0); + getPropertyValue( nRepeat, xProps, "TextAnimationCount" ); + mnRepeat = nRepeat; + + if(mbAlternate) + { + // force visible when started for scroll-forth-and-back, because + // slide has been coming in with visible text in the middle: + mbVisibleWhenStarted = true; + } + else + { + getPropertyValue( mbVisibleWhenStarted, xProps, + "TextAnimationStartInside" ); + } + + // set visible when stopped + getPropertyValue( mbVisibleWhenStopped, xProps, + "TextAnimatiogonStopInside" ); + // rotation: + getPropertyValue( mfRotationAngle, xProps, + "RotateAngle" ); + mfRotationAngle /= -100.0; // (switching direction) + + // set frequency + sal_Int16 nDelay(0); + getPropertyValue( nDelay, xProps, "TextAnimationDelay" ); + // set delay if not automatic + mnFrequency = (nDelay ? nDelay : + // default: + meAnimKind == drawing::TextAnimationKind_BLINK + ? 250 : 50 ); + + // adopted from in AInfoScrollText::ImplInit(): + + // If it is a simple m_bScrollIn, reset some parameters + if( DoScrollIn() ) + { + // most parameters are set correctly from the dialog logic, but + // eg VisibleWhenStopped is grayed out and needs to be corrected here. + mbVisibleWhenStopped = true; + mbVisibleWhenStarted = false; + mnRepeat = 0; + } + + // Get animation direction + getPropertyValue( meDirection, xProps, "TextAnimationDirection" ); + + // Get step width. Negative means pixel, positive logical units + getPropertyValue( mnStepWidth, xProps, "TextAnimationAmount" ); + + maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler( + mpListener ); +} + +bool ActivityImpl::enableAnimations() +{ + mbIsActive = true; + return maContext.mrActivitiesQueue.addActivity( std::dynamic_pointer_cast<Activity>(shared_from_this()) ); +} + +void ActivityImpl::dispose() +{ + if( mbIsDisposed ) + return; + + end(); + + // only remove subset here, since end() is called on slide end + // (and we must not spoil the slide preview bitmap with scroll + // text) + maShapeAttrLayer.reset(); + if( mpDrawShape ) + { + // TODO(Q3): Doing this manually, instead of using + // ShapeSubset. This is because of lifetime issues + // (ShapeSubset generates circular references to parent + // shape) + DrawShapeSharedPtr pParent( mpParentDrawShape.lock() ); + if( pParent ) + maContext.mpSubsettableShapeManager->revokeSubset( + pParent, + mpDrawShape ); + } + + mpMetaFile.reset(); + mpDrawShape.reset(); + mpParentDrawShape.reset(); + mpWakeupEvent.reset(); + maContext.dispose(); + mbIsDisposed = true; + + maContext.mpSubsettableShapeManager->removeIntrinsicAnimationHandler( + mpListener ); +} + +double ActivityImpl::calcTimeLag() const +{ + return 0.0; +} + +bool ActivityImpl::isActive() const +{ + return mbIsActive; +} + +void ActivityImpl::dequeued() +{ + // not used here +} + +void ActivityImpl::end() +{ + // not used here + mbIsActive = false; + + if( mbIsShapeAnimated ) + { + maContext.mpSubsettableShapeManager->leaveAnimationMode( mpDrawShape ); + mbIsShapeAnimated = false; + } +} + +} // anon namespace + +namespace slideshow::internal { + +std::shared_ptr<Activity> createDrawingLayerAnimActivity( + SlideShowContext const& rContext, + std::shared_ptr<DrawShape> const& pDrawShape ) +{ + std::shared_ptr<Activity> pActivity; + + try + { + auto const pWakeupEvent = std::make_shared<WakeupEvent>( rContext.mrEventQueue.getTimer(), + rContext.mrActivitiesQueue ); + pActivity = std::make_shared<ActivityImpl>( rContext, pWakeupEvent, pDrawShape ); + pWakeupEvent->setActivity( pActivity ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + // translate any error into empty factory product. + TOOLS_WARN_EXCEPTION( "slideshow", "" ); + } + + return pActivity; +} + +} // namespace presentation + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |