1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/debug.hxx>
21 #include <comphelper/diagnose_ex.hxx>
22 #include <sal/log.hxx>
23 #include <com/sun/star/animations/AnimationNodeType.hpp>
24 #include <com/sun/star/animations/AnimateColor.hpp>
25 #include <com/sun/star/animations/AnimateMotion.hpp>
26 #include <com/sun/star/animations/AnimateSet.hpp>
27 #include <com/sun/star/animations/AnimationFill.hpp>
28 #include <com/sun/star/animations/Audio.hpp>
29 #include <com/sun/star/animations/Command.hpp>
30 #include <com/sun/star/animations/Event.hpp>
31 #include <com/sun/star/animations/EventTrigger.hpp>
32 #include <com/sun/star/animations/IterateContainer.hpp>
33 #include <com/sun/star/animations/ParallelTimeContainer.hpp>
34 #include <com/sun/star/animations/SequenceTimeContainer.hpp>
35 #include <com/sun/star/animations/XCommand.hpp>
36 #include <com/sun/star/animations/XIterateContainer.hpp>
37 #include <com/sun/star/animations/XAnimateTransform.hpp>
38 #include <com/sun/star/animations/XAnimateMotion.hpp>
39 #include <com/sun/star/animations/XAnimate.hpp>
40 #include <com/sun/star/animations/AnimationRestart.hpp>
41 #include <com/sun/star/beans/NamedValue.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/container/XEnumerationAccess.hpp>
44 #include <com/sun/star/lang/XInitialization.hpp>
45 #include <com/sun/star/presentation/EffectNodeType.hpp>
46 #include <com/sun/star/presentation/EffectCommands.hpp>
47 #include <com/sun/star/presentation/EffectPresetClass.hpp>
48 #include <com/sun/star/presentation/ParagraphTarget.hpp>
49 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
50 #include <com/sun/star/text/XText.hpp>
51 #include <com/sun/star/util/XCloneable.hpp>
52 #include <com/sun/star/util/XChangesNotifier.hpp>
53 #include <comphelper/processfactory.hxx>
54 #include <comphelper/sequence.hxx>
55 #include <com/sun/star/lang/Locale.hpp>
56 #include <com/sun/star/i18n/BreakIterator.hpp>
57 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
58 #include <com/sun/star/i18n/WordType.hpp>
59 #include <com/sun/star/presentation/TextAnimationType.hpp>
61 #include <basegfx/polygon/b2dpolypolygon.hxx>
62 #include <basegfx/polygon/b2dpolypolygontools.hxx>
63 #include <basegfx/range/b2drange.hxx>
64 #include <basegfx/matrix/b2dhommatrixtools.hxx>
70 #include <cppuhelper/implbase.hxx>
72 #include <drawinglayer/geometry/viewinformation2d.hxx>
73 #include <o3tl/safeint.hxx>
74 #include <svx/sdr/contact/viewcontact.hxx>
75 #include <svx/svdopath.hxx>
76 #include <svx/svdpage.hxx>
77 #include <CustomAnimationEffect.hxx>
78 #include <CustomAnimationPreset.hxx>
79 #include <animations.hxx>
82 using namespace ::com::sun::star
;
83 using namespace ::com::sun::star::uno
;
84 using namespace ::com::sun::star::presentation
;
85 using namespace ::com::sun::star::animations
;
87 using ::com::sun::star::container::XEnumerationAccess
;
88 using ::com::sun::star::container::XEnumeration
;
89 using ::com::sun::star::beans::NamedValue
;
90 using ::com::sun::star::container::XChild
;
91 using ::com::sun::star::drawing::XShape
;
92 using ::com::sun::star::lang::XInitialization
;
93 using ::com::sun::star::text::XText
;
94 using ::com::sun::star::text::XTextRange
;
95 using ::com::sun::star::beans::XPropertySet
;
96 using ::com::sun::star::util::XCloneable
;
97 using ::com::sun::star::lang::Locale
;
98 using ::com::sun::star::util::XChangesNotifier
;
99 using ::com::sun::star::util::XChangesListener
;
103 class MainSequenceChangeGuard
106 explicit MainSequenceChangeGuard( EffectSequenceHelper
* pSequence
)
108 mpMainSequence
= dynamic_cast< MainSequence
* >( pSequence
);
109 if( mpMainSequence
== nullptr )
111 InteractiveSequence
* pI
= dynamic_cast< InteractiveSequence
* >( pSequence
);
113 mpMainSequence
= pI
->mpMainSequence
;
115 DBG_ASSERT( mpMainSequence
, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" );
118 mpMainSequence
->mbIgnoreChanges
++;
121 ~MainSequenceChangeGuard()
124 mpMainSequence
->mbIgnoreChanges
++;
128 MainSequence
* mpMainSequence
;
131 CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
134 mnFill(AnimationFill::HOLD
),
137 mfAbsoluteDuration(-1.0),
140 mfIterateInterval(0.0),
143 mfAcceleration( 1.0 ),
145 mbAutoReverse(false),
148 mpEffectSequence( nullptr ),
149 mbHasAfterEffect(false),
150 mbAfterEffectOnNextEffect(false)
155 void CustomAnimationEffect::setNode( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
161 const Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
163 for( const NamedValue
& rProp
: aUserData
)
165 if ( rProp
.Name
== "node-type" )
167 rProp
.Value
>>= mnNodeType
;
169 else if ( rProp
.Name
== "preset-id" )
171 rProp
.Value
>>= maPresetId
;
173 else if ( rProp
.Name
== "preset-sub-type" )
175 rProp
.Value
>>= maPresetSubType
;
177 else if ( rProp
.Name
== "preset-class" )
179 rProp
.Value
>>= mnPresetClass
;
181 else if ( rProp
.Name
== "preset-property" )
183 rProp
.Value
>>= maProperty
;
185 else if ( rProp
.Name
== "group-id" )
187 rProp
.Value
>>= mnGroupId
;
191 // get effect start time
192 mxNode
->getBegin() >>= mfBegin
;
194 mfAcceleration
= mxNode
->getAcceleration();
195 mfDecelerate
= mxNode
->getDecelerate();
196 mbAutoReverse
= mxNode
->getAutoReverse();
198 mnFill
= mxNode
->getFill();
200 // get iteration data
201 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
204 mfIterateInterval
= xIter
->getIterateInterval();
205 mnIterateType
= xIter
->getIterateType();
206 maTarget
= xIter
->getTarget();
207 mnTargetSubItem
= xIter
->getSubItem();
211 mfIterateInterval
= 0.0f
;
215 // calculate effect duration and get target shape
216 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
217 if( xEnumerationAccess
.is() )
219 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
220 if( xEnumeration
.is() )
222 while( xEnumeration
->hasMoreElements() )
224 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY
);
225 if( !xChildNode
.is() )
228 if( xChildNode
->getType() == AnimationNodeType::AUDIO
)
230 mxAudio
.set( xChildNode
, UNO_QUERY
);
232 else if( xChildNode
->getType() == AnimationNodeType::COMMAND
)
234 Reference
< XCommand
> xCommand( xChildNode
, UNO_QUERY
);
237 mnCommand
= xCommand
->getCommand();
238 if( !maTarget
.hasValue() )
239 maTarget
= xCommand
->getTarget();
245 double fDuration
= 0.0;
246 xChildNode
->getBegin() >>= fBegin
;
247 xChildNode
->getDuration() >>= fDuration
;
250 if( fDuration
> mfDuration
)
251 mfDuration
= fDuration
;
253 // no target shape yet?
254 if( !maTarget
.hasValue() )
257 Reference
< XAnimate
> xAnimate( xChildNode
, UNO_QUERY
);
260 maTarget
= xAnimate
->getTarget();
261 mnTargetSubItem
= xAnimate
->getSubItem();
269 mfAbsoluteDuration
= mfDuration
;
270 double fRepeatCount
= 1.0;
271 if( (mxNode
->getRepeatCount()) >>= fRepeatCount
)
272 mfAbsoluteDuration
*= fRepeatCount
;
277 sal_Int32
CustomAnimationEffect::getNumberOfSubitems( const Any
& aTarget
, sal_Int16 nIterateType
)
279 sal_Int32 nSubItems
= 0;
283 // first get target text
284 sal_Int32 nOnlyPara
= -1;
286 Reference
< XText
> xShape
;
290 ParagraphTarget aParaTarget
;
291 if( aTarget
>>= aParaTarget
)
293 xShape
.set( aParaTarget
.Shape
, UNO_QUERY
);
294 nOnlyPara
= aParaTarget
.Paragraph
;
298 // now use the break iterator to iterate over the given text
299 // and count the sub items
303 // TODO/LATER: Optimize this, don't create a break iterator each time
304 Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
305 Reference
< i18n::XBreakIterator
> xBI
= i18n::BreakIterator::create(xContext
);
307 Reference
< XEnumerationAccess
> xEA( xShape
, UNO_QUERY_THROW
);
308 Reference
< XEnumeration
> xEnumeration( xEA
->createEnumeration(), UNO_SET_THROW
);
309 css::lang::Locale aLocale
;
310 static const OUStringLiteral
aStrLocaleName( u
"CharLocale" );
311 Reference
< XTextRange
> xParagraph
;
314 while( xEnumeration
->hasMoreElements() )
316 xEnumeration
->nextElement() >>= xParagraph
;
318 // skip this if it's not the only paragraph we want to count
319 if( (nOnlyPara
!= -1) && (nOnlyPara
!= nPara
) )
322 if( nIterateType
== TextAnimationType::BY_PARAGRAPH
)
328 const OUString
aText( xParagraph
->getString() );
329 Reference
< XPropertySet
> xSet( xParagraph
, UNO_QUERY_THROW
);
330 xSet
->getPropertyValue( aStrLocaleName
) >>= aLocale
;
333 const sal_Int32 nEndPos
= aText
.getLength();
335 if( nIterateType
== TextAnimationType::BY_WORD
)
337 for( nPos
= 0; nPos
< nEndPos
; nPos
++ )
339 nPos
= xBI
->getWordBoundary(aText
, nPos
, aLocale
, i18n::WordType::ANY_WORD
, true).endPos
;
347 for( nPos
= 0; nPos
< nEndPos
; nPos
++ )
349 nPos
= xBI
->nextCharacters(aText
, nPos
, aLocale
, i18n::CharacterIteratorMode::SKIPCELL
, 0, nDone
);
355 if( nPara
== nOnlyPara
)
365 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" );
371 CustomAnimationEffect::~CustomAnimationEffect()
375 CustomAnimationEffectPtr
CustomAnimationEffect::clone() const
377 Reference
< XCloneable
> xCloneable( mxNode
, UNO_QUERY_THROW
);
378 Reference
< XAnimationNode
> xNode( xCloneable
->createClone(), UNO_QUERY_THROW
);
379 CustomAnimationEffectPtr pEffect
= std::make_shared
<CustomAnimationEffect
>( xNode
);
380 pEffect
->setEffectSequence( getEffectSequence() );
384 sal_Int32
CustomAnimationEffect::get_node_type( const Reference
< XAnimationNode
>& xNode
)
386 sal_Int16 nNodeType
= -1;
390 const Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
391 if( aUserData
.hasElements() )
393 const NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
394 [](const NamedValue
& rProp
) { return rProp
.Name
== "node-type"; });
395 if (pProp
!= aUserData
.end())
396 pProp
->Value
>>= nNodeType
;
403 void CustomAnimationEffect::setPresetClassAndId( sal_Int16 nPresetClass
, const OUString
& rPresetId
)
405 if( mnPresetClass
== nPresetClass
&& maPresetId
== rPresetId
)
408 mnPresetClass
= nPresetClass
;
409 maPresetId
= rPresetId
;
413 // first try to find a "preset-class" entry in the user data
415 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
416 sal_Int32 nLength
= aUserData
.getLength();
417 bool bFoundPresetClass
= false;
418 bool bFoundPresetId
= false;
421 auto [begin
, end
] = asNonConstRange(aUserData
);
422 NamedValue
* pProp
= std::find_if(begin
, end
,
423 [](const NamedValue
& rProp
) { return rProp
.Name
== "preset-class"; });
426 pProp
->Value
<<= mnPresetClass
;
427 bFoundPresetClass
= true;
430 pProp
= std::find_if(begin
, end
,
431 [](const NamedValue
& rProp
) { return rProp
.Name
== "preset-id"; });
434 pProp
->Value
<<= mnPresetClass
;
435 bFoundPresetId
= true;
439 // no "preset-class" entry inside user data, so add it
440 if( !bFoundPresetClass
)
442 aUserData
.realloc( nLength
+ 1);
443 auto& el
= aUserData
.getArray()[nLength
];
444 el
.Name
= "preset-class";
445 el
.Value
<<= mnPresetClass
;
449 if( !bFoundPresetId
&& maPresetId
.getLength() > 0 )
451 aUserData
.realloc( nLength
+ 1);
452 auto& el
= aUserData
.getArray()[nLength
];
453 el
.Name
= "preset-id";
454 el
.Value
<<= maPresetId
;
457 mxNode
->setUserData( aUserData
);
460 void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType
)
462 if( mnNodeType
== nNodeType
)
465 mnNodeType
= nNodeType
;
469 // first try to find a "node-type" entry in the user data
471 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
472 sal_Int32 nLength
= aUserData
.getLength();
476 auto [begin
, end
] = asNonConstRange(aUserData
);
477 NamedValue
* pProp
= std::find_if(begin
, end
,
478 [](const NamedValue
& rProp
) { return rProp
.Name
== "node-type"; });
481 pProp
->Value
<<= mnNodeType
;
486 // no "node-type" entry inside user data, so add it
489 aUserData
.realloc( nLength
+ 1);
490 auto& el
= aUserData
.getArray()[nLength
];
491 el
.Name
= "node-type";
492 el
.Value
<<= mnNodeType
;
495 mxNode
->setUserData( aUserData
);
498 void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId
)
500 mnGroupId
= nGroupId
;
504 // first try to find a "group-id" entry in the user data
506 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
507 sal_Int32 nLength
= aUserData
.getLength();
511 auto [begin
, end
] = asNonConstRange(aUserData
);
512 NamedValue
* pProp
= std::find_if(begin
, end
,
513 [](const NamedValue
& rProp
) { return rProp
.Name
== "group-id"; });
516 pProp
->Value
<<= mnGroupId
;
521 // no "group-id" entry inside user data, so add it
524 aUserData
.realloc( nLength
+ 1);
525 auto& el
= aUserData
.getArray()[nLength
];
526 el
.Name
= "group-id";
527 el
.Value
<<= mnGroupId
;
530 mxNode
->setUserData( aUserData
);
533 /** checks if the text for this effect has changed and updates internal flags.
534 returns true if something changed.
536 bool CustomAnimationEffect::checkForText( const std::vector
<sal_Int32
>* paragraphNumberingLevel
)
538 bool bChange
= false;
540 Reference
< XText
> xText
;
542 if( maTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
545 ParagraphTarget aParaTarget
;
546 maTarget
>>= aParaTarget
;
548 xText
.set( aParaTarget
.Shape
, UNO_QUERY
);
553 sal_Int32 nPara
= aParaTarget
.Paragraph
;
555 bool bHasText
= false;
556 sal_Int32 nParaDepth
= 0;
558 if ( paragraphNumberingLevel
)
560 bHasText
= !paragraphNumberingLevel
->empty();
561 if (nPara
>= 0 && o3tl::make_unsigned(nPara
) < paragraphNumberingLevel
->size())
562 nParaDepth
= paragraphNumberingLevel
->at(nPara
);
566 Reference
< XEnumerationAccess
> xEA( xText
, UNO_QUERY
);
569 Reference
< XEnumeration
> xEnumeration
= xEA
->createEnumeration();
570 if( xEnumeration
.is() )
572 bHasText
= xEnumeration
->hasMoreElements();
574 while( xEnumeration
->hasMoreElements() && nPara
-- )
575 xEnumeration
->nextElement();
577 if( xEnumeration
->hasMoreElements() )
579 Reference
< XPropertySet
> xParaSet
;
580 xEnumeration
->nextElement() >>= xParaSet
;
583 xParaSet
->getPropertyValue( "NumberingLevel" ) >>= nParaDepth
;
592 bChange
|= bHasText
!= mbHasText
;
593 mbHasText
= bHasText
;
595 bChange
|= nParaDepth
!= mnParaDepth
;
596 mnParaDepth
= nParaDepth
;
603 bool bHasText
= xText
.is() && !xText
->getString().isEmpty();
604 bChange
|= bHasText
!= mbHasText
;
605 mbHasText
= bHasText
;
608 bChange
|= calculateIterateDuration();
612 bool CustomAnimationEffect::calculateIterateDuration()
614 bool bChange
= false;
616 // if we have an iteration, we must also calculate the
617 // 'true' container duration, that is
618 // ( ( is form animated ) ? [contained effects duration] : 0 ) +
619 // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration]
620 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
623 double fDuration
= mfDuration
;
624 const double fSubEffectDuration
= mfDuration
;
626 if( mnTargetSubItem
!= ShapeAnimationSubType::ONLY_BACKGROUND
) // does not make sense for iterate container but better check
628 const sal_Int32 nSubItems
= getNumberOfSubitems( maTarget
, mnIterateType
);
631 const double f
= (nSubItems
-1) * mfIterateInterval
;
636 // if we also animate the form first, we have to add the
637 // sub effect duration to the whole effect duration
638 if( mnTargetSubItem
== ShapeAnimationSubType::AS_WHOLE
)
639 fDuration
+= fSubEffectDuration
;
641 bChange
|= fDuration
!= mfAbsoluteDuration
;
642 mfAbsoluteDuration
= fDuration
;
648 void CustomAnimationEffect::setTarget( const css::uno::Any
& rTarget
)
654 // first, check special case for random node
655 Reference
< XInitialization
> xInit( mxNode
, UNO_QUERY
);
658 const Sequence
< Any
> aArgs( &maTarget
, 1 );
659 xInit
->initialize( aArgs
);
663 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
666 xIter
->setTarget(maTarget
);
670 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
671 if( xEnumerationAccess
.is() )
673 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
674 if( xEnumeration
.is() )
676 while( xEnumeration
->hasMoreElements() )
678 const Any
aElem( xEnumeration
->nextElement() );
679 Reference
< XAnimate
> xAnimate( aElem
, UNO_QUERY
);
681 xAnimate
->setTarget( rTarget
);
684 Reference
< XCommand
> xCommand( aElem
, UNO_QUERY
);
686 xCommand
->setTarget( rTarget
);
697 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTarget()" );
701 void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem
)
705 mnTargetSubItem
= nSubItem
;
707 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
710 xIter
->setSubItem(mnTargetSubItem
);
714 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
715 if( xEnumerationAccess
.is() )
717 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
718 if( xEnumeration
.is() )
720 while( xEnumeration
->hasMoreElements() )
722 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
724 xAnimate
->setSubItem( mnTargetSubItem
);
732 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTargetSubItem()" );
736 void CustomAnimationEffect::setDuration( double fDuration
)
738 if( (mfDuration
== -1.0) || (mfDuration
== fDuration
) )
743 double fScale
= fDuration
/ mfDuration
;
744 mfDuration
= fDuration
;
745 double fRepeatCount
= 1.0;
746 getRepeatCount() >>= fRepeatCount
;
747 mfAbsoluteDuration
= mfDuration
* fRepeatCount
;
749 // calculate effect duration and get target shape
750 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
751 if( xEnumerationAccess
.is() )
753 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
754 if( xEnumeration
.is() )
756 while( xEnumeration
->hasMoreElements() )
758 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY
);
759 if( !xChildNode
.is() )
762 double fChildBegin
= 0.0;
763 xChildNode
->getBegin() >>= fChildBegin
;
764 if( fChildBegin
!= 0.0 )
766 fChildBegin
*= fScale
;
767 xChildNode
->setBegin( Any( fChildBegin
) );
770 double fChildDuration
= 0.0;
771 xChildNode
->getDuration() >>= fChildDuration
;
772 if( fChildDuration
!= 0.0 )
774 fChildDuration
*= fScale
;
775 xChildNode
->setDuration( Any( fChildDuration
) );
780 calculateIterateDuration();
784 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDuration()" );
788 void CustomAnimationEffect::setBegin( double fBegin
)
790 if( mxNode
.is() ) try
793 mxNode
->setBegin( Any( fBegin
) );
797 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setBegin()" );
801 void CustomAnimationEffect::setAcceleration( double fAcceleration
)
803 if( mxNode
.is() ) try
805 mfAcceleration
= fAcceleration
;
806 mxNode
->setAcceleration( fAcceleration
);
810 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAcceleration()" );
814 void CustomAnimationEffect::setDecelerate( double fDecelerate
)
816 if( mxNode
.is() ) try
818 mfDecelerate
= fDecelerate
;
819 mxNode
->setDecelerate( fDecelerate
);
823 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDecelerate()" );
827 void CustomAnimationEffect::setAutoReverse( bool bAutoReverse
)
829 if( mxNode
.is() ) try
831 mbAutoReverse
= bAutoReverse
;
832 mxNode
->setAutoReverse( bAutoReverse
);
836 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAutoReverse()" );
840 void CustomAnimationEffect::replaceNode( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
842 sal_Int16 nNodeType
= mnNodeType
;
843 Any aTarget
= maTarget
;
845 sal_Int16 nFill
= mnFill
;
846 double fBegin
= mfBegin
;
847 double fDuration
= mfDuration
;
848 double fAcceleration
= mfAcceleration
;
849 double fDecelerate
= mfDecelerate
;
850 bool bAutoReverse
= mbAutoReverse
;
851 Reference
< XAudio
> xAudio( mxAudio
);
852 sal_Int16 nIterateType
= mnIterateType
;
853 double fIterateInterval
= mfIterateInterval
;
854 sal_Int16 nSubItem
= mnTargetSubItem
;
859 setNodeType( nNodeType
);
860 setTarget( aTarget
);
861 setTargetSubItem( nSubItem
);
862 setDuration( fDuration
);
866 setAcceleration( fAcceleration
);
867 setDecelerate( fDecelerate
);
868 setAutoReverse( bAutoReverse
);
870 if( nIterateType
!= mnIterateType
)
871 setIterateType( nIterateType
);
873 if( mnIterateType
&& ( fIterateInterval
!= mfIterateInterval
) )
874 setIterateInterval( fIterateInterval
);
877 Reference
< XShape
> CustomAnimationEffect::getTargetShape() const
879 Reference
< XShape
> xShape
;
883 ParagraphTarget aParaTarget
;
884 if( maTarget
>>= aParaTarget
)
885 xShape
= aParaTarget
.Shape
;
891 Any
CustomAnimationEffect::getRepeatCount() const
895 return mxNode
->getRepeatCount();
904 Any
CustomAnimationEffect::getEnd() const
908 return mxNode
->getEnd();
917 void CustomAnimationEffect::setRepeatCount( const Any
& rRepeatCount
)
921 mxNode
->setRepeatCount( rRepeatCount
);
922 double fRepeatCount
= 1.0;
923 rRepeatCount
>>= fRepeatCount
;
924 mfAbsoluteDuration
= mfDuration
* fRepeatCount
;
928 void CustomAnimationEffect::setEnd( const Any
& rEnd
)
931 mxNode
->setEnd( rEnd
);
934 void CustomAnimationEffect::setFill( sal_Int16 nFill
)
939 mxNode
->setFill( nFill
);
943 Reference
< XAnimationNode
> CustomAnimationEffect::createAfterEffectNode() const
945 DBG_ASSERT( mbHasAfterEffect
, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" );
947 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
949 Reference
< XAnimate
> xAnimate
;
950 if( maDimColor
.hasValue() )
951 xAnimate
= AnimateColor::create( xContext
);
953 xAnimate
= AnimateSet::create( xContext
);
956 OUString aAttributeName
;
958 if( maDimColor
.hasValue() )
961 aAttributeName
= "DimColor";
966 aAttributeName
= "Visibility";
970 if( !mbAfterEffectOnNextEffect
) // sameClick
974 aEvent
.Source
<<= getNode();
975 aEvent
.Trigger
= EventTrigger::END_EVENT
;
985 xAnimate
->setBegin( aBegin
);
986 xAnimate
->setTo( aTo
);
987 xAnimate
->setAttributeName( aAttributeName
);
989 xAnimate
->setDuration( Any( 0.001 ) );
990 xAnimate
->setFill( AnimationFill::HOLD
);
991 xAnimate
->setTarget( maTarget
);
996 void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType
)
998 if( mnIterateType
== nIterateType
)
1003 // do we need to exchange the container node?
1004 if( (mnIterateType
== 0) || (nIterateType
== 0) )
1006 sal_Int16 nTargetSubItem
= mnTargetSubItem
;
1008 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1009 Reference
< XTimeContainer
> xNewContainer
;
1012 xNewContainer
.set( IterateContainer::create( xContext
) );
1015 xNewContainer
.set( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
1017 Reference
< XTimeContainer
> xOldContainer( mxNode
, UNO_QUERY_THROW
);
1018 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
1019 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1020 while( xEnumeration
->hasMoreElements() )
1022 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
1023 xOldContainer
->removeChild( xChildNode
);
1024 xNewContainer
->appendChild( xChildNode
);
1027 xNewContainer
->setBegin( mxNode
->getBegin() );
1028 xNewContainer
->setDuration( mxNode
->getDuration() );
1029 xNewContainer
->setEnd( mxNode
->getEnd() );
1030 xNewContainer
->setEndSync( mxNode
->getEndSync() );
1031 xNewContainer
->setRepeatCount( mxNode
->getRepeatCount() );
1032 xNewContainer
->setFill( mxNode
->getFill() );
1033 xNewContainer
->setFillDefault( mxNode
->getFillDefault() );
1034 xNewContainer
->setRestart( mxNode
->getRestart() );
1035 xNewContainer
->setRestartDefault( mxNode
->getRestartDefault() );
1036 xNewContainer
->setAcceleration( mxNode
->getAcceleration() );
1037 xNewContainer
->setDecelerate( mxNode
->getDecelerate() );
1038 xNewContainer
->setAutoReverse( mxNode
->getAutoReverse() );
1039 xNewContainer
->setRepeatDuration( mxNode
->getRepeatDuration() );
1040 xNewContainer
->setEndSync( mxNode
->getEndSync() );
1041 xNewContainer
->setRepeatCount( mxNode
->getRepeatCount() );
1042 xNewContainer
->setUserData( mxNode
->getUserData() );
1044 mxNode
= xNewContainer
;
1049 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY_THROW
);
1050 xIter
->setTarget(maTarget
);
1051 xIter
->setSubItem( nTargetSubItem
);
1058 Reference
< XEnumerationAccess
> xEA( mxNode
, UNO_QUERY_THROW
);
1059 Reference
< XEnumeration
> xE( xEA
->createEnumeration(), UNO_SET_THROW
);
1060 while( xE
->hasMoreElements() )
1062 Reference
< XAnimate
> xAnimate( xE
->nextElement(), UNO_QUERY
);
1065 xAnimate
->setTarget( aTarget
);
1066 xAnimate
->setSubItem( nTargetSubItem
);
1071 mnIterateType
= nIterateType
;
1073 // if we have an iteration container, we must set its type
1076 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY_THROW
);
1077 xIter
->setIterateType( nIterateType
);
1084 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setIterateType()" );
1088 void CustomAnimationEffect::setIterateInterval( double fIterateInterval
)
1090 if( mfIterateInterval
== fIterateInterval
)
1093 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
1095 DBG_ASSERT( xIter
.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" );
1098 mfIterateInterval
= fIterateInterval
;
1099 xIter
->setIterateInterval( fIterateInterval
);
1102 calculateIterateDuration();
1105 OUString
CustomAnimationEffect::getPath() const
1109 if( mxNode
.is() ) try
1111 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
1112 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1113 while( xEnumeration
->hasMoreElements() )
1115 Reference
< XAnimateMotion
> xMotion( xEnumeration
->nextElement(), UNO_QUERY
);
1118 xMotion
->getPath() >>= aPath
;
1125 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getPath()" );
1131 void CustomAnimationEffect::setPath( const OUString
& rPath
)
1138 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
1139 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1140 while( xEnumeration
->hasMoreElements() )
1142 Reference
< XAnimateMotion
> xMotion( xEnumeration
->nextElement(), UNO_QUERY
);
1146 MainSequenceChangeGuard
aGuard( mpEffectSequence
);
1147 xMotion
->setPath( Any( rPath
) );
1154 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setPath()" );
1158 Any
CustomAnimationEffect::getProperty( sal_Int32 nNodeType
, std::u16string_view rAttributeName
, EValue eValue
)
1161 if( mxNode
.is() ) try
1163 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1164 if( xEnumerationAccess
.is() )
1166 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1167 if( xEnumeration
.is() )
1169 while( xEnumeration
->hasMoreElements() && !aProperty
.hasValue() )
1171 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1172 if( !xAnimate
.is() )
1175 if( xAnimate
->getType() == nNodeType
)
1177 if( xAnimate
->getAttributeName() == rAttributeName
)
1181 case EValue::To
: aProperty
= xAnimate
->getTo(); break;
1182 case EValue::By
: aProperty
= xAnimate
->getBy(); break;
1192 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getProperty()" );
1198 bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType
, std::u16string_view rAttributeName
, EValue eValue
, const Any
& rValue
)
1200 bool bChanged
= false;
1201 if( mxNode
.is() ) try
1203 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1204 if( xEnumerationAccess
.is() )
1206 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1207 if( xEnumeration
.is() )
1209 while( xEnumeration
->hasMoreElements() )
1211 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1212 if( !xAnimate
.is() )
1215 if( xAnimate
->getType() == nNodeType
)
1217 if( xAnimate
->getAttributeName() == rAttributeName
)
1222 if( xAnimate
->getTo() != rValue
)
1224 xAnimate
->setTo( rValue
);
1229 if( xAnimate
->getTo() != rValue
)
1231 xAnimate
->setBy( rValue
);
1244 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setProperty()" );
1250 static bool implIsColorAttribute( std::u16string_view rAttributeName
)
1252 return rAttributeName
== u
"FillColor" || rAttributeName
== u
"LineColor" || rAttributeName
== u
"CharColor";
1255 Any
CustomAnimationEffect::getColor( sal_Int32 nIndex
)
1258 if( mxNode
.is() ) try
1260 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1261 if( xEnumerationAccess
.is() )
1263 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1264 if( xEnumeration
.is() )
1266 while( xEnumeration
->hasMoreElements() && !aColor
.hasValue() )
1268 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1269 if( !xAnimate
.is() )
1272 switch( xAnimate
->getType() )
1274 case AnimationNodeType::SET
:
1275 case AnimationNodeType::ANIMATE
:
1276 if( !implIsColorAttribute( xAnimate
->getAttributeName() ) )
1279 case AnimationNodeType::ANIMATECOLOR
:
1280 Sequence
<Any
> aValues( xAnimate
->getValues() );
1281 if( aValues
.hasElements() )
1283 if( aValues
.getLength() > nIndex
)
1284 aColor
= aValues
[nIndex
];
1286 else if( nIndex
== 0 )
1287 aColor
= xAnimate
->getFrom();
1289 aColor
= xAnimate
->getTo();
1297 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getColor()" );
1303 void CustomAnimationEffect::setColor( sal_Int32 nIndex
, const Any
& rColor
)
1310 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1311 if( xEnumerationAccess
.is() )
1313 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1314 if( xEnumeration
.is() )
1316 while( xEnumeration
->hasMoreElements() )
1318 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1319 if( !xAnimate
.is() )
1322 switch( xAnimate
->getType() )
1324 case AnimationNodeType::SET
:
1325 case AnimationNodeType::ANIMATE
:
1326 if( !implIsColorAttribute( xAnimate
->getAttributeName() ) )
1329 case AnimationNodeType::ANIMATECOLOR
:
1331 Sequence
<Any
> aValues( xAnimate
->getValues() );
1332 if( aValues
.hasElements() )
1334 if( aValues
.getLength() > nIndex
)
1336 aValues
.getArray()[nIndex
] = rColor
;
1337 xAnimate
->setValues( aValues
);
1340 else if( (nIndex
== 0) && xAnimate
->getFrom().hasValue() )
1341 xAnimate
->setFrom(rColor
);
1342 else if( (nIndex
== 1) && xAnimate
->getTo().hasValue() )
1343 xAnimate
->setTo(rColor
);
1354 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setColor()" );
1358 Any
CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType
, EValue eValue
)
1361 if( mxNode
.is() ) try
1363 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1364 if( xEnumerationAccess
.is() )
1366 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1367 if( xEnumeration
.is() )
1369 while( xEnumeration
->hasMoreElements() && !aProperty
.hasValue() )
1371 Reference
< XAnimateTransform
> xTransform( xEnumeration
->nextElement(), UNO_QUERY
);
1372 if( !xTransform
.is() )
1375 if( xTransform
->getTransformType() == nTransformType
)
1379 case EValue::To
: aProperty
= xTransform
->getTo(); break;
1380 case EValue::By
: aProperty
= xTransform
->getBy(); break;
1389 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getTransformationProperty()" );
1395 bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType
, EValue eValue
, const Any
& rValue
)
1397 bool bChanged
= false;
1398 if( mxNode
.is() ) try
1400 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1401 if( xEnumerationAccess
.is() )
1403 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1404 if( xEnumeration
.is() )
1406 while( xEnumeration
->hasMoreElements() )
1408 Reference
< XAnimateTransform
> xTransform( xEnumeration
->nextElement(), UNO_QUERY
);
1409 if( !xTransform
.is() )
1412 if( xTransform
->getTransformType() == nTransformType
)
1417 if( xTransform
->getTo() != rValue
)
1419 xTransform
->setTo( rValue
);
1424 if( xTransform
->getBy() != rValue
)
1426 xTransform
->setBy( rValue
);
1438 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTransformationProperty()" );
1444 void CustomAnimationEffect::createAudio( const css::uno::Any
& rSource
)
1446 DBG_ASSERT( !mxAudio
.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" );
1453 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1454 Reference
< XAudio
> xAudio( Audio::create( xContext
) );
1455 xAudio
->setSource( rSource
);
1456 xAudio
->setVolume( 1.0 );
1461 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::createAudio()" );
1465 static Reference
< XCommand
> findCommandNode( const Reference
< XAnimationNode
>& xRootNode
)
1467 Reference
< XCommand
> xCommand
;
1469 if( xRootNode
.is() ) try
1471 Reference
< XEnumerationAccess
> xEnumerationAccess( xRootNode
, UNO_QUERY_THROW
);
1472 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1473 while( !xCommand
.is() && xEnumeration
->hasMoreElements() )
1475 Reference
< XAnimationNode
> xNode( xEnumeration
->nextElement(), UNO_QUERY
);
1476 if( xNode
.is() && (xNode
->getType() == AnimationNodeType::COMMAND
) )
1477 xCommand
.set( xNode
, UNO_QUERY_THROW
);
1482 TOOLS_WARN_EXCEPTION( "sd", "sd::findCommandNode()" );
1488 void CustomAnimationEffect::removeAudio()
1492 Reference
< XAnimationNode
> xChild
;
1499 else if( mnCommand
== EffectCommands::STOPAUDIO
)
1501 xChild
= findCommandNode( mxNode
);
1507 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY
);
1508 if( xContainer
.is() )
1509 xContainer
->removeChild( xChild
);
1514 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::removeAudio()" );
1519 void CustomAnimationEffect::setAudio( const Reference
< css::animations::XAudio
>& xAudio
)
1521 if( mxAudio
== xAudio
)
1528 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY
);
1529 if( xContainer
.is() && mxAudio
.is() )
1530 xContainer
->appendChild( mxAudio
);
1534 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAudio()" );
1538 void CustomAnimationEffect::setStopAudio()
1540 if( mnCommand
== EffectCommands::STOPAUDIO
)
1548 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1549 Reference
< XCommand
> xCommand( Command::create( xContext
) );
1551 xCommand
->setCommand( EffectCommands::STOPAUDIO
);
1553 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY_THROW
);
1554 xContainer
->appendChild( xCommand
);
1556 mnCommand
= EffectCommands::STOPAUDIO
;
1560 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setStopAudio()" );
1564 bool CustomAnimationEffect::getStopAudio() const
1566 return mnCommand
== EffectCommands::STOPAUDIO
;
1569 rtl::Reference
<SdrPathObj
> CustomAnimationEffect::createSdrPathObjFromPath(SdrModel
& rTargetModel
)
1571 rtl::Reference
<SdrPathObj
> pPathObj
= new SdrPathObj(rTargetModel
, SdrObjKind::PathLine
);
1572 updateSdrPathObjFromPath( *pPathObj
);
1576 void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj
& rPathObj
)
1578 ::basegfx::B2DPolyPolygon aPolyPoly
;
1579 if( ::basegfx::utils::importFromSvgD( aPolyPoly
, getPath(), true, nullptr ) )
1581 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(getTargetShape());
1584 SdrPage
* pPage
= pObj
->getSdrPageFromSdrObject();
1587 const Size
aPageSize( pPage
->GetSize() );
1588 aPolyPoly
.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast<double>(aPageSize
.Width()), static_cast<double>(aPageSize
.Height())));
1591 const ::tools::Rectangle
aBoundRect( pObj
->GetCurrentBoundRect() );
1592 const Point
aCenter( aBoundRect
.Center() );
1593 aPolyPoly
.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter
.X(), aCenter
.Y()));
1597 rPathObj
.SetPathPoly( aPolyPoly
);
1600 void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj
& rPathObj
)
1602 ::basegfx::B2DPolyPolygon
aPolyPoly( rPathObj
.GetPathPoly() );
1604 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape(getTargetShape());
1607 ::tools::Rectangle
aBoundRect(0,0,0,0);
1609 drawinglayer::primitive2d::Primitive2DContainer xPrimitives
;
1610 pObj
->GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives
);
1611 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
1612 const basegfx::B2DRange
aRange(xPrimitives
.getB2DRange(aViewInformation2D
));
1614 if(!aRange
.isEmpty())
1616 aBoundRect
= ::tools::Rectangle(
1617 static_cast<sal_Int32
>(floor(aRange
.getMinX())), static_cast<sal_Int32
>(floor(aRange
.getMinY())),
1618 static_cast<sal_Int32
>(ceil(aRange
.getMaxX())), static_cast<sal_Int32
>(ceil(aRange
.getMaxY())));
1621 const Point
aCenter( aBoundRect
.Center() );
1623 aPolyPoly
.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter
.X(), -aCenter
.Y()));
1625 SdrPage
* pPage
= pObj
->getSdrPageFromSdrObject();
1628 const Size
aPageSize( pPage
->GetSize() );
1629 aPolyPoly
.transform(basegfx::utils::createScaleB2DHomMatrix(
1630 1.0 / static_cast<double>(aPageSize
.Width()), 1.0 / static_cast<double>(aPageSize
.Height())));
1634 setPath( ::basegfx::utils::exportToSvgD( aPolyPoly
, true, true, true) );
1637 EffectSequenceHelper::EffectSequenceHelper()
1638 : mnSequenceType( EffectNodeType::DEFAULT
)
1642 EffectSequenceHelper::EffectSequenceHelper( css::uno::Reference
< css::animations::XTimeContainer
> xSequenceRoot
)
1643 : mxSequenceRoot(std::move( xSequenceRoot
)), mnSequenceType( EffectNodeType::DEFAULT
)
1645 Reference
< XAnimationNode
> xNode( mxSequenceRoot
, UNO_QUERY_THROW
);
1649 EffectSequenceHelper::~EffectSequenceHelper()
1654 void EffectSequenceHelper::reset()
1656 for( CustomAnimationEffectPtr
& pEffect
: maEffects
)
1658 pEffect
->setEffectSequence(nullptr);
1663 Reference
< XAnimationNode
> EffectSequenceHelper::getRootNode()
1665 return mxSequenceRoot
;
1668 void EffectSequenceHelper::append( const CustomAnimationEffectPtr
& pEffect
)
1670 pEffect
->setEffectSequence( this );
1671 maEffects
.push_back(pEffect
);
1675 CustomAnimationEffectPtr
EffectSequenceHelper::append( const CustomAnimationPresetPtr
& pPreset
, const Any
& rTarget
, double fDuration
/* = -1.0 */ )
1677 CustomAnimationEffectPtr pEffect
;
1681 Reference
< XAnimationNode
> xNode( pPreset
->create( "" ) );
1684 // first, filter all only ui relevant user data
1685 std::vector
< NamedValue
> aNewUserData
;
1686 Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
1688 std::copy_if(std::cbegin(aUserData
), std::cend(aUserData
), std::back_inserter(aNewUserData
),
1689 [](const NamedValue
& rProp
) { return rProp
.Name
!= "text-only" && rProp
.Name
!= "preset-property"; });
1691 if( !aNewUserData
.empty() )
1693 aUserData
= ::comphelper::containerToSequence( aNewUserData
);
1694 xNode
->setUserData( aUserData
);
1697 // check target, maybe we need to force it to text
1698 sal_Int16 nSubItem
= ShapeAnimationSubType::AS_WHOLE
;
1700 if( rTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
1702 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1704 else if( pPreset
->isTextOnly() )
1706 Reference
< XShape
> xShape
;
1710 // that's bad, we target a shape here but the effect is only for text
1711 // so change subitem
1712 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1716 // now create effect from preset
1717 pEffect
= std::make_shared
<CustomAnimationEffect
>( xNode
);
1718 pEffect
->setEffectSequence( this );
1719 pEffect
->setTarget( rTarget
);
1720 pEffect
->setTargetSubItem( nSubItem
);
1721 if( fDuration
!= -1.0 )
1722 pEffect
->setDuration( fDuration
);
1724 maEffects
.push_back(pEffect
);
1730 DBG_ASSERT( pEffect
, "sd::EffectSequenceHelper::append(), failed!" );
1734 CustomAnimationEffectPtr
EffectSequenceHelper::append( const SdrPathObj
& rPathObj
, const Any
& rTarget
, double fDuration
/* = -1.0 */, const OUString
& rPresetId
)
1736 CustomAnimationEffectPtr pEffect
;
1738 if( fDuration
<= 0.0 )
1743 Reference
< XTimeContainer
> xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1744 Reference
< XAnimationNode
> xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) );
1746 xAnimateMotion
->setDuration( Any( fDuration
) );
1747 xAnimateMotion
->setFill( AnimationFill::HOLD
);
1748 xEffectContainer
->appendChild( xAnimateMotion
);
1750 sal_Int16 nSubItem
= ShapeAnimationSubType::AS_WHOLE
;
1752 if( rTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
1753 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1755 pEffect
= std::make_shared
<CustomAnimationEffect
>( xEffectContainer
);
1756 pEffect
->setEffectSequence( this );
1757 pEffect
->setTarget( rTarget
);
1758 pEffect
->setTargetSubItem( nSubItem
);
1759 pEffect
->setNodeType( css::presentation::EffectNodeType::ON_CLICK
);
1760 pEffect
->setPresetClassAndId( css::presentation::EffectPresetClass::MOTIONPATH
, rPresetId
);
1761 pEffect
->setAcceleration( 0.5 );
1762 pEffect
->setDecelerate( 0.5 );
1763 pEffect
->setFill( AnimationFill::HOLD
);
1764 pEffect
->setBegin( 0.0 );
1765 pEffect
->updatePathFromSdrPathObj( rPathObj
);
1766 if( fDuration
!= -1.0 )
1767 pEffect
->setDuration( fDuration
);
1769 maEffects
.push_back(pEffect
);
1775 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::append()" );
1781 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationPresetPtr
& pPreset
, const OUString
& rPresetSubType
, double fDuration
/* = -1.0 */ )
1783 if( !(pEffect
&& pPreset
) )
1788 Reference
< XAnimationNode
> xNewNode( pPreset
->create( rPresetSubType
) );
1791 pEffect
->replaceNode( xNewNode
);
1792 if( fDuration
!= -1.0 )
1793 pEffect
->setDuration( fDuration
);
1800 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::replace()" );
1804 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationPresetPtr
& pPreset
, double fDuration
/* = -1.0 */ )
1806 replace( pEffect
, pPreset
, "", fDuration
);
1809 void EffectSequenceHelper::remove( const CustomAnimationEffectPtr
& pEffect
)
1813 pEffect
->setEffectSequence( nullptr );
1814 maEffects
.remove( pEffect
);
1820 void EffectSequenceHelper::moveToBeforeEffect( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationEffectPtr
& pInsertBefore
)
1824 maEffects
.remove( pEffect
);
1825 EffectSequence::iterator
aInsertIter( find( pInsertBefore
) );
1827 // aInsertIter being end() is OK: pInsertBefore could be null, so put at end.
1828 maEffects
.insert( aInsertIter
, pEffect
);
1834 void EffectSequenceHelper::rebuild()
1839 void EffectSequenceHelper::implRebuild()
1843 // first we delete all time containers on the first two levels
1844 Reference
< XEnumerationAccess
> xEnumerationAccess( mxSequenceRoot
, UNO_QUERY_THROW
);
1845 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1846 while( xEnumeration
->hasMoreElements() )
1848 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
1849 Reference
< XTimeContainer
> xChildContainer( xChildNode
, UNO_QUERY_THROW
);
1851 Reference
< XEnumerationAccess
> xChildEnumerationAccess( xChildNode
, UNO_QUERY_THROW
);
1852 Reference
< XEnumeration
> xChildEnumeration( xChildEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1853 while( xChildEnumeration
->hasMoreElements() )
1855 Reference
< XAnimationNode
> xNode( xChildEnumeration
->nextElement(), UNO_QUERY_THROW
);
1856 xChildContainer
->removeChild( xNode
);
1859 mxSequenceRoot
->removeChild( xChildNode
);
1862 // second, rebuild main sequence
1863 EffectSequence::iterator
aIter( maEffects
.begin() );
1864 EffectSequence::iterator
aEnd( maEffects
.end() );
1867 std::vector
< sd::AfterEffectNode
> aAfterEffects
;
1869 CustomAnimationEffectPtr pEffect
= *aIter
++;
1874 // create a par container for the next click node and all following with and after effects
1875 Reference
< XTimeContainer
> xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1878 if( mxEventSource
.is() )
1880 aEvent
.Source
<<= mxEventSource
;
1881 aEvent
.Trigger
= EventTrigger::ON_CLICK
;
1885 aEvent
.Trigger
= EventTrigger::ON_NEXT
;
1889 Any
aBegin( aEvent
);
1892 // if the first node is not a click action, this click container
1893 // must not have INDEFINITE begin but start at 0s
1895 if( pEffect
->getNodeType() != EffectNodeType::ON_CLICK
)
1899 xOnClickContainer
->setBegin( aBegin
);
1901 mxSequenceRoot
->appendChild( xOnClickContainer
);
1903 double fBegin
= 0.0;
1907 // create a par container for the current click or after effect node and all following with effects
1908 Reference
< XTimeContainer
> xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1909 xWithContainer
->setBegin( Any( fBegin
) );
1910 xOnClickContainer
->appendChild( xWithContainer
);
1912 double fDuration
= 0.0;
1915 Reference
< XAnimationNode
> xEffectNode( pEffect
->getNode() );
1916 xWithContainer
->appendChild( xEffectNode
);
1918 if( pEffect
->hasAfterEffect() )
1920 Reference
< XAnimationNode
> xAfterEffect( pEffect
->createAfterEffectNode() );
1921 AfterEffectNode
a( xAfterEffect
, xEffectNode
, pEffect
->IsAfterEffectOnNext() );
1922 aAfterEffects
.push_back( a
);
1925 double fTemp
= pEffect
->getBegin() + pEffect
->getAbsoluteDuration();
1926 if( fTemp
> fDuration
)
1934 while( pEffect
&& (pEffect
->getNodeType() == EffectNodeType::WITH_PREVIOUS
) );
1936 fBegin
+= fDuration
;
1938 while( pEffect
&& (pEffect
->getNodeType() != EffectNodeType::ON_CLICK
) );
1942 // process after effect nodes
1943 std::for_each( aAfterEffects
.begin(), aAfterEffects
.end(), stl_process_after_effect_node_func
);
1947 // reset duration, might have been altered (see below)
1948 mxSequenceRoot
->setDuration( Any() );
1952 // empty sequence, set duration to 0.0 explicitly
1953 // (otherwise, this sequence will never end)
1954 mxSequenceRoot
->setDuration( Any(0.0) );
1959 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::rebuild()" );
1963 stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference
< css::animations::XAnimationNode
>& xSearchNode
)
1964 : mxSearchNode( xSearchNode
)
1968 bool stl_CustomAnimationEffect_search_node_predict::operator()( const CustomAnimationEffectPtr
& pEffect
) const
1970 return pEffect
->getNode() == mxSearchNode
;
1973 /// @throws Exception
1974 static bool implFindNextContainer( Reference
< XTimeContainer
> const & xParent
, Reference
< XTimeContainer
> const & xCurrent
, Reference
< XTimeContainer
>& xNext
)
1976 Reference
< XEnumerationAccess
> xEnumerationAccess( xParent
, UNO_QUERY_THROW
);
1977 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration() );
1978 if( xEnumeration
.is() )
1980 Reference
< XInterface
> x
;
1981 while( xEnumeration
->hasMoreElements() && !xNext
.is() )
1983 if( (xEnumeration
->nextElement() >>= x
) && (x
== xCurrent
) )
1985 if( xEnumeration
->hasMoreElements() )
1986 xEnumeration
->nextElement() >>= xNext
;
1993 void stl_process_after_effect_node_func(AfterEffectNode
const & rNode
)
1997 if( rNode
.mxNode
.is() && rNode
.mxMaster
.is() )
2000 Reference
< XAnimationNode
> xMasterNode( rNode
.mxMaster
, UNO_SET_THROW
);
2001 Sequence
< NamedValue
> aUserData( rNode
.mxNode
->getUserData() );
2002 sal_Int32 nSize
= aUserData
.getLength();
2003 aUserData
.realloc(nSize
+1);
2004 auto pUserData
= aUserData
.getArray();
2005 pUserData
[nSize
].Name
= "master-element";
2006 pUserData
[nSize
].Value
<<= xMasterNode
;
2007 rNode
.mxNode
->setUserData( aUserData
);
2009 // insert after effect node into timeline
2010 Reference
< XTimeContainer
> xContainer( rNode
.mxMaster
->getParent(), UNO_QUERY_THROW
);
2012 if( !rNode
.mbOnNextEffect
) // sameClick
2014 // insert the aftereffect after its effect is animated
2015 xContainer
->insertAfter( rNode
.mxNode
, rNode
.mxMaster
);
2019 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2020 // insert the aftereffect in the next group
2022 Reference
< XTimeContainer
> xClickContainer( xContainer
->getParent(), UNO_QUERY_THROW
);
2023 Reference
< XTimeContainer
> xSequenceContainer( xClickContainer
->getParent(), UNO_QUERY_THROW
);
2025 Reference
< XTimeContainer
> xNextContainer
;
2027 // first try if we have an after effect container
2028 if( !implFindNextContainer( xClickContainer
, xContainer
, xNextContainer
) )
2030 Reference
< XTimeContainer
> xNextClickContainer
;
2031 // if not, try to find the next click effect container
2032 if( implFindNextContainer( xSequenceContainer
, xClickContainer
, xNextClickContainer
) )
2034 Reference
< XEnumerationAccess
> xEnumerationAccess( xNextClickContainer
, UNO_QUERY_THROW
);
2035 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2036 if( xEnumeration
->hasMoreElements() )
2038 // the next container is the first child container
2039 xEnumeration
->nextElement() >>= xNextContainer
;
2043 // this does not yet have a child container, create one
2044 xNextContainer
.set( ParallelTimeContainer::create(xContext
), UNO_QUERY_THROW
);
2046 xNextContainer
->setBegin( Any( 0.0 ) );
2047 xNextClickContainer
->appendChild( xNextContainer
);
2049 DBG_ASSERT( xNextContainer
.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" );
2053 // if we don't have a next container, we add one to the sequence container
2054 if( !xNextContainer
.is() )
2056 Reference
< XTimeContainer
> xNewClickContainer( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
2059 aEvent
.Trigger
= EventTrigger::ON_NEXT
;
2061 xNewClickContainer
->setBegin( Any( aEvent
) );
2063 xSequenceContainer
->insertAfter( xNewClickContainer
, xClickContainer
);
2065 xNextContainer
.set( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
2067 xNextContainer
->setBegin( Any( 0.0 ) );
2068 xNewClickContainer
->appendChild( xNextContainer
);
2071 if( xNextContainer
.is() )
2073 // find begin time of first element
2074 Reference
< XEnumerationAccess
> xEnumerationAccess( xNextContainer
, UNO_QUERY_THROW
);
2075 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2076 if( xEnumeration
->hasMoreElements() )
2078 Reference
< XAnimationNode
> xChild
;
2079 // the next container is the first child container
2080 xEnumeration
->nextElement() >>= xChild
;
2083 Any
aBegin( xChild
->getBegin() );
2084 double fBegin
= 0.0;
2085 if( (aBegin
>>= fBegin
) && (fBegin
>= 0.0))
2086 rNode
.mxNode
->setBegin( aBegin
);
2090 xNextContainer
->appendChild( rNode
.mxNode
);
2097 TOOLS_WARN_EXCEPTION( "sd", "ppt::stl_process_after_effect_node_func::operator()" );
2101 EffectSequence::iterator
EffectSequenceHelper::find( const CustomAnimationEffectPtr
& pEffect
)
2103 return std::find( maEffects
.begin(), maEffects
.end(), pEffect
);
2106 CustomAnimationEffectPtr
EffectSequenceHelper::findEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
) const
2108 CustomAnimationEffectPtr pEffect
;
2110 EffectSequence::const_iterator aIter
= std::find_if(maEffects
.begin(), maEffects
.end(),
2111 [&xNode
](const CustomAnimationEffectPtr
& rxEffect
) { return rxEffect
->getNode() == xNode
; });
2112 if (aIter
!= maEffects
.end())
2118 sal_Int32
EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr
& xEffect
) const
2120 auto aIter
= std::find(maEffects
.begin(), maEffects
.end(), xEffect
);
2121 if (aIter
!= maEffects
.end())
2122 return static_cast<sal_Int32
>(std::distance(maEffects
.begin(), aIter
));
2127 CustomAnimationEffectPtr
EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset
) const
2129 EffectSequence::const_iterator
aIter( maEffects
.begin() );
2130 nOffset
= std::min(nOffset
, static_cast<sal_Int32
>(maEffects
.size()));
2131 std::advance(aIter
, nOffset
);
2133 CustomAnimationEffectPtr pEffect
;
2134 if( aIter
!= maEffects
.end() )
2140 bool EffectSequenceHelper::disposeShape( const Reference
< XShape
>& xShape
)
2142 bool bChanges
= false;
2144 EffectSequence::iterator
aIter( maEffects
.begin() );
2145 while( aIter
!= maEffects
.end() )
2147 if( (*aIter
)->getTargetShape() == xShape
)
2149 (*aIter
)->setEffectSequence( nullptr );
2151 aIter
= maEffects
.erase( aIter
);
2162 bool EffectSequenceHelper::hasEffect( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
2164 return std::any_of(maEffects
.begin(), maEffects
.end(),
2165 [&xShape
](const CustomAnimationEffectPtr
& rxEffect
) { return rxEffect
->getTargetShape() == xShape
; });
2168 bool EffectSequenceHelper::getParagraphNumberingLevels( const Reference
< XShape
>& xShape
, std::vector
< sal_Int32
>& rParagraphNumberingLevel
)
2170 rParagraphNumberingLevel
.clear();
2172 if( !hasEffect( xShape
) )
2175 Reference
< XText
> xText( xShape
, UNO_QUERY
);
2178 Reference
< XEnumerationAccess
> xEA( xText
, UNO_QUERY
);
2181 Reference
< XEnumeration
> xEnumeration
= xEA
->createEnumeration();
2183 if( xEnumeration
.is() )
2185 while( xEnumeration
->hasMoreElements() )
2187 Reference
< XPropertySet
> xParaSet
;
2188 xEnumeration
->nextElement() >>= xParaSet
;
2190 sal_Int32 nParaDepth
= 0;
2193 xParaSet
->getPropertyValue( "NumberingLevel" ) >>= nParaDepth
;
2196 rParagraphNumberingLevel
.push_back( nParaDepth
);
2205 void EffectSequenceHelper::insertTextRange( const css::uno::Any
& aTarget
)
2207 ParagraphTarget aParaTarget
;
2208 if( !(aTarget
>>= aParaTarget
) )
2211 // get map [paragraph index] -> [NumberingLevel]
2212 // for following reusage inside all animation effects
2213 std::vector
< sal_Int32
> paragraphNumberingLevel
;
2214 std::vector
< sal_Int32
>* paragraphNumberingLevelParam
= nullptr;
2215 if ( getParagraphNumberingLevels( aParaTarget
.Shape
, paragraphNumberingLevel
) )
2216 paragraphNumberingLevelParam
= ¶graphNumberingLevel
;
2218 // update internal flags for each animation effect
2219 const bool bChanges
= std::accumulate(maEffects
.begin(), maEffects
.end(), false,
2220 [&aParaTarget
, ¶graphNumberingLevelParam
](const bool bCheck
, const CustomAnimationEffectPtr
& rxEffect
) {
2222 if (rxEffect
->getTargetShape() == aParaTarget
.Shape
)
2223 bRes
|= rxEffect
->checkForText( paragraphNumberingLevelParam
);
2231 static bool isParagraphTargetTextEmpty( ParagraphTarget aParaTarget
)
2234 Reference
< XText
> xText ( aParaTarget
.Shape
, UNO_QUERY
);
2237 Reference
< XEnumerationAccess
> xEA( xText
, UNO_QUERY
);
2240 Reference
< XEnumeration
> xEnumeration
= xEA
->createEnumeration();
2241 if( xEnumeration
.is() )
2243 // advance to the Nth paragraph
2244 sal_Int32 nPara
= aParaTarget
.Paragraph
;
2245 while( xEnumeration
->hasMoreElements() && nPara
-- )
2246 xEnumeration
->nextElement();
2248 // get Nth paragraph's text and check if it's empty
2249 if( xEnumeration
->hasMoreElements() )
2251 Reference
< XTextRange
> xRange( xEnumeration
->nextElement(), UNO_QUERY
);
2254 OUString text
= xRange
->getString();
2255 return text
.isEmpty();
2264 void EffectSequenceHelper::disposeTextRange( const css::uno::Any
& aTarget
)
2266 ParagraphTarget aParaTarget
;
2267 if( !(aTarget
>>= aParaTarget
) )
2270 bool bChanges
= false;
2272 // building list of effects for target shape; process effects not on target shape
2273 EffectSequence aTargetParagraphEffects
;
2274 for( const auto &pEffect
: maEffects
)
2276 Any
aIterTarget( pEffect
->getTarget() );
2277 if( aIterTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2279 ParagraphTarget aIterParaTarget
;
2280 if( (aIterTarget
>>= aIterParaTarget
) && (aIterParaTarget
.Shape
== aParaTarget
.Shape
) )
2282 aTargetParagraphEffects
.push_back(pEffect
);
2285 else if( pEffect
->getTargetShape() == aParaTarget
.Shape
)
2287 bChanges
|= pEffect
->checkForText();
2291 // select effect to delete:
2292 // if paragraph before target is blank, then delete its animation effect (if any) instead
2293 ParagraphTarget aPreviousParagraph
= aParaTarget
;
2294 --aPreviousParagraph
.Paragraph
;
2295 bool bIsPreviousParagraphEmpty
= isParagraphTargetTextEmpty( aPreviousParagraph
);
2296 sal_Int16 anParaNumToDelete
= bIsPreviousParagraphEmpty
? aPreviousParagraph
.Paragraph
: aParaTarget
.Paragraph
;
2299 for( const auto &pEffect
: aTargetParagraphEffects
)
2301 Any
aIterTarget( pEffect
->getTarget() );
2303 ParagraphTarget aIterParaTarget
;
2304 aIterTarget
>>= aIterParaTarget
;
2306 // delete effect for target paragraph (may have effects in more than one text group)
2307 if( aIterParaTarget
.Paragraph
== anParaNumToDelete
)
2309 auto aItr
= find( pEffect
);
2310 DBG_ASSERT( aItr
!= maEffects
.end(), "sd::EffectSequenceHelper::disposeTextRange(), Expected effect missing.");
2311 if( aItr
!= maEffects
.end() )
2313 (*aItr
)->setEffectSequence( nullptr );
2314 maEffects
.erase(aItr
);
2319 // shift all paragraphs after disposed paragraph
2320 if( aIterParaTarget
.Paragraph
> anParaNumToDelete
)
2322 --aIterParaTarget
.Paragraph
;
2323 pEffect
->setTarget( Any( aIterParaTarget
) );
2334 CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference
< XShape
>& rTarget
, sal_Int32 nGroupId
)
2335 : maTarget( rTarget
),
2336 mnGroupId( nGroupId
)
2341 void CustomAnimationTextGroup::reset()
2343 mnTextGrouping
= -1;
2344 mbAnimateForm
= false;
2345 mbTextReverse
= false;
2346 mfGroupingAuto
= -1.0;
2347 mnLastPara
= -1; // used to check for TextReverse
2349 for (sal_Int8
& rn
: mnDepthFlags
)
2357 void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr
const & pEffect
)
2359 maEffects
.push_back( pEffect
);
2361 Any
aTarget( pEffect
->getTarget() );
2362 if( aTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2364 // now look at the paragraph
2365 ParagraphTarget aParaTarget
;
2366 aTarget
>>= aParaTarget
;
2368 if( mnLastPara
!= -1 )
2369 mbTextReverse
= mnLastPara
> aParaTarget
.Paragraph
;
2371 mnLastPara
= aParaTarget
.Paragraph
;
2373 const sal_Int32 nParaDepth
= pEffect
->getParaDepth();
2375 // only look at the first PARA_LEVELS levels
2376 if( nParaDepth
< PARA_LEVELS
)
2378 // our first paragraph with this level?
2379 if( mnDepthFlags
[nParaDepth
] == 0 )
2381 // so set it to the first found
2382 mnDepthFlags
[nParaDepth
] = static_cast<sal_Int8
>(pEffect
->getNodeType());
2384 else if( mnDepthFlags
[nParaDepth
] != pEffect
->getNodeType() )
2386 mnDepthFlags
[nParaDepth
] = -1;
2389 if( pEffect
->getNodeType() == EffectNodeType::AFTER_PREVIOUS
)
2390 mfGroupingAuto
= pEffect
->getBegin();
2392 mnTextGrouping
= PARA_LEVELS
;
2393 while( (mnTextGrouping
> 0)
2394 && (mnDepthFlags
[mnTextGrouping
- 1] <= 0) )
2400 // if we have an effect with the shape as a target, we animate the background
2401 mbAnimateForm
= pEffect
->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT
;
2405 CustomAnimationTextGroupPtr
EffectSequenceHelper::findGroup( sal_Int32 nGroupId
)
2407 CustomAnimationTextGroupPtr aPtr
;
2409 CustomAnimationTextGroupMap::iterator
aIter( maGroupMap
.find( nGroupId
) );
2410 if( aIter
!= maGroupMap
.end() )
2411 aPtr
= (*aIter
).second
;
2416 void EffectSequenceHelper::updateTextGroups()
2420 // first create all the groups
2421 for( const CustomAnimationEffectPtr
& pEffect
: maEffects
)
2423 const sal_Int32 nGroupId
= pEffect
->getGroupId();
2425 if( nGroupId
== -1 )
2426 continue; // trivial case, no group
2428 CustomAnimationTextGroupPtr pGroup
= findGroup( nGroupId
);
2431 pGroup
= std::make_shared
<CustomAnimationTextGroup
>( pEffect
->getTargetShape(), nGroupId
);
2432 maGroupMap
[nGroupId
] = pGroup
;
2435 pGroup
->addEffect( pEffect
);
2438 // Now that all the text groups have been cleared up and rebuilt, we need to update its
2439 // text grouping. addEffect() already make mnTextGrouping the last possible level,
2440 // so just continue to find the last level that is not EffectNodeType::WITH_PREVIOUS.
2441 for(const auto &rGroupMapItem
: maGroupMap
)
2443 const CustomAnimationTextGroupPtr
&pGroup
= rGroupMapItem
.second
;
2444 while(pGroup
->mnTextGrouping
> 0 && pGroup
->mnDepthFlags
[pGroup
->mnTextGrouping
- 1] == EffectNodeType::WITH_PREVIOUS
)
2445 --pGroup
->mnTextGrouping
;
2449 CustomAnimationTextGroupPtr
2450 EffectSequenceHelper::createTextGroup(const CustomAnimationEffectPtr
& pEffect
,
2451 sal_Int32 nTextGrouping
, double fTextGroupingAuto
,
2452 bool bAnimateForm
, bool bTextReverse
)
2454 // first find a free group-id
2455 sal_Int32 nGroupId
= 0;
2457 CustomAnimationTextGroupMap::iterator
aIter( maGroupMap
.begin() );
2458 const CustomAnimationTextGroupMap::iterator
aEnd( maGroupMap
.end() );
2459 while( aIter
!= aEnd
)
2461 if( (*aIter
).first
== nGroupId
)
2464 aIter
= maGroupMap
.begin();
2472 Reference
< XShape
> xTarget( pEffect
->getTargetShape() );
2474 CustomAnimationTextGroupPtr pTextGroup
= std::make_shared
<CustomAnimationTextGroup
>( xTarget
, nGroupId
);
2475 maGroupMap
[nGroupId
] = pTextGroup
;
2479 // do we need to target the shape?
2480 if( (nTextGrouping
== 0) || bAnimateForm
)
2483 if( nTextGrouping
== 0)
2484 nSubItem
= bAnimateForm
? ShapeAnimationSubType::AS_WHOLE
: ShapeAnimationSubType::ONLY_TEXT
;
2486 nSubItem
= ShapeAnimationSubType::ONLY_BACKGROUND
;
2488 pEffect
->setTarget( Any( xTarget
) );
2489 pEffect
->setTargetSubItem( nSubItem
);
2490 pEffect
->setEffectSequence( this );
2491 pEffect
->setGroupId( nGroupId
);
2493 pTextGroup
->addEffect( pEffect
);
2497 pTextGroup
->mnTextGrouping
= nTextGrouping
;
2498 pTextGroup
->mfGroupingAuto
= fTextGroupingAuto
;
2499 pTextGroup
->mbTextReverse
= bTextReverse
;
2501 // now add an effect for each paragraph
2502 createTextGroupParagraphEffects( pTextGroup
, pEffect
, bUsed
);
2509 void EffectSequenceHelper::createTextGroupParagraphEffects( const CustomAnimationTextGroupPtr
& pTextGroup
, const CustomAnimationEffectPtr
& pEffect
, bool bUsed
)
2511 Reference
< XShape
> xTarget( pTextGroup
->maTarget
);
2513 sal_Int32 nTextGrouping
= pTextGroup
->mnTextGrouping
;
2514 double fTextGroupingAuto
= pTextGroup
->mfGroupingAuto
;
2515 bool bTextReverse
= pTextGroup
->mbTextReverse
;
2517 // now add an effect for each paragraph
2518 if( nTextGrouping
< 0 )
2523 EffectSequence::iterator
aInsertIter( find( pEffect
) );
2525 Reference
< XEnumerationAccess
> xText( xTarget
, UNO_QUERY_THROW
);
2526 Reference
< XEnumeration
> xEnumeration( xText
->createEnumeration(), UNO_SET_THROW
);
2528 std::deque
< sal_Int16
> aParaList
;
2531 // fill the list with all valid paragraphs
2532 for( nPara
= 0; xEnumeration
->hasMoreElements(); nPara
++ )
2534 Reference
< XTextRange
> xRange( xEnumeration
->nextElement(), UNO_QUERY
);
2535 if( xRange
.is() && !xRange
->getString().isEmpty() )
2537 if( bTextReverse
) // sort them
2538 aParaList
.push_front( nPara
);
2540 aParaList
.push_back( nPara
);
2544 ParagraphTarget aTarget
;
2545 aTarget
.Shape
= xTarget
;
2547 for( const auto i
: aParaList
)
2549 aTarget
.Paragraph
= i
;
2551 CustomAnimationEffectPtr pNewEffect
;
2554 // clone a new effect from first effect
2555 pNewEffect
= pEffect
->clone();
2557 aInsertIter
= maEffects
.insert( aInsertIter
, pNewEffect
);
2561 // reuse first effect if it's not yet used
2562 pNewEffect
= pEffect
;
2564 aInsertIter
= find( pNewEffect
);
2567 // set target and group-id
2568 pNewEffect
->setTarget( Any( aTarget
) );
2569 pNewEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT
);
2570 pNewEffect
->setGroupId( pTextGroup
->mnGroupId
);
2571 pNewEffect
->setEffectSequence( this );
2573 // set correct node type
2574 if( pNewEffect
->getParaDepth() < nTextGrouping
)
2576 if( fTextGroupingAuto
== -1.0 )
2578 pNewEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2579 pNewEffect
->setBegin( 0.0 );
2583 pNewEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2584 pNewEffect
->setBegin( fTextGroupingAuto
);
2589 pNewEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2590 pNewEffect
->setBegin( 0.0 );
2593 pTextGroup
->addEffect( pNewEffect
);
2599 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createTextGroup()" );
2603 void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr
& pTextGroup
, sal_Int32 nTextGrouping
)
2605 if( pTextGroup
->mnTextGrouping
== nTextGrouping
)
2607 // first case, trivial case, do nothing
2609 else if( (pTextGroup
->mnTextGrouping
== -1) && (nTextGrouping
>= 0) )
2611 // second case, we need to add new effects for each paragraph
2613 CustomAnimationEffectPtr
pEffect( pTextGroup
->maEffects
.front() );
2615 pTextGroup
->mnTextGrouping
= nTextGrouping
;
2616 createTextGroupParagraphEffects( pTextGroup
, pEffect
, true );
2619 else if( (pTextGroup
->mnTextGrouping
>= 0) && (nTextGrouping
== -1 ) )
2621 // third case, we need to remove effects for each paragraph
2623 EffectSequence
aEffects( pTextGroup
->maEffects
);
2624 pTextGroup
->reset();
2626 for( const CustomAnimationEffectPtr
& pEffect
: aEffects
)
2628 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2631 pTextGroup
->addEffect( pEffect
);
2637 // fourth case, we need to change the node types for the text nodes
2638 double fTextGroupingAuto
= pTextGroup
->mfGroupingAuto
;
2640 EffectSequence
aEffects( pTextGroup
->maEffects
);
2641 pTextGroup
->reset();
2643 for( CustomAnimationEffectPtr
& pEffect
: aEffects
)
2645 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2647 // set correct node type
2648 if( pEffect
->getParaDepth() < nTextGrouping
)
2650 if( fTextGroupingAuto
== -1.0 )
2652 pEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2653 pEffect
->setBegin( 0.0 );
2657 pEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2658 pEffect
->setBegin( fTextGroupingAuto
);
2663 pEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2664 pEffect
->setBegin( 0.0 );
2668 pTextGroup
->addEffect( pEffect
);
2675 void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr
& pTextGroup
, bool bAnimateForm
)
2677 if( pTextGroup
->mbAnimateForm
== bAnimateForm
)
2679 // trivial case, do nothing
2683 EffectSequence
aEffects( pTextGroup
->maEffects
);
2684 pTextGroup
->reset();
2686 SAL_WARN_IF(aEffects
.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" );
2688 if (aEffects
.empty())
2691 EffectSequence::iterator
aIter( aEffects
.begin() );
2692 const EffectSequence::iterator
aEnd( aEffects
.end() );
2694 // first insert if we have to
2697 EffectSequence::iterator
aInsertIter( find( *aIter
) );
2699 CustomAnimationEffectPtr pEffect
;
2700 if( (aEffects
.size() == 1) && ((*aIter
)->getTarget().getValueType() != ::cppu::UnoType
<ParagraphTarget
>::get() ) )
2702 // special case, only one effect and that targets whole text,
2703 // convert this to target whole shape
2705 pEffect
->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE
);
2709 pEffect
= (*aIter
)->clone();
2710 pEffect
->setTarget( Any( (*aIter
)->getTargetShape() ) );
2711 pEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND
);
2712 maEffects
.insert( aInsertIter
, pEffect
);
2715 pTextGroup
->addEffect( pEffect
);
2718 if( !bAnimateForm
&& (aEffects
.size() == 1) )
2720 CustomAnimationEffectPtr
pEffect( *aIter
);
2721 pEffect
->setTarget( Any( (*aIter
)->getTargetShape() ) );
2722 pEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT
);
2723 pTextGroup
->addEffect( pEffect
);
2727 // read the rest to the group again
2728 while( aIter
!= aEnd
)
2730 CustomAnimationEffectPtr
pEffect( *aIter
++ );
2732 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2734 pTextGroup
->addEffect( pEffect
);
2738 DBG_ASSERT( !bAnimateForm
, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" );
2747 void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr
& pTextGroup
, double fTextGroupingAuto
)
2749 sal_Int32 nTextGrouping
= pTextGroup
->mnTextGrouping
;
2751 EffectSequence
aEffects( pTextGroup
->maEffects
);
2752 pTextGroup
->reset();
2754 for( CustomAnimationEffectPtr
& pEffect
: aEffects
)
2756 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2758 // set correct node type
2759 if( pEffect
->getParaDepth() < nTextGrouping
)
2761 if( fTextGroupingAuto
== -1.0 )
2763 pEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2764 pEffect
->setBegin( 0.0 );
2768 pEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2769 pEffect
->setBegin( fTextGroupingAuto
);
2774 pEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2775 pEffect
->setBegin( 0.0 );
2779 pTextGroup
->addEffect( pEffect
);
2787 struct ImplStlTextGroupSortHelper
2789 explicit ImplStlTextGroupSortHelper( bool bReverse
) : mbReverse( bReverse
) {};
2790 bool operator()( const CustomAnimationEffectPtr
& p1
, const CustomAnimationEffectPtr
& p2
);
2792 sal_Int32
getTargetParagraph( const CustomAnimationEffectPtr
& p1
);
2797 sal_Int32
ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr
& p1
)
2799 const Any
aTarget(p1
->getTarget());
2800 if( aTarget
.hasValue() && aTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2802 ParagraphTarget aParaTarget
;
2803 aTarget
>>= aParaTarget
;
2804 return aParaTarget
.Paragraph
;
2808 return mbReverse
? 0x7fffffff : -1;
2812 bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr
& p1
, const CustomAnimationEffectPtr
& p2
)
2816 return getTargetParagraph( p2
) < getTargetParagraph( p1
);
2820 return getTargetParagraph( p1
) < getTargetParagraph( p2
);
2824 void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr
& pTextGroup
, bool bTextReverse
)
2826 if( pTextGroup
->mbTextReverse
== bTextReverse
)
2832 std::vector
< CustomAnimationEffectPtr
> aSortedVector( pTextGroup
->maEffects
.begin(), pTextGroup
->maEffects
.end() );
2833 ImplStlTextGroupSortHelper
aSortHelper( bTextReverse
);
2834 std::sort( aSortedVector
.begin(), aSortedVector
.end(), aSortHelper
);
2836 pTextGroup
->reset();
2838 std::vector
< CustomAnimationEffectPtr
>::iterator
aIter( aSortedVector
.begin() );
2839 const std::vector
< CustomAnimationEffectPtr
>::iterator
aEnd( aSortedVector
.end() );
2843 pTextGroup
->addEffect( *aIter
);
2844 EffectSequence::iterator
aInsertIter( find( *aIter
++ ) );
2845 while( aIter
!= aEnd
)
2847 CustomAnimationEffectPtr
pEffect( *aIter
++ );
2848 maEffects
.erase( find( pEffect
) );
2849 aInsertIter
= maEffects
.insert( ++aInsertIter
, pEffect
);
2850 pTextGroup
->addEffect( pEffect
);
2857 void EffectSequenceHelper::addListener( ISequenceListener
* pListener
)
2859 if( std::find( maListeners
.begin(), maListeners
.end(), pListener
) == maListeners
.end() )
2860 maListeners
.push_back( pListener
);
2863 void EffectSequenceHelper::removeListener( ISequenceListener
* pListener
)
2865 maListeners
.remove( pListener
);
2870 struct stl_notify_listeners_func
2872 stl_notify_listeners_func() {}
2873 void operator()(ISequenceListener
* pListener
) { pListener
->notify_change(); }
2878 void EffectSequenceHelper::notify_listeners()
2880 stl_notify_listeners_func aFunc
;
2881 std::for_each( maListeners
.begin(), maListeners
.end(), aFunc
);
2884 void EffectSequenceHelper::create( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
2886 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::create(), illegal argument" );
2893 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2894 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2895 while( xEnumeration
->hasMoreElements() )
2897 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2898 createEffectsequence( xChildNode
);
2903 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::create()" );
2907 void EffectSequenceHelper::createEffectsequence( const Reference
< XAnimationNode
>& xNode
)
2909 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" );
2916 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2917 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2918 while( xEnumeration
->hasMoreElements() )
2920 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2922 createEffects( xChildNode
);
2927 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffectsequence()" );
2931 void EffectSequenceHelper::createEffects( const Reference
< XAnimationNode
>& xNode
)
2933 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" );
2940 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2941 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2942 while( xEnumeration
->hasMoreElements() )
2944 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2946 switch( xChildNode
->getType() )
2949 case AnimationNodeType::PAR
:
2950 case AnimationNodeType::ITERATE
:
2952 CustomAnimationEffectPtr pEffect
= std::make_shared
<CustomAnimationEffect
>( xChildNode
);
2954 if( pEffect
->mnNodeType
!= -1 )
2956 pEffect
->setEffectSequence( this );
2957 maEffects
.push_back(pEffect
);
2962 // found an after effect
2963 case AnimationNodeType::SET
:
2964 case AnimationNodeType::ANIMATECOLOR
:
2966 processAfterEffect( xChildNode
);
2974 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffects()" );
2978 void EffectSequenceHelper::processAfterEffect( const Reference
< XAnimationNode
>& xNode
)
2982 Reference
< XAnimationNode
> xMaster
;
2984 const Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
2985 const NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
2986 [](const NamedValue
& rProp
) { return rProp
.Name
== "master-element"; });
2988 if (pProp
!= aUserData
.end())
2989 pProp
->Value
>>= xMaster
;
2991 // only process if this is a valid after effect
2994 CustomAnimationEffectPtr pMasterEffect
;
2996 // find the master effect
2997 stl_CustomAnimationEffect_search_node_predict
aSearchPredict( xMaster
);
2998 EffectSequence::iterator
aIter( std::find_if( maEffects
.begin(), maEffects
.end(), aSearchPredict
) );
2999 if( aIter
!= maEffects
.end() )
3000 pMasterEffect
= *aIter
;
3004 pMasterEffect
->setHasAfterEffect( true );
3006 // find out what kind of after effect this is
3007 if( xNode
->getType() == AnimationNodeType::ANIMATECOLOR
)
3010 Reference
< XAnimate
> xAnimate( xNode
, UNO_QUERY_THROW
);
3011 pMasterEffect
->setDimColor( xAnimate
->getTo() );
3012 pMasterEffect
->setAfterEffectOnNext( true );
3017 pMasterEffect
->setAfterEffectOnNext( xNode
->getParent() != xMaster
->getParent() );
3024 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::processAfterEffect()" );
3030 class AnimationChangeListener
: public cppu::WeakImplHelper
< XChangesListener
>
3033 explicit AnimationChangeListener( MainSequence
* pMainSequence
) : mpMainSequence( pMainSequence
) {}
3035 virtual void SAL_CALL
changesOccurred( const css::util::ChangesEvent
& Event
) override
;
3036 virtual void SAL_CALL
disposing( const css::lang::EventObject
& Source
) override
;
3038 MainSequence
* mpMainSequence
;
3043 void SAL_CALL
AnimationChangeListener::changesOccurred( const css::util::ChangesEvent
& )
3045 if( mpMainSequence
)
3046 mpMainSequence
->startRecreateTimer();
3049 void SAL_CALL
AnimationChangeListener::disposing( const css::lang::EventObject
& )
3053 MainSequence::MainSequence()
3054 : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext()))
3055 , maTimer("sd MainSequence maTimer")
3056 , mbTimerMode(false)
3057 , mbRebuilding( false )
3058 , mnRebuildLockGuard( 0 )
3059 , mbPendingRebuildRequest( false )
3060 , mbIgnoreChanges( 0 )
3062 if( mxTimingRootNode
.is() )
3064 Sequence
< css::beans::NamedValue
> aUserData
3065 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE
) } };
3066 mxTimingRootNode
->setUserData( aUserData
);
3071 MainSequence::MainSequence( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
3072 : mxTimingRootNode( xNode
, UNO_QUERY
)
3073 , maTimer("sd MainSequence maTimer")
3074 , mbTimerMode( false )
3075 , mbRebuilding( false )
3076 , mnRebuildLockGuard( 0 )
3077 , mbPendingRebuildRequest( false )
3078 , mbIgnoreChanges( 0 )
3083 MainSequence::~MainSequence()
3088 void MainSequence::init()
3090 mnSequenceType
= EffectNodeType::MAIN_SEQUENCE
;
3092 maTimer
.SetInvokeHandler( LINK(this, MainSequence
, onTimerHdl
) );
3093 maTimer
.SetTimeout(50);
3095 mxChangesListener
.set( new AnimationChangeListener( this ) );
3097 createMainSequence();
3100 void MainSequence::reset( const css::uno::Reference
< css::animations::XAnimationNode
>& xTimingRootNode
)
3104 mxTimingRootNode
.set( xTimingRootNode
, UNO_QUERY
);
3106 createMainSequence();
3109 Reference
< css::animations::XAnimationNode
> MainSequence::getRootNode()
3111 DBG_ASSERT( mnRebuildLockGuard
== 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" );
3113 if( maTimer
.IsActive() && mbTimerMode
)
3115 // force a rebuild NOW if one is pending
3120 return EffectSequenceHelper::getRootNode();
3123 void MainSequence::createMainSequence()
3125 if( mxTimingRootNode
.is() ) try
3127 Reference
< XEnumerationAccess
> xEnumerationAccess( mxTimingRootNode
, UNO_QUERY_THROW
);
3128 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
3129 while( xEnumeration
->hasMoreElements() )
3131 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
3132 sal_Int32 nNodeType
= CustomAnimationEffect::get_node_type( xChildNode
);
3133 if( nNodeType
== EffectNodeType::MAIN_SEQUENCE
)
3135 mxSequenceRoot
.set( xChildNode
, UNO_QUERY
);
3136 EffectSequenceHelper::create( xChildNode
);
3138 else if( nNodeType
== EffectNodeType::INTERACTIVE_SEQUENCE
)
3140 Reference
< XTimeContainer
> xInteractiveRoot( xChildNode
, UNO_QUERY_THROW
);
3141 InteractiveSequencePtr pIS
= std::make_shared
<InteractiveSequence
>( xInteractiveRoot
, this );
3142 pIS
->addListener( this );
3143 maInteractiveSequenceVector
.push_back( pIS
);
3147 // see if we have a mainsequence at all. if not, create one...
3148 if( !mxSequenceRoot
.is() )
3150 mxSequenceRoot
= SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3152 uno::Sequence
< css::beans::NamedValue
> aUserData
3153 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE
) } };
3154 mxSequenceRoot
->setUserData( aUserData
);
3156 // empty sequence until now, set duration to 0.0
3157 // explicitly (otherwise, this sequence will never
3159 mxSequenceRoot
->setDuration( Any(0.0) );
3161 Reference
< XAnimationNode
> xMainSequenceNode( mxSequenceRoot
, UNO_QUERY_THROW
);
3162 mxTimingRootNode
->appendChild( xMainSequenceNode
);
3169 Reference
< XChangesNotifier
> xNotifier( mxTimingRootNode
, UNO_QUERY
);
3170 if( xNotifier
.is() )
3171 xNotifier
->addChangesListener( mxChangesListener
);
3175 TOOLS_WARN_EXCEPTION( "sd", "sd::MainSequence::create()" );
3179 DBG_ASSERT( mxSequenceRoot
.is(), "sd::MainSequence::create(), found no main sequence!" );
3182 void MainSequence::reset()
3184 EffectSequenceHelper::reset();
3186 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3187 interactiveSequence
->reset();
3188 maInteractiveSequenceVector
.clear();
3192 Reference
< XChangesNotifier
> xNotifier( mxTimingRootNode
, UNO_QUERY
);
3193 if( xNotifier
.is() )
3194 xNotifier
->removeChangesListener( mxChangesListener
);
3202 InteractiveSequencePtr
MainSequence::createInteractiveSequence( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
3204 InteractiveSequencePtr pIS
;
3206 // create a new interactive sequence container
3207 Reference
< XTimeContainer
> xISRoot
= SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3209 uno::Sequence
< css::beans::NamedValue
> aUserData
3210 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE
) } };
3211 xISRoot
->setUserData( aUserData
);
3212 xISRoot
->setRestart( css::animations::AnimationRestart::WHEN_NOT_ACTIVE
);
3214 Reference
< XChild
> xChild( mxSequenceRoot
, UNO_QUERY_THROW
);
3215 Reference
< XTimeContainer
> xParent( xChild
->getParent(), UNO_QUERY_THROW
);
3216 xParent
->appendChild( xISRoot
);
3218 pIS
= std::make_shared
<InteractiveSequence
>( xISRoot
, this);
3219 pIS
->setTriggerShape( xShape
);
3220 pIS
->addListener( this );
3221 maInteractiveSequenceVector
.push_back( pIS
);
3225 CustomAnimationEffectPtr
MainSequence::findEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
) const
3227 CustomAnimationEffectPtr pEffect
= EffectSequenceHelper::findEffect( xNode
);
3231 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3233 pEffect
= interactiveSequence
->findEffect( xNode
);
3241 sal_Int32
MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr
& pEffect
) const
3243 sal_Int32 nOffset
= EffectSequenceHelper::getOffsetFromEffect( pEffect
);
3248 nOffset
= EffectSequenceHelper::getCount();
3250 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3252 sal_Int32 nTemp
= interactiveSequence
->getOffsetFromEffect( pEffect
);
3254 return nOffset
+ nTemp
;
3256 nOffset
+= interactiveSequence
->getCount();
3262 CustomAnimationEffectPtr
MainSequence::getEffectFromOffset( sal_Int32 nOffset
) const
3266 if( nOffset
< getCount() )
3267 return EffectSequenceHelper::getEffectFromOffset( nOffset
);
3269 nOffset
-= getCount();
3271 auto aIter( maInteractiveSequenceVector
.begin() );
3273 while( (aIter
!= maInteractiveSequenceVector
.end()) && (nOffset
> (*aIter
)->getCount()) )
3274 nOffset
-= (*aIter
++)->getCount();
3276 if( (aIter
!= maInteractiveSequenceVector
.end()) && (nOffset
>= 0) )
3277 return (*aIter
)->getEffectFromOffset( nOffset
);
3280 CustomAnimationEffectPtr pEffect
;
3284 bool MainSequence::disposeShape( const Reference
< XShape
>& xShape
)
3286 bool bChanges
= EffectSequenceHelper::disposeShape( xShape
);
3288 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3290 bChanges
|= iterativeSequence
->disposeShape( xShape
);
3294 startRebuildTimer();
3299 bool MainSequence::hasEffect( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
3301 if( EffectSequenceHelper::hasEffect( xShape
) )
3304 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3306 if( iterativeSequence
->getTriggerShape() == xShape
)
3309 if( iterativeSequence
->hasEffect( xShape
) )
3316 void MainSequence::insertTextRange( const css::uno::Any
& aTarget
)
3318 EffectSequenceHelper::insertTextRange( aTarget
);
3320 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3322 iterativeSequence
->insertTextRange( aTarget
);
3326 void MainSequence::disposeTextRange( const css::uno::Any
& aTarget
)
3328 EffectSequenceHelper::disposeTextRange( aTarget
);
3330 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3332 iterativeSequence
->disposeTextRange( aTarget
);
3336 /** callback from the sd::View when an object just left text edit mode */
3337 void MainSequence::onTextChanged( const Reference
< XShape
>& xShape
)
3339 EffectSequenceHelper::onTextChanged( xShape
);
3341 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3343 iterativeSequence
->onTextChanged( xShape
);
3347 void EffectSequenceHelper::onTextChanged( const Reference
< XShape
>& xShape
)
3349 // get map [paragraph index] -> [NumberingLevel]
3350 // for following reusage inside all animation effects
3351 std::vector
< sal_Int32
> paragraphNumberingLevel
;
3352 std::vector
< sal_Int32
>* paragraphNumberingLevelParam
= nullptr;
3353 if ( getParagraphNumberingLevels( xShape
, paragraphNumberingLevel
) )
3354 paragraphNumberingLevelParam
= ¶graphNumberingLevel
;
3356 // update internal flags for each animation effect
3357 const bool bChanges
= std::accumulate(maEffects
.begin(), maEffects
.end(), false,
3358 [&xShape
, ¶graphNumberingLevelParam
](const bool bCheck
, const CustomAnimationEffectPtr
& rxEffect
) {
3360 if (rxEffect
->getTargetShape() == xShape
)
3361 bRes
|= rxEffect
->checkForText( paragraphNumberingLevelParam
);
3369 void MainSequence::rebuild()
3371 startRebuildTimer();
3374 void MainSequence::lockRebuilds()
3376 mnRebuildLockGuard
++;
3379 void MainSequence::unlockRebuilds()
3381 DBG_ASSERT( mnRebuildLockGuard
, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" );
3382 if( mnRebuildLockGuard
)
3383 mnRebuildLockGuard
--;
3385 if( (mnRebuildLockGuard
== 0) && mbPendingRebuildRequest
)
3387 mbPendingRebuildRequest
= false;
3388 startRebuildTimer();
3392 void MainSequence::implRebuild()
3394 if( mnRebuildLockGuard
)
3396 mbPendingRebuildRequest
= true;
3400 mbRebuilding
= true;
3402 EffectSequenceHelper::implRebuild();
3404 auto aIter( maInteractiveSequenceVector
.begin() );
3405 while( aIter
!= maInteractiveSequenceVector
.end() )
3407 InteractiveSequencePtr
pIS( *aIter
);
3408 if( pIS
->maEffects
.empty() )
3410 // remove empty interactive sequences
3411 aIter
= maInteractiveSequenceVector
.erase( aIter
);
3413 Reference
< XChild
> xChild( mxSequenceRoot
, UNO_QUERY_THROW
);
3414 Reference
< XTimeContainer
> xParent( xChild
->getParent(), UNO_QUERY_THROW
);
3415 Reference
< XAnimationNode
> xISNode( pIS
->mxSequenceRoot
, UNO_QUERY_THROW
);
3416 xParent
->removeChild( xISNode
);
3426 mbRebuilding
= false;
3429 void MainSequence::notify_change()
3434 bool MainSequence::setTrigger( const CustomAnimationEffectPtr
& pEffect
, const css::uno::Reference
< css::drawing::XShape
>& xTriggerShape
)
3436 EffectSequenceHelper
* pOldSequence
= pEffect
->getEffectSequence();
3438 EffectSequenceHelper
* pNewSequence
= nullptr;
3439 if( xTriggerShape
.is() )
3441 for (InteractiveSequencePtr
const& pIS
: maInteractiveSequenceVector
)
3443 if( pIS
->getTriggerShape() == xTriggerShape
)
3445 pNewSequence
= pIS
.get();
3451 pNewSequence
= createInteractiveSequence( xTriggerShape
).get();
3455 pNewSequence
= this;
3458 if( pOldSequence
!= pNewSequence
)
3461 pOldSequence
->maEffects
.remove( pEffect
);
3463 pNewSequence
->maEffects
.push_back( pEffect
);
3464 pEffect
->setEffectSequence( pNewSequence
);
3474 IMPL_LINK_NOARG(MainSequence
, onTimerHdl
, Timer
*, void)
3483 createMainSequence();
3487 /** starts a timer that recreates the internal structure from the API core */
3488 void MainSequence::startRecreateTimer()
3490 if( !mbRebuilding
&& (mbIgnoreChanges
== 0) )
3492 mbTimerMode
= false;
3498 * starts a timer that rebuilds the API core from the internal structure
3499 * This is used to reduce the number of screen redraws due to animation changes.
3501 void MainSequence::startRebuildTimer()
3507 InteractiveSequence::InteractiveSequence( const Reference
< XTimeContainer
>& xSequenceRoot
, MainSequence
* pMainSequence
)
3508 : EffectSequenceHelper( xSequenceRoot
), mpMainSequence( pMainSequence
)
3510 mnSequenceType
= EffectNodeType::INTERACTIVE_SEQUENCE
;
3514 if( mxSequenceRoot
.is() )
3516 Reference
< XEnumerationAccess
> xEnumerationAccess( mxSequenceRoot
, UNO_QUERY_THROW
);
3517 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
3518 while( !mxEventSource
.is() && xEnumeration
->hasMoreElements() )
3520 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
3523 if( (xChildNode
->getBegin() >>= aEvent
) && (aEvent
.Trigger
== EventTrigger::ON_CLICK
) )
3524 aEvent
.Source
>>= mxEventSource
;
3530 TOOLS_WARN_EXCEPTION( "sd", "sd::InteractiveSequence::InteractiveSequence()" );
3535 void InteractiveSequence::rebuild()
3537 mpMainSequence
->rebuild();
3540 void InteractiveSequence::implRebuild()
3542 EffectSequenceHelper::implRebuild();
3545 MainSequenceRebuildGuard::MainSequenceRebuildGuard( MainSequencePtr pMainSequence
)
3546 : mpMainSequence(std::move( pMainSequence
))
3548 if( mpMainSequence
)
3549 mpMainSequence
->lockRebuilds();
3552 MainSequenceRebuildGuard::~MainSequenceRebuildGuard()
3554 if( mpMainSequence
)
3555 mpMainSequence
->unlockRebuilds();
3560 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */