summaryrefslogtreecommitdiff
path: root/slideshow/source/engine/shapes/drawinglayeranimation.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/shapes/drawinglayeranimation.cxx')
-rw-r--r--slideshow/source/engine/shapes/drawinglayeranimation.cxx983
1 files changed, 983 insertions, 0 deletions
diff --git a/slideshow/source/engine/shapes/drawinglayeranimation.cxx b/slideshow/source/engine/shapes/drawinglayeranimation.cxx
new file mode 100644
index 000000000000..323f16849123
--- /dev/null
+++ b/slideshow/source/engine/shapes/drawinglayeranimation.cxx
@@ -0,0 +1,983 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_slideshow.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <canvas/elapsedtime.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <comphelper/anytostring.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <rtl/math.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/salbtype.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/metaact.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/TextAnimationKind.hpp>
+#include <com/sun/star/drawing/TextAnimationDirection.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+#include "activity.hxx"
+#include "wakeupevent.hxx"
+#include "eventqueue.hxx"
+#include "drawshapesubsetting.hxx"
+#include "drawshape.hxx"
+#include "shapesubset.hxx"
+#include "shapeattributelayerholder.hxx"
+#include "slideshowcontext.hxx"
+#include "tools.hxx"
+#include "gdimtftools.hxx"
+#include "eventmultiplexer.hxx"
+#include "intrinsicanimationactivity.hxx"
+#include "intrinsicanimationeventhandler.hxx"
+
+#include <boost/weak_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/noncopyable.hpp>
+#include <vector>
+
+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 GetDuration() const { return mnDuration; }
+ sal_uInt32 GetRepeat() const { return mnRepeat; }
+ sal_uInt32 GetFullTime() const { return mnDuration * mnRepeat; }
+ double GetStart() const { return mfStart; }
+ 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 + 1L) % 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 + 1L) % 2L)
+ nFrameTime = mnDuration - nFrameTime;
+ }
+
+ return mfStart + ((mfStop - mfStart) * (double(nFrameTime) / mnDuration));
+ }
+}
+
+class ActivityImpl : public Activity,
+ public boost::enable_shared_from_this<ActivityImpl>,
+ private boost::noncopyable
+{
+public:
+ virtual ~ActivityImpl();
+
+ ActivityImpl(
+ SlideShowContext const& rContext,
+ boost::shared_ptr<WakeupEvent> const& pWakeupEvent,
+ boost::shared_ptr<DrawShape> const& pDrawShape );
+
+ bool enableAnimations();
+
+ // Disposable:
+ virtual void dispose();
+ // Activity:
+ virtual double calcTimeLag() const;
+ virtual bool perform();
+ virtual bool isActive() const;
+ virtual void dequeued();
+ virtual void end();
+
+private:
+ void updateShapeAttributes( double fTime,
+ basegfx::B2DRectangle const& parentBounds );
+
+ // Access to VisibleWhenSTarted flags
+ sal_Bool IsVisibleWhenStarted() const { return mbVisibleWhenStarted; }
+ sal_Bool IsVisibleWhenStopped() const { return mbVisibleWhenStopped; }
+
+ // 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;
+ boost::shared_ptr<WakeupEvent> mpWakeupEvent;
+ boost::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 scrollin text
+ bool mbScrollIn;
+
+ // start time for this animation
+ sal_uInt32 mnStartTime;
+
+ // 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
+ Rectangle maScrollRectangleLogic;
+
+ // the paint rectangle
+ Rectangle maPaintRectangleLogic;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler,
+ private boost::noncopyable
+{
+public:
+ explicit IntrinsicAnimationListener( ActivityImpl& rActivity ) :
+ mrActivity( rActivity )
+ {}
+
+private:
+
+ virtual bool enableAnimations() { return mrActivity.enableAnimations(); }
+ virtual bool disableAnimations() { mrActivity.end(); return true; }
+
+ ActivityImpl& mrActivity;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+double ActivityImpl::GetMixerState( sal_uInt32 nTime )
+{
+ if( meAnimKind == drawing::TextAnimationKind_BLINK )
+ {
+ // from AInfoBlinkText:
+ double fRetval(0.0);
+ sal_Bool bDone(sal_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 = sal_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() - 1L].GetStop();
+ }
+ }
+
+ return fRetval;
+ }
+}
+
+// Access to StepWidth in logical units
+sal_uInt32 ActivityImpl::GetStepWidthLogic() const
+{
+ // #i69847# Assuming higher DPI
+ sal_uInt32 const PIXEL_TO_LOGIC = 30;
+
+ sal_uInt32 nRetval(0L);
+
+ if(mnStepWidth < 0L)
+ {
+ // is in pixels, convert to logical units
+ nRetval = (-mnStepWidth * PIXEL_TO_LOGIC);
+ }
+ else if(mnStepWidth > 0L)
+ {
+ // is in logical units
+ nRetval = mnStepWidth;
+ }
+
+ if(0L == 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 = 100L;
+ }
+
+ return nRetval;
+}
+
+void ActivityImpl::ImpForceScrollTextAnimNodes()
+{
+ if(maVector.empty())
+ {
+ // 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(mnStartTime)
+ {
+ // Start time loop
+ ScrollTextAnimNode aStartNode(
+ mnStartTime, 1L, 0.0, 0.0, mnStartTime, false);
+ maVector.push_back(aStartNode);
+ }
+
+ if(IsVisibleWhenStarted())
+ {
+ 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, 1L,
+ 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(0L == mnRepeat)
+ {
+ if(!DoScrollIn())
+ {
+ // endless main loop
+ ScrollTextAnimNode aMainNode(
+ nLoopTime, 0L,
+ fRelativeStartValue, fRelativeEndValue,
+ mnFrequency, DoAlternate());
+ maVector.push_back(aMainNode);
+ }
+ }
+ else
+ {
+ sal_uInt32 nNumRepeat(mnRepeat);
+
+ if(DoAlternate() && (nNumRepeat + 1L) % 2L)
+ nNumRepeat += 1L;
+
+ // ending main loop
+ ScrollTextAnimNode aMainNode(
+ nLoopTime, nNumRepeat,
+ fRelativeStartValue, fRelativeEndValue,
+ mnFrequency, DoAlternate());
+ maVector.push_back(aMainNode);
+ }
+ }
+
+ if(IsVisibleWhenStopped())
+ {
+ 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, 1L,
+ fRelativeStartValue, fRelativeEndValue, mnFrequency, false);
+ maVector.push_back(aExitNode);
+ }
+ }
+}
+
+ScrollTextAnimNode* ActivityImpl::ImpGetScrollTextAnimNode(
+ sal_uInt32 nTime, sal_uInt32& rRelativeTime )
+{
+ ScrollTextAnimNode* pRetval = 0L;
+ ImpForceScrollTextAnimNodes();
+
+ if(!maVector.empty())
+ {
+ rRelativeTime = nTime;
+
+ for(sal_uInt32 a(0L); !pRetval && a < maVector.size(); a++)
+ {
+ ScrollTextAnimNode & rNode = maVector[a];
+ 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(0L);
+ ImpForceScrollTextAnimNodes();
+
+ if(maVector.size())
+ {
+ 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::tools::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 = (mfRotationAngle * M_PI / 180.0);
+ 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,
+ boost::shared_ptr<WakeupEvent> const& pWakeupEvent,
+ boost::shared_ptr<DrawShape> const& pParentDrawShape )
+ : maContext(rContext),
+ mpWakeupEvent(pWakeupEvent),
+ mpParentDrawShape(pParentDrawShape),
+ mpListener( new IntrinsicAnimationListener(*this) ),
+ maTimer(rContext.mrEventQueue.getTimer()),
+ mbIsShapeAnimated(false),
+ mbIsDisposed(false),
+ mbIsActive(true),
+ meAnimKind(drawing::TextAnimationKind_NONE),
+ mnStartTime(0L)
+{
+ // get doctreenode:
+ sal_Int32 const nNodes = pParentDrawShape->getNumberOfTreeNodes(
+ DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH );
+
+ DocTreeNode scrollTextNode(
+ pParentDrawShape->getTreeNode(
+ 0, DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ));
+ // xxx todo: remove this hack
+ if( nNodes > 1 )
+ scrollTextNode.setEndIndex(
+ pParentDrawShape->getTreeNode(
+ nNodes - 1,
+ DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ).getEndIndex());
+
+ // TODO(Q3): Doing this manually, instead of using
+ // ShapeSubset. This is because of lifetime issues (ShapeSubset
+ // generates circular references to parent shape)
+ mpDrawShape = boost::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, OUSTR("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, OUSTR("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,
+ OUSTR("TextAnimationStartInside") );
+ }
+
+ // set visible when stopped
+ getPropertyValue( mbVisibleWhenStopped, xProps,
+ OUSTR("TextAnimatiogonStopInside") );
+ // rotation:
+ getPropertyValue( mfRotationAngle, xProps,
+ OUSTR("RotateAngle") );
+ mfRotationAngle /= -100.0; // (switching direction)
+
+ // set frequency
+ sal_Int16 nDelay(0);
+ getPropertyValue( nDelay, xProps, OUSTR("TextAnimationDelay") );
+ // set delay if not automatic
+ mnFrequency = (nDelay ? nDelay :
+ // default:
+ meAnimKind == drawing::TextAnimationKind_BLINK
+ ? 250L : 50L );
+
+ // 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 VisisbleWhenStopped is grayed out and needs to be corrected here.
+ mbVisibleWhenStopped = true;
+ mbVisibleWhenStarted = false;
+ mnRepeat = 0L;
+ }
+
+ // Get animation direction
+ getPropertyValue( meDirection, xProps, OUSTR("TextAnimationDirection") );
+
+ // Get step width. Negative means pixel, positive logical units
+ getPropertyValue( mnStepWidth, xProps, OUSTR("TextAnimationAmount") );
+
+ maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler(
+ mpListener );
+}
+
+bool ActivityImpl::enableAnimations()
+{
+ mbIsActive = true;
+ return maContext.mrActivitiesQueue.addActivity(
+ shared_from_this() );
+}
+
+ActivityImpl::~ActivityImpl()
+{
+}
+
+void ActivityImpl::dispose()
+{
+ if( !mbIsDisposed )
+ {
+ 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 {
+namespace internal {
+
+boost::shared_ptr<Activity> createDrawingLayerAnimActivity(
+ SlideShowContext const& rContext,
+ boost::shared_ptr<DrawShape> const& pDrawShape )
+{
+ boost::shared_ptr<Activity> pActivity;
+
+ try
+ {
+ boost::shared_ptr<WakeupEvent> const pWakeupEvent(
+ new WakeupEvent( rContext.mrEventQueue.getTimer(),
+ rContext.mrActivitiesQueue ) );
+ pActivity.reset( new ActivityImpl( rContext, pWakeupEvent, pDrawShape ) );
+ pWakeupEvent->setActivity( pActivity );
+ }
+ catch( uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( uno::Exception& )
+ {
+ // translate any error into empty factory product.
+ OSL_FAIL( rtl::OUStringToOString(
+ comphelper::anyToString( cppu::getCaughtException() ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+
+ return pActivity;
+}
+
+} // namespace internal
+} // namespace presentation
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */