summaryrefslogtreecommitdiff
path: root/sd/source/core/CustomAnimationEffect.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sd/source/core/CustomAnimationEffect.cxx')
-rwxr-xr-xsd/source/core/CustomAnimationEffect.cxx3834
1 files changed, 3834 insertions, 0 deletions
diff --git a/sd/source/core/CustomAnimationEffect.cxx b/sd/source/core/CustomAnimationEffect.cxx
new file mode 100755
index 000000000000..33b9c233909b
--- /dev/null
+++ b/sd/source/core/CustomAnimationEffect.cxx
@@ -0,0 +1,3834 @@
+/*************************************************************************
+ *
+ * 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_sd.hxx"
+#include <tools/debug.hxx>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/animations/AnimationFill.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/presentation/EffectNodeType.hpp>
+#include <com/sun/star/presentation/EffectCommands.hpp>
+#include <com/sun/star/presentation/EffectPresetClass.hpp>
+#include <com/sun/star/presentation/ParagraphTarget.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
+#include <com/sun/star/animations/AnimationNodeType.hpp>
+#include <com/sun/star/animations/XCommand.hpp>
+#include <com/sun/star/animations/AnimationTransformType.hpp>
+#include <com/sun/star/animations/XIterateContainer.hpp>
+#include <com/sun/star/animations/XAnimateTransform.hpp>
+#include <com/sun/star/animations/Event.hpp>
+#include <com/sun/star/animations/EventTrigger.hpp>
+#include <com/sun/star/animations/Timing.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/animations/XAnimate.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/animations/XAnimateMotion.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
+#include <com/sun/star/i18n/WordType.hpp>
+#endif
+#include <com/sun/star/presentation/TextAnimationType.hpp>
+
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include <algorithm>
+
+#include <cppuhelper/implbase1.hxx>
+
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+#include "CustomAnimationEffect.hxx"
+#include <CustomAnimationPreset.hxx>
+#include "animations.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::presentation;
+using namespace ::com::sun::star::animations;
+
+using ::rtl::OUString;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::makeAny;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::container::XEnumerationAccess;
+using ::com::sun::star::container::XEnumeration;
+using ::com::sun::star::beans::NamedValue;
+using ::com::sun::star::container::XChild;
+using ::com::sun::star::container::XElementAccess;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::lang::XInitialization;
+using ::com::sun::star::drawing::XShapes;
+using ::com::sun::star::drawing::XDrawPage;
+using ::com::sun::star::text::XText;
+using ::com::sun::star::text::XTextRange;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using ::com::sun::star::util::XCloneable;
+using ::com::sun::star::lang::Locale;
+using ::com::sun::star::util::XChangesNotifier;
+using ::com::sun::star::util::XChangesListener;
+
+namespace sd
+{
+class MainSequenceChangeGuard
+{
+public:
+ MainSequenceChangeGuard( EffectSequenceHelper* pSequence )
+ {
+ mpMainSequence = dynamic_cast< MainSequence* >( pSequence );
+ if( mpMainSequence == 0 )
+ {
+ InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence );
+ if( pI )
+ mpMainSequence = pI->mpMainSequence;
+ }
+ DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" );
+
+ if( mpMainSequence )
+ mpMainSequence->mbIgnoreChanges++;
+ }
+
+ ~MainSequenceChangeGuard()
+ {
+ if( mpMainSequence )
+ mpMainSequence->mbIgnoreChanges++;
+ }
+
+private:
+ MainSequence* mpMainSequence;
+};
+
+CustomAnimationEffect::CustomAnimationEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode )
+: mnNodeType(-1),
+ mnPresetClass(-1),
+ mfBegin(-1.0),
+ mfDuration(-1.0),
+ mfAbsoluteDuration(-1.0),
+ mnGroupId(-1),
+ mnIterateType(0),
+ mfIterateInterval(0.0),
+ mnParaDepth( -1 ),
+ mbHasText(sal_False),
+ mfAcceleration( 1.0 ),
+ mfDecelerate( 1.0 ),
+ mbAutoReverse(false),
+ mnTargetSubItem(0),
+ mnCommand(0),
+ mpEffectSequence( 0 ),
+ mbHasAfterEffect(false),
+ mbAfterEffectOnNextEffect(false)
+{
+ setNode( xNode );
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setNode( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode )
+{
+ mxNode = xNode;
+ mxAudio.clear();
+
+ Sequence< NamedValue > aUserData( mxNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ const NamedValue* p = aUserData.getConstArray();
+
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "node-type" ) )
+ {
+ p->Value >>= mnNodeType;
+ }
+ else if( p->Name.equalsAscii( "preset-id" ) )
+ {
+ p->Value >>= maPresetId;
+ }
+ else if( p->Name.equalsAscii( "preset-sub-type" ) )
+ {
+ p->Value >>= maPresetSubType;
+ }
+ else if( p->Name.equalsAscii( "preset-class" ) )
+ {
+ p->Value >>= mnPresetClass;
+ }
+ else if( p->Name.equalsAscii( "preset-property" ) )
+ {
+ p->Value >>= maProperty;
+ }
+ else if( p->Name.equalsAscii( "group-id" ) )
+ {
+ p->Value >>= mnGroupId;
+ }
+
+ p++;
+ }
+
+ // get effect start time
+ mxNode->getBegin() >>= mfBegin;
+
+ mfAcceleration = mxNode->getAcceleration();
+ mfDecelerate = mxNode->getDecelerate();
+ mbAutoReverse = mxNode->getAutoReverse();
+
+ // get iteration data
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
+ if( xIter.is() )
+ {
+ mfIterateInterval = xIter->getIterateInterval();
+ mnIterateType = xIter->getIterateType();
+ maTarget = xIter->getTarget();
+ mnTargetSubItem = xIter->getSubItem();
+ }
+ else
+ {
+ mfIterateInterval = 0.0f;
+ mnIterateType = 0;
+ }
+
+ // calculate effect duration and get target shape
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xChildNode.is() )
+ continue;
+
+ if( xChildNode->getType() == AnimationNodeType::AUDIO )
+ {
+ mxAudio.set( xChildNode, UNO_QUERY );
+ }
+ else if( xChildNode->getType() == AnimationNodeType::COMMAND )
+ {
+ Reference< XCommand > xCommand( xChildNode, UNO_QUERY );
+ if( xCommand.is() )
+ {
+ mnCommand = xCommand->getCommand();
+ if( !maTarget.hasValue() )
+ maTarget = xCommand->getTarget();
+ }
+ }
+ else
+ {
+ double fBegin = 0.0;
+ double fDuration = 0.0;
+ xChildNode->getBegin() >>= fBegin;
+ xChildNode->getDuration() >>= fDuration;
+
+ fDuration += fBegin;
+ if( fDuration > mfDuration )
+ mfDuration = fDuration;
+
+ // no target shape yet?
+ if( !maTarget.hasValue() )
+ {
+ // go get it boys!
+ Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY );
+ if( xAnimate.is() )
+ {
+ maTarget = xAnimate->getTarget();
+ mnTargetSubItem = xAnimate->getSubItem();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mfAbsoluteDuration = mfDuration;
+ checkForText();
+}
+
+// --------------------------------------------------------------------
+
+sal_Int32 CustomAnimationEffect::getNumberOfSubitems( const Any& aTarget, sal_Int16 nIterateType )
+{
+ sal_Int32 nSubItems = 0;
+
+ try
+ {
+ // first get target text
+ sal_Int32 nOnlyPara = -1;
+
+ Reference< XText > xShape;
+ aTarget >>= xShape;
+ if( !xShape.is() )
+ {
+ ParagraphTarget aParaTarget;
+ if( aTarget >>= aParaTarget )
+ {
+ xShape.set( aParaTarget.Shape, UNO_QUERY );
+ nOnlyPara = aParaTarget.Paragraph;
+ }
+ }
+
+ // now use the break iterator to iterate over the given text
+ // and count the sub items
+
+ if( xShape.is() )
+ {
+ // TODO/LATER: Optimize this, don't create a break iterator each time
+ Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
+ Reference < i18n::XBreakIterator > xBI( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
+ DBG_ASSERT( xBI.is(), "sd::CustomAnimationEffect::getNumberOfSubitems(), could not create a 'com.sun.star.i18n.BreakIterator'!" );
+
+ if( xBI.is() )
+ {
+ Reference< XEnumerationAccess > xEA( xShape, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_QUERY_THROW );
+ Locale aLocale;
+ const OUString aStrLocaleName( RTL_CONSTASCII_USTRINGPARAM("CharLocale") );
+ Reference< XTextRange > xParagraph;
+
+ sal_Int32 nPara = 0;
+ while( xEnumeration->hasMoreElements() )
+ {
+ xEnumeration->nextElement() >>= xParagraph;
+
+ // skip this if its not the only paragraph we want to count
+ if( (nOnlyPara != -1) && (nOnlyPara != nPara ) )
+ continue;
+
+ if( nIterateType == TextAnimationType::BY_PARAGRAPH )
+ {
+ nSubItems++;
+ }
+ else
+ {
+ const OUString aText( xParagraph->getString() );
+ Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW );
+ xSet->getPropertyValue( aStrLocaleName ) >>= aLocale;
+
+ sal_Int32 nPos;
+ const sal_Int32 nEndPos = aText.getLength();
+
+ if( nIterateType == TextAnimationType::BY_WORD )
+ {
+ for( nPos = 0; nPos < nEndPos; nPos++ )
+ {
+ nPos = xBI->getWordBoundary(aText, nPos, aLocale, i18n::WordType::ANY_WORD, sal_True).endPos;
+ nSubItems++;
+ }
+ break;
+ }
+ else
+ {
+ sal_Int32 nDone;
+ for( nPos = 0; nPos < nEndPos; nPos++ )
+ {
+ nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone);
+ nSubItems++;
+ }
+ }
+ }
+
+ if( nPara == nOnlyPara )
+ break;
+
+ nPara++;
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ nSubItems = 0;
+ DBG_ERROR( "sd::CustomAnimationEffect::getNumberOfSubitems(), exception cought!" );
+ }
+
+ return nSubItems;
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffect::~CustomAnimationEffect()
+{
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr CustomAnimationEffect::clone() const
+{
+ Reference< XCloneable > xCloneable( mxNode, UNO_QUERY_THROW );
+ Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW );
+ CustomAnimationEffectPtr pEffect( new CustomAnimationEffect( xNode ) );
+ pEffect->setEffectSequence( getEffectSequence() );
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode )
+{
+ sal_Int16 nNodeType = -1;
+
+ if( xNode.is() )
+ {
+ Sequence< NamedValue > aUserData( xNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ if( nLength )
+ {
+ const NamedValue* p = aUserData.getConstArray();
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "node-type" ) )
+ {
+ p->Value >>= nNodeType;
+ break;
+ }
+ p++;
+ }
+ }
+ }
+
+ return nNodeType;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setPresetClass( sal_Int16 nPresetClass )
+{
+ if( mnPresetClass != nPresetClass )
+ {
+ mnPresetClass = nPresetClass;
+ if( mxNode.is() )
+ {
+ // first try to find a "preset-class" entry in the user data
+ // and change it
+ Sequence< NamedValue > aUserData( mxNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ bool bFound = false;
+ if( nLength )
+ {
+ NamedValue* p = aUserData.getArray();
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "preset-class" ) )
+ {
+ p->Value <<= mnPresetClass;
+ bFound = true;
+ break;
+ }
+ p++;
+ }
+ }
+
+ // no "node-type" entry inside user data, so add it
+ if( !bFound )
+ {
+ nLength = aUserData.getLength();
+ aUserData.realloc( nLength + 1);
+ aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "preset-class" ) );
+ aUserData[nLength].Value <<= mnPresetClass;
+ }
+
+ mxNode->setUserData( aUserData );
+ }
+ }
+}
+
+void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType )
+{
+ if( mnNodeType != nNodeType )
+ {
+ mnNodeType = nNodeType;
+ if( mxNode.is() )
+ {
+ // first try to find a "node-type" entry in the user data
+ // and change it
+ Sequence< NamedValue > aUserData( mxNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ bool bFound = false;
+ if( nLength )
+ {
+ NamedValue* p = aUserData.getArray();
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "node-type" ) )
+ {
+ p->Value <<= mnNodeType;
+ bFound = true;
+ break;
+ }
+ p++;
+ }
+ }
+
+ // no "node-type" entry inside user data, so add it
+ if( !bFound )
+ {
+ nLength = aUserData.getLength();
+ aUserData.realloc( nLength + 1);
+ aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) );
+ aUserData[nLength].Value <<= mnNodeType;
+ }
+
+ mxNode->setUserData( aUserData );
+ }
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId )
+{
+ mnGroupId = nGroupId;
+ if( mxNode.is() )
+ {
+ // first try to find a "group-id" entry in the user data
+ // and change it
+ Sequence< NamedValue > aUserData( mxNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ bool bFound = false;
+ if( nLength )
+ {
+ NamedValue* p = aUserData.getArray();
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "group-id" ) )
+ {
+ p->Value <<= mnGroupId;
+ bFound = true;
+ break;
+ }
+ p++;
+ }
+ }
+
+ // no "node-type" entry inside user data, so add it
+ if( !bFound )
+ {
+ nLength = aUserData.getLength();
+ aUserData.realloc( nLength + 1);
+ aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "group-id" ) );
+ aUserData[nLength].Value <<= mnGroupId;
+ }
+
+ mxNode->setUserData( aUserData );
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** checks if the text for this effect has changed and updates internal flags.
+ returns true if something changed.
+*/
+bool CustomAnimationEffect::checkForText()
+{
+ bool bChange = false;
+
+ Reference< XText > xText;
+
+ if( maTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ // calc para depth
+ ParagraphTarget aParaTarget;
+ maTarget >>= aParaTarget;
+
+ xText = Reference< XText >::query( aParaTarget.Shape );
+
+ // get paragraph
+ if( xText.is() )
+ {
+ Reference< XEnumerationAccess > xEA( xText, UNO_QUERY );
+ if( xEA.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ sal_Bool bHasText = xEnumeration->hasMoreElements();
+ bChange |= bHasText != mbHasText;
+ mbHasText = bHasText;
+
+ sal_Int32 nPara = aParaTarget.Paragraph;
+
+ while( xEnumeration->hasMoreElements() && nPara-- )
+ xEnumeration->nextElement();
+
+ if( xEnumeration->hasMoreElements() )
+ {
+ Reference< XPropertySet > xParaSet;
+ xEnumeration->nextElement() >>= xParaSet;
+ if( xParaSet.is() )
+ {
+ sal_Int32 nParaDepth = 0;
+ const OUString strNumberingLevel( RTL_CONSTASCII_USTRINGPARAM("NumberingLevel") );
+ xParaSet->getPropertyValue( strNumberingLevel ) >>= nParaDepth;
+ bChange |= nParaDepth != mnParaDepth;
+ mnParaDepth = nParaDepth;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ maTarget >>= xText;
+ sal_Bool bHasText = xText.is() && xText->getString().getLength();
+ bChange |= bHasText != mbHasText;
+ mbHasText = bHasText;
+ }
+
+ bChange |= calculateIterateDuration();
+ return bChange;
+}
+
+bool CustomAnimationEffect::calculateIterateDuration()
+{
+ bool bChange = false;
+
+ // if we have an iteration, we must also calculate the
+ // 'true' container duration, that is
+ // ( ( is form animated ) ? [contained effects duration] : 0 ) +
+ // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration]
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
+ if( xIter.is() )
+ {
+ double fDuration = mfDuration;
+ const double fSubEffectDuration = mfDuration;
+
+ if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check
+ {
+ const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType );
+ if( nSubItems )
+ {
+ const double f = (nSubItems-1) * mfIterateInterval;
+ fDuration += f;
+ }
+ }
+
+ // if we also animate the form first, we have to add the
+ // sub effect duration to the whole effect duration
+ if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE )
+ fDuration += fSubEffectDuration;
+
+ bChange |= fDuration != mfAbsoluteDuration;
+ mfAbsoluteDuration = fDuration;
+ }
+
+ return bChange;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setTarget( const ::com::sun::star::uno::Any& rTarget )
+{
+ try
+ {
+ maTarget = rTarget;
+
+ // first, check special case for random node
+ Reference< XInitialization > xInit( mxNode, UNO_QUERY );
+ if( xInit.is() )
+ {
+ const Sequence< Any > aArgs( &maTarget, 1 );
+ xInit->initialize( aArgs );
+ }
+ else
+ {
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
+ if( xIter.is() )
+ {
+ xIter->setTarget(maTarget);
+ }
+ else
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ const Any aElem( xEnumeration->nextElement() );
+ Reference< XAnimate > xAnimate( aElem, UNO_QUERY );
+ if( xAnimate.is() )
+ xAnimate->setTarget( rTarget );
+ else
+ {
+ Reference< XCommand > xCommand( aElem, UNO_QUERY );
+ if( xCommand.is() )
+ xCommand->setTarget( rTarget );
+ }
+ }
+ }
+ }
+ }
+ }
+ checkForText();
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setTarget(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem )
+{
+ try
+ {
+ mnTargetSubItem = nSubItem;
+
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
+ if( xIter.is() )
+ {
+ xIter->setSubItem(mnTargetSubItem);
+ }
+ else
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
+ if( xAnimate.is() )
+ xAnimate->setSubItem( mnTargetSubItem );
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setTargetSubItem(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setDuration( double fDuration )
+{
+ if( (mfDuration != -1.0) && (mfDuration != fDuration) ) try
+ {
+ double fScale = fDuration / mfDuration;
+ mfDuration = fDuration;
+ mfAbsoluteDuration = mfDuration;
+
+ // calculate effect duration and get target shape
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xChildNode.is() )
+ continue;
+
+
+ double fChildBegin = 0.0;
+ xChildNode->getBegin() >>= fChildBegin;
+ if( fChildBegin != 0.0 )
+ {
+ fChildBegin *= fScale;
+ xChildNode->setBegin( makeAny( fChildBegin ) );
+ }
+
+ double fChildDuration = 0.0;
+ xChildNode->getDuration() >>= fChildDuration;
+ if( fChildDuration != 0.0 )
+ {
+ fChildDuration *= fScale;
+ xChildNode->setDuration( makeAny( fChildDuration ) );
+ }
+ }
+ }
+ }
+ calculateIterateDuration();
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setDuration(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setBegin( double fBegin )
+{
+ if( mxNode.is() ) try
+ {
+ mfBegin = fBegin;
+ mxNode->setBegin( makeAny( fBegin ) );
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setBegin(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setAcceleration( double fAcceleration )
+{
+ if( mxNode.is() ) try
+ {
+ mfAcceleration = fAcceleration;
+ mxNode->setAcceleration( fAcceleration );
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setAcceleration(), exception cought!" );
+ }
+}
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setDecelerate( double fDecelerate )
+{
+ if( mxNode.is() ) try
+ {
+ mfDecelerate = fDecelerate;
+ mxNode->setDecelerate( fDecelerate );
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setDecelerate(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setAutoReverse( sal_Bool bAutoReverse )
+{
+ if( mxNode.is() ) try
+ {
+ mbAutoReverse = bAutoReverse;
+ mxNode->setAutoReverse( bAutoReverse );
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::CustomAnimationEffect::setAutoReverse(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::replaceNode( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode )
+{
+ sal_Int16 nNodeType = mnNodeType;
+ Any aTarget = maTarget;
+
+ double fBegin = mfBegin;
+ double fDuration = mfDuration;
+ double fAcceleration = mfAcceleration;
+ double fDecelerate = mfDecelerate ;
+ sal_Bool bAutoReverse = mbAutoReverse;
+ Reference< XAudio > xAudio( mxAudio );
+ sal_Int16 nIterateType = mnIterateType;
+ double fIterateInterval = mfIterateInterval;
+ sal_Int16 nSubItem = mnTargetSubItem;
+
+ setNode( xNode );
+
+ setAudio( xAudio );
+ setNodeType( nNodeType );
+ setTarget( aTarget );
+ setTargetSubItem( nSubItem );
+ setDuration( fDuration );
+ setBegin( fBegin );
+
+ setAcceleration( fAcceleration );
+ setDecelerate( fDecelerate );
+ setAutoReverse( bAutoReverse );
+
+ if( nIterateType != mnIterateType )
+ setIterateType( nIterateType );
+
+ if( mnIterateType && ( fIterateInterval != mfIterateInterval ) )
+ setIterateInterval( fIterateInterval );
+}
+
+// --------------------------------------------------------------------
+
+Reference< XShape > CustomAnimationEffect::getTargetShape() const
+{
+ Reference< XShape > xShape;
+ maTarget >>= xShape;
+ if( !xShape.is() )
+ {
+ ParagraphTarget aParaTarget;
+ if( maTarget >>= aParaTarget )
+ xShape = aParaTarget.Shape;
+ }
+
+ return xShape;
+}
+
+// --------------------------------------------------------------------
+
+Any CustomAnimationEffect::getRepeatCount() const
+{
+ if( mxNode.is() )
+ {
+ return mxNode->getRepeatCount();
+ }
+ else
+ {
+ Any aAny;
+ return aAny;
+ }
+}
+
+// --------------------------------------------------------------------
+
+Any CustomAnimationEffect::getEnd() const
+{
+ if( mxNode.is() )
+ {
+ return mxNode->getEnd();
+ }
+ else
+ {
+ Any aAny;
+ return aAny;
+ }
+}
+
+// --------------------------------------------------------------------
+
+sal_Int16 CustomAnimationEffect::getFill() const
+{
+ if( mxNode.is() )
+ return mxNode->getFill();
+ else
+ return 0;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount )
+{
+ if( mxNode.is() )
+ mxNode->setRepeatCount( rRepeatCount );
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setEnd( const Any& rEnd )
+{
+ if( mxNode.is() )
+ mxNode->setEnd( rEnd );
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setFill( sal_Int16 nFill )
+{
+ if( mxNode.is() )
+ mxNode->setFill( nFill );
+}
+
+// --------------------------------------------------------------------
+
+Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const throw (Exception)
+{
+ DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" );
+
+ Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() );
+
+ const char* pServiceName = maDimColor.hasValue() ?
+ "com.sun.star.animations.AnimateColor" : "com.sun.star.animations.AnimateSet";
+
+ Reference< XAnimate > xAnimate( xMsf->createInstance(OUString::createFromAscii(pServiceName) ), UNO_QUERY_THROW );
+
+ Any aTo;
+ OUString aAttributeName;
+
+ if( maDimColor.hasValue() )
+ {
+ aTo = maDimColor;
+ aAttributeName = OUString( RTL_CONSTASCII_USTRINGPARAM( "DimColor" ) );
+ }
+ else
+ {
+ aTo = makeAny( (sal_Bool)sal_False );
+ aAttributeName = OUString( RTL_CONSTASCII_USTRINGPARAM( "Visibility" ) );
+ }
+
+ Any aBegin;
+ if( !mbAfterEffectOnNextEffect ) // sameClick
+ {
+ Event aEvent;
+
+ aEvent.Source <<= getNode();
+ aEvent.Trigger = EventTrigger::END_EVENT;
+ aEvent.Repeat = 0;
+
+ aBegin <<= aEvent;
+ }
+ else
+ {
+ aBegin <<= (double)0.0;
+ }
+
+ xAnimate->setBegin( aBegin );
+ xAnimate->setTo( aTo );
+ xAnimate->setAttributeName( aAttributeName );
+
+ xAnimate->setDuration( makeAny( (double)0.001 ) );
+ xAnimate->setFill( AnimationFill::HOLD );
+ xAnimate->setTarget( maTarget );
+
+ return Reference< XAnimationNode >( xAnimate, UNO_QUERY_THROW );
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType )
+{
+ if( mnIterateType != nIterateType ) try
+ {
+ // do we need to exchange the container node?
+ if( (mnIterateType == 0) || (nIterateType == 0) )
+ {
+ sal_Int16 nTargetSubItem = mnTargetSubItem;
+
+ Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() );
+ const char * pServiceName =
+ nIterateType ? "com.sun.star.animations.IterateContainer" : "com.sun.star.animations.ParallelTimeContainer";
+ Reference< XTimeContainer > xNewContainer(
+ xMsf->createInstance( OUString::createFromAscii(pServiceName) ), UNO_QUERY_THROW );
+
+ Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW );
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+ xOldContainer->removeChild( xChildNode );
+ xNewContainer->appendChild( xChildNode );
+ }
+
+ Reference< XAnimationNode > xNewNode( xNewContainer, UNO_QUERY_THROW );
+
+ xNewNode->setBegin( mxNode->getBegin() );
+ xNewNode->setDuration( mxNode->getDuration() );
+ xNewNode->setEnd( mxNode->getEnd() );
+ xNewNode->setEndSync( mxNode->getEndSync() );
+ xNewNode->setRepeatCount( mxNode->getRepeatCount() );
+ xNewNode->setFill( mxNode->getFill() );
+ xNewNode->setFillDefault( mxNode->getFillDefault() );
+ xNewNode->setRestart( mxNode->getRestart() );
+ xNewNode->setRestartDefault( mxNode->getRestartDefault() );
+ xNewNode->setAcceleration( mxNode->getAcceleration() );
+ xNewNode->setDecelerate( mxNode->getDecelerate() );
+ xNewNode->setAutoReverse( mxNode->getAutoReverse() );
+ xNewNode->setRepeatDuration( mxNode->getRepeatDuration() );
+ xNewNode->setEndSync( mxNode->getEndSync() );
+ xNewNode->setRepeatCount( mxNode->getRepeatCount() );
+ xNewNode->setUserData( mxNode->getUserData() );
+
+ mxNode = xNewNode;
+
+ Any aTarget;
+ if( nIterateType )
+ {
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
+ xIter->setTarget(maTarget);
+ xIter->setSubItem( nTargetSubItem );
+ }
+ else
+ {
+ aTarget = maTarget;
+ }
+
+ Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_QUERY_THROW );
+ while( xE->hasMoreElements() )
+ {
+ Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY );
+ if( xAnimate.is() )
+ {
+ xAnimate->setTarget( aTarget );
+ xAnimate->setSubItem( nTargetSubItem );
+ }
+ }
+ }
+
+ mnIterateType = nIterateType;
+
+ // if we have an iteration container, we must set its type
+ if( mnIterateType )
+ {
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
+ xIter->setIterateType( nIterateType );
+ }
+
+ checkForText();
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::CustomAnimationEffect::setIterateType(), Exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setIterateInterval( double fIterateInterval )
+{
+ if( mfIterateInterval != fIterateInterval )
+ {
+ Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
+
+ DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" );
+ if( xIter.is() )
+ {
+ mfIterateInterval = fIterateInterval;
+ xIter->setIterateInterval( fIterateInterval );
+ }
+
+ calculateIterateDuration();
+ }
+}
+
+// --------------------------------------------------------------------
+
+::rtl::OUString CustomAnimationEffect::getPath() const
+{
+ ::rtl::OUString aPath;
+
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
+ if( xMotion.is() )
+ {
+ xMotion->getPath() >>= aPath;
+ break;
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::getPath(), exception cought!" );
+ }
+
+ return aPath;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setPath( const ::rtl::OUString& rPath )
+{
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
+ if( xMotion.is() )
+ {
+
+ MainSequenceChangeGuard aGuard( mpEffectSequence );
+ xMotion->setPath( Any( rPath ) );
+ break;
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setPath(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue )
+{
+ Any aProperty;
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
+ {
+ Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xAnimate.is() )
+ continue;
+
+ if( xAnimate->getType() == nNodeType )
+ {
+ if( xAnimate->getAttributeName() == rAttributeName )
+ {
+ switch( eValue )
+ {
+ case VALUE_FROM: aProperty = xAnimate->getFrom(); break;
+ case VALUE_TO: aProperty = xAnimate->getTo(); break;
+ case VALUE_BY: aProperty = xAnimate->getBy(); break;
+ case VALUE_FIRST:
+ case VALUE_LAST:
+ {
+ Sequence<Any> aValues( xAnimate->getValues() );
+ if( aValues.hasElements() )
+ aProperty = aValues[ eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1 ];
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::getProperty(), exception cought!" );
+ }
+
+ return aProperty;
+}
+
+// --------------------------------------------------------------------
+
+bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue, const Any& rValue )
+{
+ bool bChanged = false;
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xAnimate.is() )
+ continue;
+
+ if( xAnimate->getType() == nNodeType )
+ {
+ if( xAnimate->getAttributeName() == rAttributeName )
+ {
+ switch( eValue )
+ {
+ case VALUE_FROM:
+ if( xAnimate->getFrom() != rValue )
+ {
+ xAnimate->setFrom( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_TO:
+ if( xAnimate->getTo() != rValue )
+ {
+ xAnimate->setTo( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_BY:
+ if( xAnimate->getTo() != rValue )
+ {
+ xAnimate->setBy( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_FIRST:
+ case VALUE_LAST:
+ {
+ Sequence<Any> aValues( xAnimate->getValues() );
+ if( !aValues.hasElements() )
+ aValues.realloc(1);
+
+ sal_Int32 nIndex = eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1;
+
+ if( aValues[ nIndex ] != rValue )
+ {
+ aValues[ nIndex ] = rValue;
+ xAnimate->setValues( aValues );
+ bChanged = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setProperty(), exception cought!" );
+ }
+
+ return bChanged;
+}
+
+// --------------------------------------------------------------------
+
+static bool implIsColorAttribute( const OUString& rAttributeName )
+{
+ return rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("FillColor") ) ||
+ rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("LineColor") ) ||
+ rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("CharColor") );
+}
+
+// --------------------------------------------------------------------
+
+Any CustomAnimationEffect::getColor( sal_Int32 nIndex )
+{
+ Any aColor;
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() && !aColor.hasValue() )
+ {
+ Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xAnimate.is() )
+ continue;
+
+ switch( xAnimate->getType() )
+ {
+ case AnimationNodeType::SET:
+ case AnimationNodeType::ANIMATE:
+ if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
+ break;
+ case AnimationNodeType::ANIMATECOLOR:
+ Sequence<Any> aValues( xAnimate->getValues() );
+ if( aValues.hasElements() )
+ {
+ if( aValues.getLength() > nIndex )
+ aColor = aValues[nIndex];
+ }
+ else if( nIndex == 0 )
+ aColor = xAnimate->getFrom();
+ else
+ aColor = xAnimate->getTo();
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::getColor(), exception cought!" );
+ }
+
+ return aColor;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor )
+{
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xAnimate.is() )
+ continue;
+
+ switch( xAnimate->getType() )
+ {
+ case AnimationNodeType::SET:
+ case AnimationNodeType::ANIMATE:
+ if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
+ break;
+ case AnimationNodeType::ANIMATECOLOR:
+ {
+ Sequence<Any> aValues( xAnimate->getValues() );
+ if( aValues.hasElements() )
+ {
+ if( aValues.getLength() > nIndex )
+ {
+ aValues[nIndex] = rColor;
+ xAnimate->setValues( aValues );
+ }
+ }
+ else if( (nIndex == 0) && xAnimate->getFrom().hasValue() )
+ xAnimate->setFrom(rColor);
+ else if( (nIndex == 1) && xAnimate->getTo().hasValue() )
+ xAnimate->setTo(rColor);
+ }
+ break;
+
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setColor(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+Any CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType, EValue eValue )
+{
+ Any aProperty;
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
+ {
+ Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xTransform.is() )
+ continue;
+
+ if( xTransform->getTransformType() == nTransformType )
+ {
+ switch( eValue )
+ {
+ case VALUE_FROM: aProperty = xTransform->getFrom(); break;
+ case VALUE_TO: aProperty = xTransform->getTo(); break;
+ case VALUE_BY: aProperty = xTransform->getBy(); break;
+ case VALUE_FIRST:
+ case VALUE_LAST:
+ {
+ Sequence<Any> aValues( xTransform->getValues() );
+ if( aValues.hasElements() )
+ aProperty = aValues[ eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1 ];
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::getTransformationProperty(), exception cought!" );
+ }
+
+ return aProperty;
+}
+
+// --------------------------------------------------------------------
+
+bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue )
+{
+ bool bChanged = false;
+ if( mxNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
+ if( xEnumerationAccess.is() )
+ {
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
+ if( xEnumeration.is() )
+ {
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
+ if( !xTransform.is() )
+ continue;
+
+ if( xTransform->getTransformType() == nTransformType )
+ {
+ switch( eValue )
+ {
+ case VALUE_FROM:
+ if( xTransform->getFrom() != rValue )
+ {
+ xTransform->setFrom( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_TO:
+ if( xTransform->getTo() != rValue )
+ {
+ xTransform->setTo( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_BY:
+ if( xTransform->getBy() != rValue )
+ {
+ xTransform->setBy( rValue );
+ bChanged = true;
+ }
+ break;
+ case VALUE_FIRST:
+ case VALUE_LAST:
+ {
+ Sequence<Any> aValues( xTransform->getValues() );
+ if( !aValues.hasElements() )
+ aValues.realloc(1);
+
+ sal_Int32 nIndex = eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1;
+ if( aValues[nIndex] != rValue )
+ {
+ aValues[nIndex] = rValue;
+ xTransform->setValues( aValues );
+ bChanged = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setTransformationProperty(), exception cought!" );
+ }
+
+ return bChanged;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::createAudio( const ::com::sun::star::uno::Any& rSource, double fVolume /* = 1.0 */ )
+{
+ DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" );
+
+ if( !mxAudio.is() ) try
+ {
+ Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() );
+ Reference< XAudio > xAudio( xMsf->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.Audio") ) ), UNO_QUERY_THROW );
+ xAudio->setSource( rSource );
+ xAudio->setVolume( fVolume );
+ setAudio( xAudio );
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::createAudio(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode )
+{
+ Reference< XCommand > xCommand;
+
+ if( xRootNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( !xCommand.is() && xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY );
+ if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) )
+ xCommand.set( xNode, UNO_QUERY_THROW );
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::findCommandNode(), exception caught!" );
+ }
+
+ return xCommand;
+}
+
+void CustomAnimationEffect::removeAudio()
+{
+ try
+ {
+ Reference< XAnimationNode > xChild;
+
+ if( mxAudio.is() )
+ {
+ xChild.set( mxAudio, UNO_QUERY );
+ mxAudio.clear();
+ }
+ else if( mnCommand == EffectCommands::STOPAUDIO )
+ {
+ xChild.set( findCommandNode( mxNode ), UNO_QUERY );
+ mnCommand = 0;
+ }
+
+ if( xChild.is() )
+ {
+ Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
+ if( xContainer.is() )
+ xContainer->removeChild( xChild );
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::removeAudio(), exception caught!" );
+ }
+
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setAudio( const Reference< ::com::sun::star::animations::XAudio >& xAudio )
+{
+ if( mxAudio != xAudio ) try
+ {
+ removeAudio();
+ mxAudio = xAudio;
+ Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
+ Reference< XAnimationNode > xChild( mxAudio, UNO_QUERY );
+ if( xContainer.is() && xChild.is() )
+ xContainer->appendChild( xChild );
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setAudio(), exception caught!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::setStopAudio()
+{
+ if( mnCommand != EffectCommands::STOPAUDIO ) try
+ {
+ if( mxAudio.is() )
+ removeAudio();
+
+ Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() );
+ Reference< XCommand > xCommand( xMsf->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.Command") ) ), UNO_QUERY_THROW );
+
+ xCommand->setCommand( EffectCommands::STOPAUDIO );
+
+ Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW );
+ Reference< XAnimationNode > xChild( xCommand, UNO_QUERY_THROW );
+ xContainer->appendChild( xChild );
+
+ mnCommand = EffectCommands::STOPAUDIO;
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::CustomAnimationEffect::setStopAudio(), exception caught!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+bool CustomAnimationEffect::getStopAudio() const
+{
+ return mnCommand == EffectCommands::STOPAUDIO;
+}
+
+// --------------------------------------------------------------------
+
+SdrPathObj* CustomAnimationEffect::createSdrPathObjFromPath()
+{
+ SdrPathObj * pPathObj = new SdrPathObj( OBJ_PATHLINE );
+ updateSdrPathObjFromPath( *pPathObj );
+ return pPathObj;
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj& rPathObj )
+{
+ ::basegfx::B2DPolyPolygon xPolyPoly;
+ if( ::basegfx::tools::importFromSvgD( xPolyPoly, getPath() ) )
+ {
+ SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() );
+ if( pObj )
+ {
+ SdrPage* pPage = pObj->GetPage();
+ if( pPage )
+ {
+ const Size aPageSize( pPage->GetSize() );
+ xPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix((double)aPageSize.Width(), (double)aPageSize.Height()));
+ }
+
+ const Rectangle aBoundRect( pObj->GetCurrentBoundRect() );
+ const Point aCenter( aBoundRect.Center() );
+ xPolyPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y()));
+ }
+ }
+
+ rPathObj.SetPathPoly( xPolyPoly );
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj& rPathObj )
+{
+ ::basegfx::B2DPolyPolygon xPolyPoly( rPathObj.GetPathPoly() );
+
+ SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() );
+ if( pObj )
+ {
+ const Rectangle aBoundRect( pObj->GetCurrentBoundRect() );
+ const Point aCenter( aBoundRect.Center() );
+
+ xPolyPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
+
+ SdrPage* pPage = pObj->GetPage();
+ if( pPage )
+ {
+ const Size aPageSize( pPage->GetSize() );
+ xPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix(
+ 1.0 / (double)aPageSize.Width(), 1.0 / (double)aPageSize.Height()));
+ }
+ }
+
+ setPath( ::basegfx::tools::exportToSvgD( xPolyPoly ) );
+}
+
+// ====================================================================
+
+EffectSequenceHelper::EffectSequenceHelper()
+: mnSequenceType( EffectNodeType::DEFAULT )
+{
+}
+
+// --------------------------------------------------------------------
+
+EffectSequenceHelper::EffectSequenceHelper( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XTimeContainer >& xSequenceRoot )
+: mxSequenceRoot( xSequenceRoot ), mnSequenceType( EffectNodeType::DEFAULT )
+{
+ Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW );
+ create( xNode );
+}
+
+// --------------------------------------------------------------------
+
+EffectSequenceHelper::~EffectSequenceHelper()
+{
+ reset();
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::reset()
+{
+ EffectSequence::iterator aIter( maEffects.begin() );
+ EffectSequence::iterator aEnd( maEffects.end() );
+ if( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect = (*aIter++);
+ pEffect->setEffectSequence(0);
+ }
+ maEffects.clear();
+}
+
+Reference< XAnimationNode > EffectSequenceHelper::getRootNode()
+{
+ Reference< XAnimationNode > xRoot( mxSequenceRoot, UNO_QUERY );
+ return xRoot;
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::append( const CustomAnimationEffectPtr& pEffect )
+{
+ pEffect->setEffectSequence( this );
+ maEffects.push_back(pEffect);
+ rebuild();
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ )
+{
+ CustomAnimationEffectPtr pEffect;
+
+ if( pPreset.get() )
+ {
+ OUString strEmpty;
+ Reference< XAnimationNode > xNode( pPreset->create( strEmpty ) );
+ if( xNode.is() )
+ {
+ // first, filter all only ui relevant user data
+ std::vector< NamedValue > aNewUserData;
+ Sequence< NamedValue > aUserData( xNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ const NamedValue* p = aUserData.getConstArray();
+ bool bFilter = false;
+
+ while( nLength-- )
+ {
+ if( !p->Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "text-only" ) ) &&
+ !p->Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "preset-property" ) ) )
+ {
+ aNewUserData.push_back( *p );
+ bFilter = true;
+ }
+ p++;
+ }
+
+ if( bFilter )
+ {
+ aUserData = ::comphelper::containerToSequence< NamedValue, std::vector< NamedValue > >( aNewUserData );
+ xNode->setUserData( aUserData );
+ }
+
+ // check target, maybe we need to force it to text
+ Any aTarget( rTarget );
+ sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
+
+ if( aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ nSubItem = ShapeAnimationSubType::ONLY_TEXT;
+ }
+ else if( pPreset->isTextOnly() )
+ {
+ Reference< XShape > xShape;
+ aTarget >>= xShape;
+ if( xShape.is() )
+ {
+ // thats bad, we target a shape here but the effect is only for text
+ // so change subitem
+ nSubItem = ShapeAnimationSubType::ONLY_TEXT;
+ }
+ }
+
+ // now create effect from preset
+ pEffect.reset( new CustomAnimationEffect( xNode ) );
+ pEffect->setEffectSequence( this );
+ pEffect->setTarget( aTarget );
+ pEffect->setTargetSubItem( nSubItem );
+ if( fDuration != -1.0 )
+ pEffect->setDuration( fDuration );
+
+ maEffects.push_back(pEffect);
+
+ rebuild();
+ }
+ }
+
+ DBG_ASSERT( pEffect.get(), "sd::EffectSequenceHelper::append(), failed!" );
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */ )
+{
+ CustomAnimationEffectPtr pEffect;
+
+ if( fDuration <= 0.0 )
+ fDuration = 2.0;
+
+ try
+ {
+ Reference< XTimeContainer > xEffectContainer( createParallelTimeContainer() );
+ const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.animations.AnimateMotion" ) );
+ Reference< XAnimationNode > xAnimateMotion( ::comphelper::getProcessServiceFactory()->createInstance(aServiceName), UNO_QUERY_THROW );
+
+ xAnimateMotion->setDuration( Any( fDuration ) );
+ xAnimateMotion->setFill( AnimationFill::HOLD );
+ xEffectContainer->appendChild( xAnimateMotion );
+
+ sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
+
+ if( rTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ nSubItem = ShapeAnimationSubType::ONLY_TEXT;
+
+ Reference< XAnimationNode > xEffectNode( xEffectContainer, UNO_QUERY_THROW );
+ pEffect.reset( new CustomAnimationEffect( xEffectNode ) );
+ pEffect->setEffectSequence( this );
+ pEffect->setTarget( rTarget );
+ pEffect->setTargetSubItem( nSubItem );
+ pEffect->setNodeType( ::com::sun::star::presentation::EffectNodeType::ON_CLICK );
+ pEffect->setPresetClass( ::com::sun::star::presentation::EffectPresetClass::MOTIONPATH );
+ pEffect->setAcceleration( 0.5 );
+ pEffect->setDecelerate( 0.5 );
+ pEffect->setFill( AnimationFill::HOLD );
+ pEffect->setBegin( 0.0 );
+ pEffect->updatePathFromSdrPathObj( rPathObj );
+ if( fDuration != -1.0 )
+ pEffect->setDuration( fDuration );
+
+ maEffects.push_back(pEffect);
+
+ rebuild();
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::EffectSequenceHelper::append(), exception cought!" );
+ }
+
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ )
+{
+ if( pEffect.get() && pPreset.get() ) try
+ {
+ Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) );
+ if( xNewNode.is() )
+ {
+ pEffect->replaceNode( xNewNode );
+ if( fDuration != -1.0 )
+ pEffect->setDuration( fDuration );
+ }
+
+ rebuild();
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::EffectSequenceHelper::replace(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ )
+{
+ OUString strEmpty;
+ replace( pEffect, pPreset, strEmpty, fDuration );
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::remove( const CustomAnimationEffectPtr& pEffect )
+{
+ if( pEffect.get() )
+ {
+ pEffect->setEffectSequence( 0 );
+ maEffects.remove( pEffect );
+ }
+
+ rebuild();
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::rebuild()
+{
+ implRebuild();
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::implRebuild()
+{
+ try
+ {
+ // first we delete all time containers on the first two levels
+ Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+ Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW );
+
+ Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xChildEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW );
+ xChildContainer->removeChild( xNode );
+ }
+
+ mxSequenceRoot->removeChild( xChildNode );
+ }
+
+ // second, rebuild main sequence
+ EffectSequence::iterator aIter( maEffects.begin() );
+ EffectSequence::iterator aEnd( maEffects.end() );
+ if( aIter != aEnd )
+ {
+ AfterEffectNodeList aAfterEffects;
+
+ CustomAnimationEffectPtr pEffect = (*aIter++);
+
+ bool bFirst = true;
+ do
+ {
+ // create a par container for the next click node and all following with and after effects
+ Reference< XTimeContainer > xOnClickContainer( createParallelTimeContainer() );
+
+ Event aEvent;
+ if( mxEventSource.is() )
+ {
+ aEvent.Source <<= mxEventSource;
+ aEvent.Trigger = EventTrigger::ON_CLICK;
+ }
+ else
+ {
+ aEvent.Trigger = EventTrigger::ON_NEXT;
+ }
+ aEvent.Repeat = 0;
+
+ Any aBegin( makeAny( aEvent ) );
+ if( bFirst )
+ {
+ // if the first node is not a click action, this click container
+ // must not have INDEFINITE begin but start at 0s
+ bFirst = false;
+ if( pEffect->getNodeType() != EffectNodeType::ON_CLICK )
+ aBegin <<= (double)0.0;
+ }
+
+ xOnClickContainer->setBegin( aBegin );
+
+ Reference< XAnimationNode > xOnClickContainerNode( xOnClickContainer, UNO_QUERY_THROW );
+ mxSequenceRoot->appendChild( xOnClickContainerNode );
+
+ double fBegin = 0.0;
+
+ do
+ {
+ // create a par container for the current click or after effect node and all following with effects
+ Reference< XTimeContainer > xWithContainer( createParallelTimeContainer() );
+ Reference< XAnimationNode > xWithContainerNode( xWithContainer, UNO_QUERY_THROW );
+ xWithContainer->setBegin( makeAny( fBegin ) );
+ xOnClickContainer->appendChild( xWithContainerNode );
+
+ double fDuration = 0.0;
+ do
+ {
+ Reference< XAnimationNode > xEffectNode( pEffect->getNode() );
+ xWithContainer->appendChild( xEffectNode );
+
+ if( pEffect->hasAfterEffect() )
+ {
+ Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() );
+ AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() );
+ aAfterEffects.push_back( a );
+ }
+
+ double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration();
+ if( fTemp > fDuration )
+ fDuration = fTemp;
+
+ if( aIter != aEnd )
+ pEffect = (*aIter++);
+ else
+ pEffect.reset();
+ }
+ while( pEffect.get() && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) );
+
+ fBegin += fDuration;
+ }
+ while( pEffect.get() && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) );
+ }
+ while( pEffect.get() );
+
+ // process after effect nodes
+ std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func );
+
+ updateTextGroups();
+
+ // reset duration, might have been altered (see below)
+ mxSequenceRoot->setDuration( Any() );
+ }
+ else
+ {
+ // empty sequence, set duration to 0.0 explicitely
+ // (otherwise, this sequence will never end)
+ mxSequenceRoot->setDuration( makeAny((double)0.0) );
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::EffectSequenceHelper::rebuild(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+Reference< XTimeContainer > EffectSequenceHelper::createParallelTimeContainer() const
+{
+ const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.animations.ParallelTimeContainer" ) );
+ return Reference< XTimeContainer >( ::comphelper::getProcessServiceFactory()->createInstance(aServiceName), UNO_QUERY );
+}
+
+// --------------------------------------------------------------------
+
+stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xSearchNode )
+: mxSearchNode( xSearchNode )
+{
+}
+
+// --------------------------------------------------------------------
+
+bool stl_CustomAnimationEffect_search_node_predict::operator()( CustomAnimationEffectPtr pEffect ) const
+{
+ return pEffect->getNode() == mxSearchNode;
+}
+
+// --------------------------------------------------------------------
+
+static bool implFindNextContainer( Reference< XTimeContainer >& xParent, Reference< XTimeContainer >& xCurrent, Reference< XTimeContainer >& xNext )
+ throw(Exception)
+{
+ Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() );
+ if( xEnumeration.is() )
+ {
+ Reference< XInterface > x;
+ while( xEnumeration->hasMoreElements() && !xNext.is() )
+ {
+ if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) )
+ {
+ if( xEnumeration->hasMoreElements() )
+ xEnumeration->nextElement() >>= xNext;
+ }
+ }
+ }
+ return xNext.is();
+}
+
+// --------------------------------------------------------------------
+
+void stl_process_after_effect_node_func(AfterEffectNode& rNode)
+{
+ try
+ {
+ if( rNode.mxNode.is() && rNode.mxMaster.is() )
+ {
+ // set master node
+ Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_QUERY_THROW );
+ Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() );
+ sal_Int32 nSize = aUserData.getLength();
+ aUserData.realloc(nSize+1);
+ aUserData[nSize].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "master-element" ) );
+ aUserData[nSize].Value <<= xMasterNode;
+ rNode.mxNode->setUserData( aUserData );
+
+ // insert after effect node into timeline
+ Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW );
+
+ if( !rNode.mbOnNextEffect ) // sameClick
+ {
+ // insert the aftereffect after its effect is animated
+ xContainer->insertAfter( rNode.mxNode, rNode.mxMaster );
+ }
+ else // nextClick
+ {
+ Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() );
+ // insert the aftereffect in the next group
+
+ Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW );
+ Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW );
+
+ Reference< XTimeContainer > xNextContainer;
+
+ // first try if we have an after effect container
+ if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) )
+ {
+ Reference< XTimeContainer > xNextClickContainer;
+ // if not, try to find the next click effect container
+ if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) )
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ if( xEnumeration->hasMoreElements() )
+ {
+ // the next container is the first child container
+ xEnumeration->nextElement() >>= xNextContainer;
+ }
+ else
+ {
+ // this does not yet have a child container, create one
+ const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.ParallelTimeContainer") );
+ xNextContainer = Reference< XTimeContainer >::query( xMsf->createInstance(aServiceName) );
+
+ if( xNextContainer.is() )
+ {
+ Reference< XAnimationNode > xNode( xNextContainer, UNO_QUERY_THROW );
+ xNode->setBegin( makeAny( (double)0.0 ) );
+// xNode->setFill( AnimationFill::HOLD );
+ xNextClickContainer->appendChild( xNode );
+ }
+ }
+ DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" );
+ }
+ }
+
+ // if we don't have a next container, we add one to the sequence container
+ if( !xNextContainer.is() )
+ {
+ const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.ParallelTimeContainer") );
+ Reference< XTimeContainer > xNewClickContainer( xMsf->createInstance(aServiceName), UNO_QUERY_THROW );
+
+ Reference< XAnimationNode > xNewClickNode( xNewClickContainer, UNO_QUERY_THROW );
+
+ Event aEvent;
+ aEvent.Trigger = EventTrigger::ON_NEXT;
+ aEvent.Repeat = 0;
+ xNewClickNode->setBegin( makeAny( aEvent ) );
+
+ Reference< XAnimationNode > xRefNode( xClickContainer, UNO_QUERY_THROW );
+ xSequenceContainer->insertAfter( xNewClickNode, xRefNode );
+
+ xNextContainer = Reference< XTimeContainer >::query( xMsf->createInstance(aServiceName) );
+
+ DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not create container!" );
+ if( xNextContainer.is() )
+ {
+ Reference< XAnimationNode > xNode( xNextContainer, UNO_QUERY_THROW );
+ xNode->setBegin( makeAny( (double)0.0 ) );
+// xNode->setFill( AnimationFill::HOLD );
+ xNewClickContainer->appendChild( xNode );
+ }
+ }
+
+ if( xNextContainer.is() )
+ {
+ // find begin time of first element
+ Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ if( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChild;
+ // the next container is the first child container
+ xEnumeration->nextElement() >>= xChild;
+ if( xChild.is() )
+ {
+ Any aBegin( xChild->getBegin() );
+ double fBegin = 0.0;
+ if( (aBegin >>= fBegin) && (fBegin >= 0.0))
+ rNode.mxNode->setBegin( aBegin );
+ }
+ }
+
+ xNextContainer->appendChild( rNode.mxNode );
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "ppt::stl_process_after_effect_node_func::operator(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect )
+{
+ return std::find( maEffects.begin(), maEffects.end(), pEffect );
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) const
+{
+ CustomAnimationEffectPtr pEffect;
+
+ EffectSequence::const_iterator aIter( maEffects.begin() );
+ for( ; aIter != maEffects.end(); aIter++ )
+ {
+ if( (*aIter)->getNode() == xNode )
+ {
+ pEffect = (*aIter);
+ break;
+ }
+ }
+
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+sal_Int32 EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr& xEffect ) const
+{
+ sal_Int32 nOffset = 0;
+
+ EffectSequence::const_iterator aIter( maEffects.begin() );
+ for( ; aIter != maEffects.end(); aIter++, nOffset++ )
+ {
+ if( (*aIter) == xEffect )
+ return nOffset;
+ }
+
+ return -1;
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset ) const
+{
+ EffectSequence::const_iterator aIter( maEffects.begin() );
+ while( nOffset-- && aIter != maEffects.end() )
+ aIter++;
+
+ CustomAnimationEffectPtr pEffect;
+ if( aIter != maEffects.end() )
+ pEffect = (*aIter);
+
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape )
+{
+ bool bChanges = false;
+
+ EffectSequence::iterator aIter( maEffects.begin() );
+ while( aIter != maEffects.end() )
+ {
+ if( (*aIter)->getTargetShape() == xShape )
+ {
+ (*aIter)->setEffectSequence( 0 );
+ bChanges = true;
+ aIter = maEffects.erase( aIter );
+ }
+ else
+ {
+ aIter++;
+ }
+ }
+
+ return bChanges;
+}
+
+// --------------------------------------------------------------------
+
+bool EffectSequenceHelper::hasEffect( const com::sun::star::uno::Reference< com::sun::star::drawing::XShape >& xShape )
+{
+ EffectSequence::iterator aIter( maEffects.begin() );
+ while( aIter != maEffects.end() )
+ {
+ if( (*aIter)->getTargetShape() == xShape )
+ return true;
+ aIter++;
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::insertTextRange( const com::sun::star::uno::Any& aTarget )
+{
+ bool bChanges = false;
+
+ ParagraphTarget aParaTarget;
+ if( !(aTarget >>= aParaTarget ) )
+ return;
+
+ EffectSequence::iterator aIter( maEffects.begin() );
+ while( aIter != maEffects.end() )
+ {
+ if( (*aIter)->getTargetShape() == aParaTarget.Shape )
+ bChanges |= (*aIter)->checkForText();
+ aIter++;
+ }
+
+ if( bChanges )
+ rebuild();
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::disposeTextRange( const com::sun::star::uno::Any& aTarget )
+{
+ ParagraphTarget aParaTarget;
+ if( !(aTarget >>= aParaTarget ) )
+ return;
+
+ bool bChanges = false;
+ bool bErased = false;
+
+ EffectSequence::iterator aIter( maEffects.begin() );
+ while( aIter != maEffects.end() )
+ {
+ Any aIterTarget( (*aIter)->getTarget() );
+ if( aIterTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ ParagraphTarget aIterParaTarget;
+ if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) )
+ {
+ if( aIterParaTarget.Paragraph == aParaTarget.Paragraph )
+ {
+ // delete this effect if it targets the disposed paragraph directly
+ (*aIter)->setEffectSequence( 0 );
+ aIter = maEffects.erase( aIter );
+ bChanges = true;
+ bErased = true;
+ }
+ else
+ {
+ if( aIterParaTarget.Paragraph > aParaTarget.Paragraph )
+ {
+ // shift all paragraphs after disposed paragraph
+ aIterParaTarget.Paragraph--;
+ (*aIter)->setTarget( makeAny( aIterParaTarget ) );
+ }
+ }
+ }
+ }
+ else if( (*aIter)->getTargetShape() == aParaTarget.Shape )
+ {
+ bChanges |= (*aIter)->checkForText();
+ }
+
+ if( bErased )
+ bErased = false;
+ else
+ aIter++;
+ }
+
+ if( bChanges )
+ rebuild();
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId )
+: maTarget( rTarget ),
+ mnGroupId( nGroupId )
+{
+ reset();
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationTextGroup::reset()
+{
+ mnTextGrouping = -1;
+ mbAnimateForm = false;
+ mbTextReverse = false;
+ mfGroupingAuto = -1.0;
+ mnLastPara = -1; // used to check for TextReverse
+
+ int i = 5;
+ while( i-- ) mnDepthFlags[i] = 0;
+
+ maEffects.clear();
+}
+
+// --------------------------------------------------------------------
+
+void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr& pEffect )
+{
+ maEffects.push_back( pEffect );
+
+ Any aTarget( pEffect->getTarget() );
+ if( aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ // now look at the paragraph
+ ParagraphTarget aParaTarget;
+ aTarget >>= aParaTarget;
+
+ if( mnLastPara != -1 )
+ mbTextReverse = mnLastPara > aParaTarget.Paragraph;
+
+ mnLastPara = aParaTarget.Paragraph;
+
+ const sal_Int32 nParaDepth = pEffect->getParaDepth();
+
+ // only look at the first 5 levels
+ if( nParaDepth < 5 )
+ {
+ // our first paragraph with this level?
+ if( mnDepthFlags[nParaDepth] == 0 )
+ {
+ // so set it to the first found
+ mnDepthFlags[nParaDepth] = (sal_Int8)pEffect->getNodeType();
+ }
+ else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() )
+ {
+ mnDepthFlags[nParaDepth] = -1;
+ }
+
+ if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS )
+ mfGroupingAuto = pEffect->getBegin();
+
+ mnTextGrouping = 0;
+ while( (mnTextGrouping < 5) && (mnDepthFlags[mnTextGrouping] > 0) )
+ mnTextGrouping++;
+ }
+ }
+ else
+ {
+ // if we have an effect with the shape as a target, we animate the background
+ mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT;
+ }
+}
+
+// --------------------------------------------------------------------
+
+class TextGroupMapImpl : public std::map< sal_Int32, CustomAnimationTextGroup* >
+{
+public:
+ CustomAnimationTextGroup* findGroup( sal_Int32 nGroupId );
+};
+
+// --------------------------------------------------------------------
+
+CustomAnimationTextGroupPtr EffectSequenceHelper::findGroup( sal_Int32 nGroupId )
+{
+ CustomAnimationTextGroupPtr aPtr;
+
+ CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) );
+ if( aIter != maGroupMap.end() )
+ aPtr = (*aIter).second;
+
+ return aPtr;
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::updateTextGroups()
+{
+ maGroupMap.clear();
+
+ // first create all the groups
+ EffectSequence::iterator aIter( maEffects.begin() );
+ const EffectSequence::iterator aEnd( maEffects.end() );
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+
+ const sal_Int32 nGroupId = pEffect->getGroupId();
+
+ if( nGroupId == -1 )
+ continue; // trivial case, no group
+
+ CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId );
+ if( !pGroup.get() )
+ {
+ pGroup.reset( new CustomAnimationTextGroup( pEffect->getTargetShape(), nGroupId ) );
+ maGroupMap[nGroupId] = pGroup;
+ }
+
+ pGroup->addEffect( pEffect );
+ }
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationTextGroupPtr EffectSequenceHelper::createTextGroup( CustomAnimationEffectPtr pEffect, sal_Int32 nTextGrouping, double fTextGroupingAuto, sal_Bool bAnimateForm, sal_Bool bTextReverse )
+{
+ // first finde a free group-id
+ sal_Int32 nGroupId = 0;
+
+ CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() );
+ const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() );
+ while( aIter != aEnd )
+ {
+ if( (*aIter).first == nGroupId )
+ {
+ nGroupId++;
+ aIter = maGroupMap.begin();
+ }
+ else
+ {
+ aIter++;
+ }
+ }
+
+ Reference< XShape > xTarget( pEffect->getTargetShape() );
+
+ CustomAnimationTextGroupPtr pTextGroup( new CustomAnimationTextGroup( xTarget, nGroupId ) );
+ maGroupMap[nGroupId] = pTextGroup;
+
+ bool bUsed = false;
+
+ // do we need to target the shape?
+ if( (nTextGrouping == 0) || bAnimateForm )
+ {
+ sal_Int16 nSubItem;
+ if( nTextGrouping == 0)
+ nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT;
+ else
+ nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND;
+
+ pEffect->setTarget( makeAny( xTarget ) );
+ pEffect->setTargetSubItem( nSubItem );
+ pEffect->setEffectSequence( this );
+ pEffect->setGroupId( nGroupId );
+
+ pTextGroup->addEffect( pEffect );
+ bUsed = true;
+ }
+
+ pTextGroup->mnTextGrouping = nTextGrouping;
+ pTextGroup->mfGroupingAuto = fTextGroupingAuto;
+ pTextGroup->mbTextReverse = bTextReverse;
+
+ // now add an effect for each paragraph
+ createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed );
+
+ notify_listeners();
+
+ return pTextGroup;
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::createTextGroupParagraphEffects( CustomAnimationTextGroupPtr pTextGroup, CustomAnimationEffectPtr pEffect, bool bUsed )
+{
+ Reference< XShape > xTarget( pTextGroup->maTarget );
+
+ sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
+ double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
+ sal_Bool bTextReverse = pTextGroup->mbTextReverse;
+
+ // now add an effect for each paragraph
+ if( nTextGrouping >= 0 ) try
+ {
+ EffectSequence::iterator aInsertIter( find( pEffect ) );
+
+ const OUString strNumberingLevel( RTL_CONSTASCII_USTRINGPARAM("NumberingLevel") );
+ Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_QUERY_THROW );
+
+ std::list< sal_Int16 > aParaList;
+ sal_Int16 nPara;
+
+ // fill the list with all valid paragraphs
+ for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ )
+ {
+ Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY );
+ if( xRange.is() && xRange->getString().getLength() )
+ {
+ if( bTextReverse ) // sort them
+ aParaList.push_front( nPara );
+ else
+ aParaList.push_back( nPara );
+ }
+ }
+
+ ParagraphTarget aTarget;
+ aTarget.Shape = xTarget;
+
+ std::list< sal_Int16 >::iterator aIter( aParaList.begin() );
+ std::list< sal_Int16 >::iterator aEnd( aParaList.end() );
+ while( aIter != aEnd )
+ {
+ aTarget.Paragraph = (*aIter++);
+
+ CustomAnimationEffectPtr pNewEffect;
+ if( bUsed )
+ {
+ // clone a new effect from first effect
+ pNewEffect = pEffect->clone();
+ ++aInsertIter;
+ aInsertIter = maEffects.insert( aInsertIter, pNewEffect );
+ }
+ else
+ {
+ // reuse first effect if its not yet used
+ pNewEffect = pEffect;
+ bUsed = true;
+ aInsertIter = find( pNewEffect );
+ }
+
+ // set target and group-id
+ pNewEffect->setTarget( makeAny( aTarget ) );
+ pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
+ pNewEffect->setGroupId( pTextGroup->mnGroupId );
+ pNewEffect->setEffectSequence( this );
+
+ // set correct node type
+ if( pNewEffect->getParaDepth() < nTextGrouping )
+ {
+ if( fTextGroupingAuto == -1.0 )
+ {
+ pNewEffect->setNodeType( EffectNodeType::ON_CLICK );
+ pNewEffect->setBegin( 0.0 );
+ }
+ else
+ {
+ pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
+ pNewEffect->setBegin( fTextGroupingAuto );
+ }
+ }
+ else
+ {
+ pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
+ pNewEffect->setBegin( 0.0 );
+ }
+
+ pTextGroup->addEffect( pNewEffect );
+ }
+ notify_listeners();
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR("sd::EffectSequenceHelper::createTextGroup(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::setTextGrouping( CustomAnimationTextGroupPtr pTextGroup, sal_Int32 nTextGrouping )
+{
+ if( pTextGroup->mnTextGrouping == nTextGrouping )
+ {
+ // first case, trivial case, do nothing
+ }
+ else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) )
+ {
+ // second case, we need to add new effects for each paragraph
+
+ CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() );
+
+ pTextGroup->mnTextGrouping = nTextGrouping;
+ createTextGroupParagraphEffects( pTextGroup, pEffect, true );
+ notify_listeners();
+ }
+ else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) )
+ {
+ // third case, we need to remove effects for each paragraph
+
+ EffectSequence aEffects( pTextGroup->maEffects );
+ pTextGroup->reset();
+
+ EffectSequence::iterator aIter( aEffects.begin() );
+ const EffectSequence::iterator aEnd( aEffects.end() );
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+
+ if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ remove( pEffect );
+ else
+ pTextGroup->addEffect( pEffect );
+ }
+ notify_listeners();
+ }
+ else
+ {
+ // fourth case, we need to change the node types for the text nodes
+ double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
+
+ EffectSequence aEffects( pTextGroup->maEffects );
+ pTextGroup->reset();
+
+ EffectSequence::iterator aIter( aEffects.begin() );
+ const EffectSequence::iterator aEnd( aEffects.end() );
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+
+ if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ // set correct node type
+ if( pEffect->getParaDepth() < nTextGrouping )
+ {
+ if( fTextGroupingAuto == -1.0 )
+ {
+ pEffect->setNodeType( EffectNodeType::ON_CLICK );
+ pEffect->setBegin( 0.0 );
+ }
+ else
+ {
+ pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
+ pEffect->setBegin( fTextGroupingAuto );
+ }
+ }
+ else
+ {
+ pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
+ pEffect->setBegin( 0.0 );
+ }
+ }
+
+ pTextGroup->addEffect( pEffect );
+
+ }
+ notify_listeners();
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::setAnimateForm( CustomAnimationTextGroupPtr pTextGroup, sal_Bool bAnimateForm )
+{
+ if( pTextGroup->mbAnimateForm == bAnimateForm )
+ {
+ // trivial case, do nothing
+ }
+ else
+ {
+ EffectSequence aEffects( pTextGroup->maEffects );
+ pTextGroup->reset();
+
+ EffectSequence::iterator aIter( aEffects.begin() );
+ const EffectSequence::iterator aEnd( aEffects.end() );
+
+ // first insert if we have to
+ if( bAnimateForm )
+ {
+ EffectSequence::iterator aInsertIter( find( (*aIter) ) );
+
+ CustomAnimationEffectPtr pEffect;
+ if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::getCppuType((const ParagraphTarget*)0) ) )
+ {
+ // special case, only one effect and that targets whole text,
+ // convert this to target whole shape
+ pEffect = (*aIter++);
+ pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE );
+ }
+ else
+ {
+ pEffect = (*aIter)->clone();
+ pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) );
+ pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND );
+ maEffects.insert( aInsertIter, pEffect );
+ }
+
+ pTextGroup->addEffect( pEffect );
+ }
+
+ if( !bAnimateForm && (aEffects.size() == 1) )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter) );
+ pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) );
+ pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
+ pTextGroup->addEffect( pEffect );
+ }
+ else
+ {
+ // readd the rest to the group again
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+
+ if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ pTextGroup->addEffect( pEffect );
+ }
+ else
+ {
+ DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" );
+ remove( pEffect );
+ }
+ }
+ }
+ notify_listeners();
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::setTextGroupingAuto( CustomAnimationTextGroupPtr pTextGroup, double fTextGroupingAuto )
+{
+ sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
+
+ EffectSequence aEffects( pTextGroup->maEffects );
+ pTextGroup->reset();
+
+ EffectSequence::iterator aIter( aEffects.begin() );
+ const EffectSequence::iterator aEnd( aEffects.end() );
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+
+ if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ // set correct node type
+ if( pEffect->getParaDepth() < nTextGrouping )
+ {
+ if( fTextGroupingAuto == -1.0 )
+ {
+ pEffect->setNodeType( EffectNodeType::ON_CLICK );
+ pEffect->setBegin( 0.0 );
+ }
+ else
+ {
+ pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
+ pEffect->setBegin( fTextGroupingAuto );
+ }
+ }
+ else
+ {
+ pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
+ pEffect->setBegin( 0.0 );
+ }
+ }
+
+ pTextGroup->addEffect( pEffect );
+
+ }
+ notify_listeners();
+}
+
+// --------------------------------------------------------------------
+
+struct ImplStlTextGroupSortHelper
+{
+ ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {};
+ bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 );
+ bool mbReverse;
+ sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 );
+};
+
+// --------------------------------------------------------------------
+
+sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 )
+{
+ const Any aTarget(p1->getTarget());
+ if( aTarget.hasValue() && aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
+ {
+ ParagraphTarget aParaTarget;
+ aTarget >>= aParaTarget;
+ return aParaTarget.Paragraph;
+ }
+ else
+ {
+ return mbReverse ? 0x7fffffff : -1;
+ }
+}
+
+// --------------------------------------------------------------------
+
+bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 )
+{
+ if( mbReverse )
+ {
+ return getTargetParagraph( p2 ) < getTargetParagraph( p1 );
+ }
+ else
+ {
+ return getTargetParagraph( p1 ) < getTargetParagraph( p2 );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::setTextReverse( CustomAnimationTextGroupPtr pTextGroup, sal_Bool bTextReverse )
+{
+ if( pTextGroup->mbTextReverse == bTextReverse )
+ {
+ // do nothing
+ }
+ else
+ {
+ std::vector< CustomAnimationEffectPtr > aSortedVector(pTextGroup->maEffects.size());
+ std::copy( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end(), aSortedVector.begin() );
+ ImplStlTextGroupSortHelper aSortHelper( bTextReverse );
+ std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper );
+
+ pTextGroup->reset();
+
+ std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() );
+ const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() );
+
+ if( aIter != aEnd )
+ {
+ pTextGroup->addEffect( (*aIter ) );
+ EffectSequence::iterator aInsertIter( find( (*aIter++) ) );
+ while( aIter != aEnd )
+ {
+ CustomAnimationEffectPtr pEffect( (*aIter++) );
+ maEffects.erase( find( pEffect ) );
+ aInsertIter = maEffects.insert( ++aInsertIter, pEffect );
+ pTextGroup->addEffect( pEffect );
+ }
+ }
+ notify_listeners();
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::addListener( ISequenceListener* pListener )
+{
+ if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() )
+ maListeners.push_back( pListener );
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::removeListener( ISequenceListener* pListener )
+{
+ maListeners.remove( pListener );
+}
+
+// --------------------------------------------------------------------
+
+struct stl_notify_listeners_func : public std::unary_function<ISequenceListener*, void>
+{
+ stl_notify_listeners_func() {}
+ void operator()(ISequenceListener* pListener) { pListener->notify_change(); }
+};
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::notify_listeners()
+{
+ stl_notify_listeners_func aFunc;
+ std::for_each( maListeners.begin(), maListeners.end(), aFunc );
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::create( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode )
+{
+ DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" );
+
+ if( xNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+ createEffectsequence( xChildNode );
+ }
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::EffectSequenceHelper::create(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode )
+{
+ DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" );
+
+ if( xNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+
+ createEffects( xChildNode );
+ }
+ }
+ catch( Exception& )
+ {
+ DBG_ERROR( "sd::EffectSequenceHelper::createEffectsequence(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode )
+{
+ DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" );
+
+ if( xNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+
+ switch( xChildNode->getType() )
+ {
+ // found an effect
+ case AnimationNodeType::PAR:
+ case AnimationNodeType::ITERATE:
+ {
+ CustomAnimationEffectPtr pEffect( new CustomAnimationEffect( xChildNode ) );
+
+ if( pEffect->mnNodeType != -1 )
+ {
+ pEffect->setEffectSequence( this );
+ maEffects.push_back(pEffect);
+ }
+ }
+ break;
+
+ // found an after effect
+ case AnimationNodeType::SET:
+ case AnimationNodeType::ANIMATECOLOR:
+ {
+ processAfterEffect( xChildNode );
+ }
+ break;
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::EffectSequenceHelper::createEffects(), exception cought!" );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode )
+{
+ try
+ {
+ Reference< XAnimationNode > xMaster;
+
+ Sequence< NamedValue > aUserData( xNode->getUserData() );
+ sal_Int32 nLength = aUserData.getLength();
+ const NamedValue* p = aUserData.getConstArray();
+
+ while( nLength-- )
+ {
+ if( p->Name.equalsAscii( "master-element" ) )
+ {
+ p->Value >>= xMaster;
+ break;
+ }
+ p++;
+ }
+
+ // only process if this is a valid after effect
+ if( xMaster.is() )
+ {
+ CustomAnimationEffectPtr pMasterEffect;
+
+ // find the master effect
+ stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster );
+ EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) );
+ if( aIter != maEffects.end() )
+ pMasterEffect = (*aIter );
+
+ if( pMasterEffect.get() )
+ {
+ pMasterEffect->setHasAfterEffect( true );
+
+ // find out what kind of after effect this is
+ if( xNode->getType() == AnimationNodeType::ANIMATECOLOR )
+ {
+ // its a dim
+ Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
+ pMasterEffect->setDimColor( xAnimate->getTo() );
+ pMasterEffect->setAfterEffectOnNext( true );
+ }
+ else
+ {
+ // its a hide
+ Reference< XChild > xNodeChild( xNode, UNO_QUERY_THROW );
+ Reference< XChild > xMasterChild( xMaster, UNO_QUERY_THROW );
+ pMasterEffect->setAfterEffectOnNext( xNodeChild->getParent() != xMasterChild->getParent() );
+ }
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::EffectSequenceHelper::processAfterEffect(), exception cought!" );
+ }
+}
+
+/*
+double EffectSequenceHelper::calculateIterateNodeDuration(
+{
+ Reference< i18n::XBreakIterator > xBI( ImplGetBreakIterator() );
+
+ sal_Int32 nDone;
+ sal_Int32 nNextCellBreak( xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone) );
+ i18n::Boundary nNextWordBoundary( xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True) );
+ sal_Int32 nNextSentenceBreak( xBI->endOfSentence(rTxt, nIdx, rLocale) );
+
+ const sal_Int32 nEndPos( nIdx + nLen );
+ sal_Int32 i, currOffset(0);
+ for( i=nIdx; i<nEndPos; ++i )
+ {
+ // TODO: Check whether position update is valid for CTL/BiDi
+ rOutDev.DrawText( rPos + Point(currOffset,0), rTxt, i, 1 );
+ currOffset = *pDXArray++;
+
+ // issue the comments at the respective break positions
+ if( i == nNextCellBreak )
+ {
+ rMtf.AddAction( new MetaCommentAction( "XTEXT_EOC" ) );
+ nNextCellBreak = xBI->nextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
+ }
+ if( i == nNextWordBoundary.endPos )
+ {
+ rMtf.AddAction( new MetaCommentAction( "XTEXT_EOW" ) );
+ nNextWordBoundary = xBI->getWordBoundary(rTxt, i+1, rLocale, i18n::WordType::ANY_WORD, sal_True);
+ }
+ if( i == nNextSentenceBreak )
+ {
+ rMtf.AddAction( new MetaCommentAction( "XTEXT_EOS" ) );
+ nNextSentenceBreak = xBI->endOfSentence(rTxt, i+1, rLocale);
+ }
+ }
+}
+
+*/
+// ====================================================================
+
+class AnimationChangeListener : public cppu::WeakImplHelper1< XChangesListener >
+{
+public:
+ AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {}
+
+ virtual void SAL_CALL changesOccurred( const ::com::sun::star::util::ChangesEvent& Event ) throw (RuntimeException);
+ virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (RuntimeException);
+private:
+ MainSequence* mpMainSequence;
+};
+
+void SAL_CALL AnimationChangeListener::changesOccurred( const ::com::sun::star::util::ChangesEvent& ) throw (RuntimeException)
+{
+ if( mpMainSequence )
+ mpMainSequence->startRecreateTimer();
+}
+
+void SAL_CALL AnimationChangeListener::disposing( const ::com::sun::star::lang::EventObject& ) throw (RuntimeException)
+{
+}
+
+// ====================================================================
+
+MainSequence::MainSequence()
+: mxTimingRootNode( ::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer"))), UNO_QUERY )
+, mbRebuilding( false )
+, mnRebuildLockGuard( 0 )
+, mbPendingRebuildRequest( false )
+{
+ if( mxTimingRootNode.is() )
+ {
+ Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 );
+ aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) );
+ aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::MAIN_SEQUENCE;
+ mxTimingRootNode->setUserData( aUserData );
+ }
+ init();
+}
+
+// --------------------------------------------------------------------
+
+MainSequence::MainSequence( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode )
+: mxTimingRootNode( xNode, UNO_QUERY )
+, mbRebuilding( false )
+, mnRebuildLockGuard( 0 )
+, mbPendingRebuildRequest( false )
+, mbIgnoreChanges( 0 )
+{
+ init();
+}
+
+// --------------------------------------------------------------------
+
+MainSequence::~MainSequence()
+{
+ reset();
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::init()
+{
+ mnSequenceType = EffectNodeType::MAIN_SEQUENCE;
+
+ maTimer.SetTimeoutHdl( LINK(this, MainSequence, onTimerHdl) );
+ maTimer.SetTimeout(500);
+
+ mxChangesListener.set( new AnimationChangeListener( this ) );
+
+ createMainSequence();
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::reset( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xTimingRootNode )
+{
+ reset();
+
+ mxTimingRootNode.set( xTimingRootNode, UNO_QUERY );
+
+ createMainSequence();
+}
+
+// --------------------------------------------------------------------
+
+Reference< ::com::sun::star::animations::XAnimationNode > MainSequence::getRootNode()
+{
+ DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, ist this really what you want?" );
+
+ if( maTimer.IsActive() && mbTimerMode )
+ {
+ // force a rebuild NOW if one is pending
+ maTimer.Stop();
+ implRebuild();
+ }
+
+ return EffectSequenceHelper::getRootNode();
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::createMainSequence()
+{
+ if( mxTimingRootNode.is() ) try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+ sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode );
+ if( nNodeType == EffectNodeType::MAIN_SEQUENCE )
+ {
+ mxSequenceRoot.set( xChildNode, UNO_QUERY );
+ EffectSequenceHelper::create( xChildNode );
+ }
+ else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE )
+ {
+ Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW );
+ InteractiveSequencePtr pIS( new InteractiveSequence( xInteractiveRoot, this ) );
+ pIS->addListener( this );
+ maInteractiveSequenceList.push_back( pIS );
+ }
+ }
+
+ // see if we have a mainsequence at all. if not, create one...
+ if( !mxSequenceRoot.is() )
+ {
+ mxSequenceRoot = Reference< XTimeContainer >::query(::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer"))));
+ if( mxSequenceRoot.is() )
+ {
+ uno::Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 );
+ aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) );
+ aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::MAIN_SEQUENCE;
+ mxSequenceRoot->setUserData( aUserData );
+
+ // empty sequence until now, set duration to 0.0
+ // explicitely (otherwise, this sequence will never
+ // end)
+ mxSequenceRoot->setDuration( makeAny((double)0.0) );
+
+ Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW );
+ mxTimingRootNode->appendChild( xMainSequenceNode );
+ }
+ }
+
+ updateTextGroups();
+
+ notify_listeners();
+
+ Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
+ if( xNotifier.is() )
+ xNotifier->addChangesListener( mxChangesListener );
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::MainSequence::create(), exception cought!" );
+ return;
+ }
+
+ DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" );
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::reset()
+{
+ EffectSequenceHelper::reset();
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ )
+ (*aIter)->reset();
+ maInteractiveSequenceList.clear();
+
+ try
+ {
+ Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
+ if( xNotifier.is() )
+ xNotifier->removeChangesListener( mxChangesListener );
+ }
+ catch( Exception& )
+ {
+ // ...
+ }
+}
+
+// --------------------------------------------------------------------
+
+InteractiveSequencePtr MainSequence::createInteractiveSequence( const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& xShape )
+{
+ InteractiveSequencePtr pIS;
+
+ // create a new interactive sequence container
+ Reference< XTimeContainer > xISRoot( ::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer"))), UNO_QUERY );
+ DBG_ASSERT( xISRoot.is(), "sd::MainSequence::createInteractiveSequence(), could not create \"com.sun.star.animations.SequenceTimeContainer\"!");
+ if( xISRoot.is() )
+ {
+ uno::Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 );
+ aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) );
+ aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::INTERACTIVE_SEQUENCE ;
+ xISRoot->setUserData( aUserData );
+
+ Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
+ Reference< XAnimationNode > xISNode( xISRoot, UNO_QUERY_THROW );
+ Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
+ xParent->appendChild( xISNode );
+ }
+ pIS.reset( new InteractiveSequence( xISRoot, this) );
+ pIS->setTriggerShape( xShape );
+ pIS->addListener( this );
+ maInteractiveSequenceList.push_back( pIS );
+ return pIS;
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr MainSequence::findEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) const
+{
+ CustomAnimationEffectPtr pEffect = EffectSequenceHelper::findEffect( xNode );
+
+ if( pEffect.get() == 0 )
+ {
+ InteractiveSequenceList::const_iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); (aIter != maInteractiveSequenceList.end()) && (pEffect.get() == 0); aIter++ )
+ {
+ pEffect = (*aIter)->findEffect( xNode );
+ }
+ }
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+sal_Int32 MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr& pEffect ) const
+{
+ sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect );
+
+ if( nOffset != -1 )
+ return nOffset;
+
+ nOffset = EffectSequenceHelper::getCount();
+
+ InteractiveSequenceList::const_iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ )
+ {
+ sal_Int32 nTemp = (*aIter)->getOffsetFromEffect( pEffect );
+ if( nTemp != -1 )
+ return nOffset + nTemp;
+
+ nOffset += (*aIter)->getCount();
+ }
+
+ return -1;
+}
+
+// --------------------------------------------------------------------
+
+CustomAnimationEffectPtr MainSequence::getEffectFromOffset( sal_Int32 nOffset ) const
+{
+ if( nOffset >= 0 )
+ {
+ if( nOffset < getCount() )
+ return EffectSequenceHelper::getEffectFromOffset( nOffset );
+
+ nOffset -= getCount();
+
+ InteractiveSequenceList::const_iterator aIter( maInteractiveSequenceList.begin() );
+
+ while( (aIter != maInteractiveSequenceList.end()) && (nOffset > (*aIter)->getCount()) )
+ nOffset -= (*aIter++)->getCount();
+
+ if( (aIter != maInteractiveSequenceList.end()) && (nOffset >= 0) )
+ return (*aIter)->getEffectFromOffset( nOffset );
+ }
+
+ CustomAnimationEffectPtr pEffect;
+ return pEffect;
+}
+
+// --------------------------------------------------------------------
+
+bool MainSequence::disposeShape( const Reference< XShape >& xShape )
+{
+ bool bChanges = EffectSequenceHelper::disposeShape( xShape );
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); )
+ {
+ if( (*aIter)->getTriggerShape() == xShape )
+ {
+ aIter = maInteractiveSequenceList.erase( aIter );
+ bChanges = true;
+ }
+ else
+ {
+ bChanges |= (*aIter++)->disposeShape( xShape );
+ }
+ }
+
+ if( bChanges )
+ startRebuildTimer();
+
+ return bChanges;
+}
+
+// --------------------------------------------------------------------
+
+bool MainSequence::hasEffect( const com::sun::star::uno::Reference< com::sun::star::drawing::XShape >& xShape )
+{
+ if( EffectSequenceHelper::hasEffect( xShape ) )
+ return true;
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); )
+ {
+ if( (*aIter)->getTriggerShape() == xShape )
+ return true;
+
+ if( (*aIter++)->hasEffect( xShape ) )
+ return true;
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::insertTextRange( const com::sun::star::uno::Any& aTarget )
+{
+ EffectSequenceHelper::insertTextRange( aTarget );
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ )
+ {
+ (*aIter)->insertTextRange( aTarget );
+ }
+}
+// --------------------------------------------------------------------
+
+void MainSequence::disposeTextRange( const com::sun::star::uno::Any& aTarget )
+{
+ EffectSequenceHelper::disposeTextRange( aTarget );
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ )
+ {
+ (*aIter)->disposeTextRange( aTarget );
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** callback from the sd::View when an object just left text edit mode */
+void MainSequence::onTextChanged( const Reference< XShape >& xShape )
+{
+ EffectSequenceHelper::onTextChanged( xShape );
+
+ InteractiveSequenceList::iterator aIter;
+ for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ )
+ {
+ (*aIter)->onTextChanged( xShape );
+ }
+}
+
+// --------------------------------------------------------------------
+
+void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape )
+{
+ bool bChanges = false;
+
+ EffectSequence::iterator aIter;
+ for( aIter = maEffects.begin(); aIter != maEffects.end(); aIter++ )
+ {
+ if( (*aIter)->getTargetShape() == xShape )
+ bChanges |= (*aIter)->checkForText();
+ }
+
+ if( bChanges )
+ EffectSequenceHelper::implRebuild();
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::rebuild()
+{
+ startRebuildTimer();
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::lockRebuilds()
+{
+ mnRebuildLockGuard++;
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::unlockRebuilds()
+{
+ DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" );
+ if( mnRebuildLockGuard )
+ mnRebuildLockGuard--;
+
+ if( (mnRebuildLockGuard == 0) && mbPendingRebuildRequest )
+ {
+ mbPendingRebuildRequest = false;
+ startRebuildTimer();
+ }
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::implRebuild()
+{
+ if( mnRebuildLockGuard )
+ {
+ mbPendingRebuildRequest = true;
+ return;
+ }
+
+ mbRebuilding = true;
+
+ EffectSequenceHelper::implRebuild();
+
+ InteractiveSequenceList::iterator aIter( maInteractiveSequenceList.begin() );
+ const InteractiveSequenceList::iterator aEnd( maInteractiveSequenceList.end() );
+ while( aIter != aEnd )
+ {
+ InteractiveSequencePtr pIS( (*aIter) );
+ if( pIS->maEffects.empty() )
+ {
+ // remove empty interactive sequences
+ aIter = maInteractiveSequenceList.erase( aIter );
+
+ Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
+ Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
+ Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW );
+ xParent->removeChild( xISNode );
+ }
+ else
+ {
+ pIS->implRebuild();
+ aIter++;
+ }
+ }
+
+ notify_listeners();
+ mbRebuilding = false;
+}
+
+// --------------------------------------------------------------------
+
+void MainSequence::notify_change()
+{
+ notify_listeners();
+}
+
+// --------------------------------------------------------------------
+
+bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& xTriggerShape )
+{
+ EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence();
+
+ EffectSequenceHelper* pNewSequence = 0;
+ if( xTriggerShape.is() )
+ {
+ InteractiveSequenceList::iterator aIter( maInteractiveSequenceList.begin() );
+ const InteractiveSequenceList::iterator aEnd( maInteractiveSequenceList.end() );
+ while( aIter != aEnd )
+ {
+ InteractiveSequencePtr pIS( (*aIter++) );
+ if( pIS->getTriggerShape() == xTriggerShape )
+ {
+ pNewSequence = pIS.get();
+ break;
+ }
+ }
+
+ if( !pNewSequence )
+ pNewSequence = createInteractiveSequence( xTriggerShape ).get();
+ }
+ else
+ {
+ pNewSequence = this;
+ }
+
+ if( pOldSequence != pNewSequence )
+ {
+ if( pOldSequence )
+ pOldSequence->maEffects.remove( pEffect );
+ if( pNewSequence )
+ pNewSequence->maEffects.push_back( pEffect );
+ pEffect->setEffectSequence( pNewSequence );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+}
+
+// --------------------------------------------------------------------
+
+IMPL_LINK( MainSequence, onTimerHdl, Timer *, EMPTYARG )
+{
+ if( mbTimerMode )
+ {
+ implRebuild();
+ }
+ else
+ {
+ reset();
+ createMainSequence();
+ }
+
+ return 0;
+}
+
+// --------------------------------------------------------------------
+
+/** starts a timer that recreates the internal structure from the API core after 1 second */
+void MainSequence::startRecreateTimer()
+{
+ if( !mbRebuilding && (mbIgnoreChanges == 0) )
+ {
+ mbTimerMode = false;
+ maTimer.Start();
+ }
+}
+
+// --------------------------------------------------------------------
+
+/** starts a timer that rebuilds the API core from the internal structure after 1 second */
+void MainSequence::startRebuildTimer()
+{
+ mbTimerMode = true;
+ maTimer.Start();
+}
+
+// ====================================================================
+
+InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence )
+: EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence )
+{
+ mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE;
+
+ try
+ {
+ if( mxSequenceRoot.is() )
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
+ Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
+ while( !mxEventSource.is() && xEnumeration->hasMoreElements() )
+ {
+ Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
+
+ Event aEvent;
+ if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) )
+ aEvent.Source >>= mxEventSource;
+ }
+ }
+ }
+ catch( Exception& e )
+ {
+ (void)e;
+ DBG_ERROR( "sd::InteractiveSequence::InteractiveSequence(), exception cought!" );
+ return;
+ }
+}
+
+// --------------------------------------------------------------------
+
+void InteractiveSequence::rebuild()
+{
+ mpMainSequence->rebuild();
+}
+
+void InteractiveSequence::implRebuild()
+{
+ EffectSequenceHelper::implRebuild();
+}
+
+// --------------------------------------------------------------------
+
+MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr& pMainSequence )
+: mpMainSequence( pMainSequence )
+{
+ if( mpMainSequence.get() )
+ mpMainSequence->lockRebuilds();
+}
+
+MainSequenceRebuildGuard::~MainSequenceRebuildGuard()
+{
+ if( mpMainSequence.get() )
+ mpMainSequence->unlockRebuilds();
+}
+
+
+}