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 <sal/log.hxx>
22 #include <com/sun/star/animations/AnimationNodeType.hpp>
23 #include <com/sun/star/animations/AnimateColor.hpp>
24 #include <com/sun/star/animations/AnimateMotion.hpp>
25 #include <com/sun/star/animations/AnimateSet.hpp>
26 #include <com/sun/star/animations/AnimationFill.hpp>
27 #include <com/sun/star/animations/Audio.hpp>
28 #include <com/sun/star/animations/Command.hpp>
29 #include <com/sun/star/animations/Event.hpp>
30 #include <com/sun/star/animations/EventTrigger.hpp>
31 #include <com/sun/star/animations/IterateContainer.hpp>
32 #include <com/sun/star/animations/ParallelTimeContainer.hpp>
33 #include <com/sun/star/animations/SequenceTimeContainer.hpp>
34 #include <com/sun/star/animations/XCommand.hpp>
35 #include <com/sun/star/animations/XIterateContainer.hpp>
36 #include <com/sun/star/animations/XAnimateTransform.hpp>
37 #include <com/sun/star/animations/XAnimateMotion.hpp>
38 #include <com/sun/star/animations/XAnimate.hpp>
39 #include <com/sun/star/animations/AnimationRestart.hpp>
40 #include <com/sun/star/beans/NamedValue.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/container/XEnumerationAccess.hpp>
43 #include <com/sun/star/lang/XInitialization.hpp>
44 #include <com/sun/star/presentation/EffectNodeType.hpp>
45 #include <com/sun/star/presentation/EffectCommands.hpp>
46 #include <com/sun/star/presentation/EffectPresetClass.hpp>
47 #include <com/sun/star/presentation/ParagraphTarget.hpp>
48 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
49 #include <com/sun/star/text/XText.hpp>
50 #include <com/sun/star/util/XCloneable.hpp>
51 #include <com/sun/star/util/XChangesNotifier.hpp>
52 #include <comphelper/processfactory.hxx>
53 #include <comphelper/sequence.hxx>
54 #include <com/sun/star/lang/Locale.hpp>
55 #include <com/sun/star/i18n/BreakIterator.hpp>
56 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
57 #include <com/sun/star/i18n/WordType.hpp>
58 #include <com/sun/star/presentation/TextAnimationType.hpp>
60 #include <basegfx/polygon/b2dpolypolygon.hxx>
61 #include <basegfx/polygon/b2dpolypolygontools.hxx>
62 #include <basegfx/range/b2drange.hxx>
63 #include <basegfx/matrix/b2dhommatrixtools.hxx>
69 #include <cppuhelper/implbase.hxx>
71 #include <drawinglayer/geometry/viewinformation2d.hxx>
72 #include <svx/sdr/contact/viewcontact.hxx>
73 #include <svx/svdopath.hxx>
74 #include <svx/svdpage.hxx>
75 #include <svx/unoapi.hxx>
76 #include <CustomAnimationEffect.hxx>
77 #include <CustomAnimationPreset.hxx>
78 #include <animations.hxx>
80 using namespace ::com::sun::star
;
81 using namespace ::com::sun::star::uno
;
82 using namespace ::com::sun::star::presentation
;
83 using namespace ::com::sun::star::animations
;
85 using ::com::sun::star::container::XEnumerationAccess
;
86 using ::com::sun::star::container::XEnumeration
;
87 using ::com::sun::star::beans::NamedValue
;
88 using ::com::sun::star::container::XChild
;
89 using ::com::sun::star::drawing::XShape
;
90 using ::com::sun::star::lang::XInitialization
;
91 using ::com::sun::star::text::XText
;
92 using ::com::sun::star::text::XTextRange
;
93 using ::com::sun::star::beans::XPropertySet
;
94 using ::com::sun::star::util::XCloneable
;
95 using ::com::sun::star::lang::Locale
;
96 using ::com::sun::star::util::XChangesNotifier
;
97 using ::com::sun::star::util::XChangesListener
;
101 class MainSequenceChangeGuard
104 explicit MainSequenceChangeGuard( EffectSequenceHelper
* pSequence
)
106 mpMainSequence
= dynamic_cast< MainSequence
* >( pSequence
);
107 if( mpMainSequence
== nullptr )
109 InteractiveSequence
* pI
= dynamic_cast< InteractiveSequence
* >( pSequence
);
111 mpMainSequence
= pI
->mpMainSequence
;
113 DBG_ASSERT( mpMainSequence
, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" );
116 mpMainSequence
->mbIgnoreChanges
++;
119 ~MainSequenceChangeGuard()
122 mpMainSequence
->mbIgnoreChanges
++;
126 MainSequence
* mpMainSequence
;
129 CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
132 mnFill(AnimationFill::HOLD
),
135 mfAbsoluteDuration(-1.0),
138 mfIterateInterval(0.0),
141 mfAcceleration( 1.0 ),
143 mbAutoReverse(false),
146 mpEffectSequence( nullptr ),
147 mbHasAfterEffect(false),
148 mbAfterEffectOnNextEffect(false)
153 void CustomAnimationEffect::setNode( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
158 const Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
160 for( const NamedValue
& rProp
: aUserData
)
162 if ( rProp
.Name
== "node-type" )
164 rProp
.Value
>>= mnNodeType
;
166 else if ( rProp
.Name
== "preset-id" )
168 rProp
.Value
>>= maPresetId
;
170 else if ( rProp
.Name
== "preset-sub-type" )
172 rProp
.Value
>>= maPresetSubType
;
174 else if ( rProp
.Name
== "preset-class" )
176 rProp
.Value
>>= mnPresetClass
;
178 else if ( rProp
.Name
== "preset-property" )
180 rProp
.Value
>>= maProperty
;
182 else if ( rProp
.Name
== "group-id" )
184 rProp
.Value
>>= mnGroupId
;
188 // get effect start time
189 mxNode
->getBegin() >>= mfBegin
;
191 mfAcceleration
= mxNode
->getAcceleration();
192 mfDecelerate
= mxNode
->getDecelerate();
193 mbAutoReverse
= mxNode
->getAutoReverse();
195 mnFill
= mxNode
->getFill();
197 // get iteration data
198 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
201 mfIterateInterval
= xIter
->getIterateInterval();
202 mnIterateType
= xIter
->getIterateType();
203 maTarget
= xIter
->getTarget();
204 mnTargetSubItem
= xIter
->getSubItem();
208 mfIterateInterval
= 0.0f
;
212 // calculate effect duration and get target shape
213 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
214 if( xEnumerationAccess
.is() )
216 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
217 if( xEnumeration
.is() )
219 while( xEnumeration
->hasMoreElements() )
221 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY
);
222 if( !xChildNode
.is() )
225 if( xChildNode
->getType() == AnimationNodeType::AUDIO
)
227 mxAudio
.set( xChildNode
, UNO_QUERY
);
229 else if( xChildNode
->getType() == AnimationNodeType::COMMAND
)
231 Reference
< XCommand
> xCommand( xChildNode
, UNO_QUERY
);
234 mnCommand
= xCommand
->getCommand();
235 if( !maTarget
.hasValue() )
236 maTarget
= xCommand
->getTarget();
242 double fDuration
= 0.0;
243 xChildNode
->getBegin() >>= fBegin
;
244 xChildNode
->getDuration() >>= fDuration
;
247 if( fDuration
> mfDuration
)
248 mfDuration
= fDuration
;
250 // no target shape yet?
251 if( !maTarget
.hasValue() )
254 Reference
< XAnimate
> xAnimate( xChildNode
, UNO_QUERY
);
257 maTarget
= xAnimate
->getTarget();
258 mnTargetSubItem
= xAnimate
->getSubItem();
266 mfAbsoluteDuration
= mfDuration
;
267 double fRepeatCount
= 1.0;
268 if( (mxNode
->getRepeatCount()) >>= fRepeatCount
)
269 mfAbsoluteDuration
*= fRepeatCount
;
274 sal_Int32
CustomAnimationEffect::getNumberOfSubitems( const Any
& aTarget
, sal_Int16 nIterateType
)
276 sal_Int32 nSubItems
= 0;
280 // first get target text
281 sal_Int32 nOnlyPara
= -1;
283 Reference
< XText
> xShape
;
287 ParagraphTarget aParaTarget
;
288 if( aTarget
>>= aParaTarget
)
290 xShape
.set( aParaTarget
.Shape
, UNO_QUERY
);
291 nOnlyPara
= aParaTarget
.Paragraph
;
295 // now use the break iterator to iterate over the given text
296 // and count the sub items
300 // TODO/LATER: Optimize this, don't create a break iterator each time
301 Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
302 Reference
< i18n::XBreakIterator
> xBI
= i18n::BreakIterator::create(xContext
);
304 Reference
< XEnumerationAccess
> xEA( xShape
, UNO_QUERY_THROW
);
305 Reference
< XEnumeration
> xEnumeration( xEA
->createEnumeration(), UNO_SET_THROW
);
306 css::lang::Locale aLocale
;
307 const OUString
aStrLocaleName( "CharLocale" );
308 Reference
< XTextRange
> xParagraph
;
311 while( xEnumeration
->hasMoreElements() )
313 xEnumeration
->nextElement() >>= xParagraph
;
315 // skip this if it's not the only paragraph we want to count
316 if( (nOnlyPara
!= -1) && (nOnlyPara
!= nPara
) )
319 if( nIterateType
== TextAnimationType::BY_PARAGRAPH
)
325 const OUString
aText( xParagraph
->getString() );
326 Reference
< XPropertySet
> xSet( xParagraph
, UNO_QUERY_THROW
);
327 xSet
->getPropertyValue( aStrLocaleName
) >>= aLocale
;
330 const sal_Int32 nEndPos
= aText
.getLength();
332 if( nIterateType
== TextAnimationType::BY_WORD
)
334 for( nPos
= 0; nPos
< nEndPos
; nPos
++ )
336 nPos
= xBI
->getWordBoundary(aText
, nPos
, aLocale
, i18n::WordType::ANY_WORD
, true).endPos
;
344 for( nPos
= 0; nPos
< nEndPos
; nPos
++ )
346 nPos
= xBI
->nextCharacters(aText
, nPos
, aLocale
, i18n::CharacterIteratorMode::SKIPCELL
, 0, nDone
);
352 if( nPara
== nOnlyPara
)
362 OSL_FAIL( "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" );
368 CustomAnimationEffect::~CustomAnimationEffect()
372 CustomAnimationEffectPtr
CustomAnimationEffect::clone() const
374 Reference
< XCloneable
> xCloneable( mxNode
, UNO_QUERY_THROW
);
375 Reference
< XAnimationNode
> xNode( xCloneable
->createClone(), UNO_QUERY_THROW
);
376 CustomAnimationEffectPtr
pEffect( new CustomAnimationEffect( xNode
) );
377 pEffect
->setEffectSequence( getEffectSequence() );
381 sal_Int32
CustomAnimationEffect::get_node_type( const Reference
< XAnimationNode
>& xNode
)
383 sal_Int16 nNodeType
= -1;
387 Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
388 if( aUserData
.hasElements() )
390 const NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
391 [](const NamedValue
& rProp
) { return rProp
.Name
== "node-type"; });
392 if (pProp
!= aUserData
.end())
393 pProp
->Value
>>= nNodeType
;
400 void CustomAnimationEffect::setPresetClass( sal_Int16 nPresetClass
)
402 if( mnPresetClass
== nPresetClass
)
405 mnPresetClass
= nPresetClass
;
409 // first try to find a "preset-class" entry in the user data
411 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
412 sal_Int32 nLength
= aUserData
.getLength();
416 NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
417 [](const NamedValue
& rProp
) { return rProp
.Name
== "preset-class"; });
418 if (pProp
!= aUserData
.end())
420 pProp
->Value
<<= mnPresetClass
;
425 // no "preset-class" entry inside user data, so add it
428 aUserData
.realloc( nLength
+ 1);
429 aUserData
[nLength
].Name
= "preset-class";
430 aUserData
[nLength
].Value
<<= mnPresetClass
;
433 mxNode
->setUserData( aUserData
);
436 void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType
)
438 if( mnNodeType
== nNodeType
)
441 mnNodeType
= nNodeType
;
445 // first try to find a "node-type" entry in the user data
447 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
448 sal_Int32 nLength
= aUserData
.getLength();
452 NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
453 [](const NamedValue
& rProp
) { return rProp
.Name
== "node-type"; });
454 if (pProp
!= aUserData
.end())
456 pProp
->Value
<<= mnNodeType
;
461 // no "node-type" entry inside user data, so add it
464 aUserData
.realloc( nLength
+ 1);
465 aUserData
[nLength
].Name
= "node-type";
466 aUserData
[nLength
].Value
<<= mnNodeType
;
469 mxNode
->setUserData( aUserData
);
472 void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId
)
474 mnGroupId
= nGroupId
;
478 // first try to find a "group-id" entry in the user data
480 Sequence
< NamedValue
> aUserData( mxNode
->getUserData() );
481 sal_Int32 nLength
= aUserData
.getLength();
485 NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
486 [](const NamedValue
& rProp
) { return rProp
.Name
== "group-id"; });
487 if (pProp
!= aUserData
.end())
489 pProp
->Value
<<= mnGroupId
;
494 // no "group-id" entry inside user data, so add it
497 aUserData
.realloc( nLength
+ 1);
498 aUserData
[nLength
].Name
= "group-id";
499 aUserData
[nLength
].Value
<<= mnGroupId
;
502 mxNode
->setUserData( aUserData
);
505 /** checks if the text for this effect has changed and updates internal flags.
506 returns true if something changed.
508 bool CustomAnimationEffect::checkForText()
510 bool bChange
= false;
512 Reference
< XText
> xText
;
514 if( maTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
517 ParagraphTarget aParaTarget
;
518 maTarget
>>= aParaTarget
;
520 xText
.set( aParaTarget
.Shape
, UNO_QUERY
);
525 Reference
< XEnumerationAccess
> xEA( xText
, UNO_QUERY
);
528 Reference
< XEnumeration
> xEnumeration
= xEA
->createEnumeration();
529 if( xEnumeration
.is() )
531 bool bHasText
= xEnumeration
->hasMoreElements();
532 bChange
|= bHasText
!= mbHasText
;
533 mbHasText
= bHasText
;
535 sal_Int32 nPara
= aParaTarget
.Paragraph
;
537 while( xEnumeration
->hasMoreElements() && nPara
-- )
538 xEnumeration
->nextElement();
540 if( xEnumeration
->hasMoreElements() )
542 Reference
< XPropertySet
> xParaSet
;
543 xEnumeration
->nextElement() >>= xParaSet
;
546 sal_Int32 nParaDepth
= 0;
547 const OUString
strNumberingLevel( "NumberingLevel" );
548 xParaSet
->getPropertyValue( strNumberingLevel
) >>= nParaDepth
;
549 bChange
|= nParaDepth
!= mnParaDepth
;
550 mnParaDepth
= nParaDepth
;
560 bool bHasText
= xText
.is() && !xText
->getString().isEmpty();
561 bChange
|= bHasText
!= mbHasText
;
562 mbHasText
= bHasText
;
565 bChange
|= calculateIterateDuration();
569 bool CustomAnimationEffect::calculateIterateDuration()
571 bool bChange
= false;
573 // if we have an iteration, we must also calculate the
574 // 'true' container duration, that is
575 // ( ( is form animated ) ? [contained effects duration] : 0 ) +
576 // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration]
577 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
580 double fDuration
= mfDuration
;
581 const double fSubEffectDuration
= mfDuration
;
583 if( mnTargetSubItem
!= ShapeAnimationSubType::ONLY_BACKGROUND
) // does not make sense for iterate container but better check
585 const sal_Int32 nSubItems
= getNumberOfSubitems( maTarget
, mnIterateType
);
588 const double f
= (nSubItems
-1) * mfIterateInterval
;
593 // if we also animate the form first, we have to add the
594 // sub effect duration to the whole effect duration
595 if( mnTargetSubItem
== ShapeAnimationSubType::AS_WHOLE
)
596 fDuration
+= fSubEffectDuration
;
598 bChange
|= fDuration
!= mfAbsoluteDuration
;
599 mfAbsoluteDuration
= fDuration
;
605 void CustomAnimationEffect::setTarget( const css::uno::Any
& rTarget
)
611 // first, check special case for random node
612 Reference
< XInitialization
> xInit( mxNode
, UNO_QUERY
);
615 const Sequence
< Any
> aArgs( &maTarget
, 1 );
616 xInit
->initialize( aArgs
);
620 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
623 xIter
->setTarget(maTarget
);
627 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
628 if( xEnumerationAccess
.is() )
630 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
631 if( xEnumeration
.is() )
633 while( xEnumeration
->hasMoreElements() )
635 const Any
aElem( xEnumeration
->nextElement() );
636 Reference
< XAnimate
> xAnimate( aElem
, UNO_QUERY
);
638 xAnimate
->setTarget( rTarget
);
641 Reference
< XCommand
> xCommand( aElem
, UNO_QUERY
);
643 xCommand
->setTarget( rTarget
);
654 OSL_FAIL( "sd::CustomAnimationEffect::setTarget(), exception caught!" );
658 void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem
)
662 mnTargetSubItem
= nSubItem
;
664 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
667 xIter
->setSubItem(mnTargetSubItem
);
671 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
672 if( xEnumerationAccess
.is() )
674 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
675 if( xEnumeration
.is() )
677 while( xEnumeration
->hasMoreElements() )
679 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
681 xAnimate
->setSubItem( mnTargetSubItem
);
689 OSL_FAIL( "sd::CustomAnimationEffect::setTargetSubItem(), exception caught!" );
693 void CustomAnimationEffect::setDuration( double fDuration
)
695 if( (mfDuration
== -1.0) || (mfDuration
== fDuration
) )
700 double fScale
= fDuration
/ mfDuration
;
701 mfDuration
= fDuration
;
702 double fRepeatCount
= 1.0;
703 getRepeatCount() >>= fRepeatCount
;
704 mfAbsoluteDuration
= mfDuration
* fRepeatCount
;
706 // calculate effect duration and get target shape
707 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
708 if( xEnumerationAccess
.is() )
710 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
711 if( xEnumeration
.is() )
713 while( xEnumeration
->hasMoreElements() )
715 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY
);
716 if( !xChildNode
.is() )
719 double fChildBegin
= 0.0;
720 xChildNode
->getBegin() >>= fChildBegin
;
721 if( fChildBegin
!= 0.0 )
723 fChildBegin
*= fScale
;
724 xChildNode
->setBegin( makeAny( fChildBegin
) );
727 double fChildDuration
= 0.0;
728 xChildNode
->getDuration() >>= fChildDuration
;
729 if( fChildDuration
!= 0.0 )
731 fChildDuration
*= fScale
;
732 xChildNode
->setDuration( makeAny( fChildDuration
) );
737 calculateIterateDuration();
741 OSL_FAIL( "sd::CustomAnimationEffect::setDuration(), exception caught!" );
745 void CustomAnimationEffect::setBegin( double fBegin
)
747 if( mxNode
.is() ) try
750 mxNode
->setBegin( makeAny( fBegin
) );
754 OSL_FAIL( "sd::CustomAnimationEffect::setBegin(), exception caught!" );
758 void CustomAnimationEffect::setAcceleration( double fAcceleration
)
760 if( mxNode
.is() ) try
762 mfAcceleration
= fAcceleration
;
763 mxNode
->setAcceleration( fAcceleration
);
767 OSL_FAIL( "sd::CustomAnimationEffect::setAcceleration(), exception caught!" );
771 void CustomAnimationEffect::setDecelerate( double fDecelerate
)
773 if( mxNode
.is() ) try
775 mfDecelerate
= fDecelerate
;
776 mxNode
->setDecelerate( fDecelerate
);
780 OSL_FAIL( "sd::CustomAnimationEffect::setDecelerate(), exception caught!" );
784 void CustomAnimationEffect::setAutoReverse( bool bAutoReverse
)
786 if( mxNode
.is() ) try
788 mbAutoReverse
= bAutoReverse
;
789 mxNode
->setAutoReverse( bAutoReverse
);
793 OSL_FAIL( "sd::CustomAnimationEffect::setAutoReverse(), exception caught!" );
797 void CustomAnimationEffect::replaceNode( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
799 sal_Int16 nNodeType
= mnNodeType
;
800 Any aTarget
= maTarget
;
802 sal_Int16 nFill
= mnFill
;
803 double fBegin
= mfBegin
;
804 double fDuration
= mfDuration
;
805 double fAcceleration
= mfAcceleration
;
806 double fDecelerate
= mfDecelerate
;
807 bool bAutoReverse
= mbAutoReverse
;
808 Reference
< XAudio
> xAudio( mxAudio
);
809 sal_Int16 nIterateType
= mnIterateType
;
810 double fIterateInterval
= mfIterateInterval
;
811 sal_Int16 nSubItem
= mnTargetSubItem
;
816 setNodeType( nNodeType
);
817 setTarget( aTarget
);
818 setTargetSubItem( nSubItem
);
819 setDuration( fDuration
);
823 setAcceleration( fAcceleration
);
824 setDecelerate( fDecelerate
);
825 setAutoReverse( bAutoReverse
);
827 if( nIterateType
!= mnIterateType
)
828 setIterateType( nIterateType
);
830 if( mnIterateType
&& ( fIterateInterval
!= mfIterateInterval
) )
831 setIterateInterval( fIterateInterval
);
834 Reference
< XShape
> CustomAnimationEffect::getTargetShape() const
836 Reference
< XShape
> xShape
;
840 ParagraphTarget aParaTarget
;
841 if( maTarget
>>= aParaTarget
)
842 xShape
= aParaTarget
.Shape
;
848 Any
CustomAnimationEffect::getRepeatCount() const
852 return mxNode
->getRepeatCount();
861 Any
CustomAnimationEffect::getEnd() const
865 return mxNode
->getEnd();
874 void CustomAnimationEffect::setRepeatCount( const Any
& rRepeatCount
)
878 mxNode
->setRepeatCount( rRepeatCount
);
879 double fRepeatCount
= 1.0;
880 rRepeatCount
>>= fRepeatCount
;
881 mfAbsoluteDuration
= mfDuration
* fRepeatCount
;
885 void CustomAnimationEffect::setEnd( const Any
& rEnd
)
888 mxNode
->setEnd( rEnd
);
891 void CustomAnimationEffect::setFill( sal_Int16 nFill
)
896 mxNode
->setFill( nFill
);
900 Reference
< XAnimationNode
> CustomAnimationEffect::createAfterEffectNode() const
902 DBG_ASSERT( mbHasAfterEffect
, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" );
904 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
906 Reference
< XAnimate
> xAnimate
;
907 if( maDimColor
.hasValue() )
908 xAnimate
= AnimateColor::create( xContext
);
910 xAnimate
= AnimateSet::create( xContext
);
913 OUString aAttributeName
;
915 if( maDimColor
.hasValue() )
918 aAttributeName
= "DimColor";
923 aAttributeName
= "Visibility";
927 if( !mbAfterEffectOnNextEffect
) // sameClick
931 aEvent
.Source
<<= getNode();
932 aEvent
.Trigger
= EventTrigger::END_EVENT
;
942 xAnimate
->setBegin( aBegin
);
943 xAnimate
->setTo( aTo
);
944 xAnimate
->setAttributeName( aAttributeName
);
946 xAnimate
->setDuration( makeAny( 0.001 ) );
947 xAnimate
->setFill( AnimationFill::HOLD
);
948 xAnimate
->setTarget( maTarget
);
953 void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType
)
955 if( mnIterateType
== nIterateType
)
960 // do we need to exchange the container node?
961 if( (mnIterateType
== 0) || (nIterateType
== 0) )
963 sal_Int16 nTargetSubItem
= mnTargetSubItem
;
965 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
966 Reference
< XTimeContainer
> xNewContainer
;
969 xNewContainer
.set( IterateContainer::create( xContext
) );
972 xNewContainer
.set( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
974 Reference
< XTimeContainer
> xOldContainer( mxNode
, UNO_QUERY_THROW
);
975 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
976 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
977 while( xEnumeration
->hasMoreElements() )
979 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
980 xOldContainer
->removeChild( xChildNode
);
981 xNewContainer
->appendChild( xChildNode
);
984 xNewContainer
->setBegin( mxNode
->getBegin() );
985 xNewContainer
->setDuration( mxNode
->getDuration() );
986 xNewContainer
->setEnd( mxNode
->getEnd() );
987 xNewContainer
->setEndSync( mxNode
->getEndSync() );
988 xNewContainer
->setRepeatCount( mxNode
->getRepeatCount() );
989 xNewContainer
->setFill( mxNode
->getFill() );
990 xNewContainer
->setFillDefault( mxNode
->getFillDefault() );
991 xNewContainer
->setRestart( mxNode
->getRestart() );
992 xNewContainer
->setRestartDefault( mxNode
->getRestartDefault() );
993 xNewContainer
->setAcceleration( mxNode
->getAcceleration() );
994 xNewContainer
->setDecelerate( mxNode
->getDecelerate() );
995 xNewContainer
->setAutoReverse( mxNode
->getAutoReverse() );
996 xNewContainer
->setRepeatDuration( mxNode
->getRepeatDuration() );
997 xNewContainer
->setEndSync( mxNode
->getEndSync() );
998 xNewContainer
->setRepeatCount( mxNode
->getRepeatCount() );
999 xNewContainer
->setUserData( mxNode
->getUserData() );
1001 mxNode
= xNewContainer
;
1006 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY_THROW
);
1007 xIter
->setTarget(maTarget
);
1008 xIter
->setSubItem( nTargetSubItem
);
1015 Reference
< XEnumerationAccess
> xEA( mxNode
, UNO_QUERY_THROW
);
1016 Reference
< XEnumeration
> xE( xEA
->createEnumeration(), UNO_SET_THROW
);
1017 while( xE
->hasMoreElements() )
1019 Reference
< XAnimate
> xAnimate( xE
->nextElement(), UNO_QUERY
);
1022 xAnimate
->setTarget( aTarget
);
1023 xAnimate
->setSubItem( nTargetSubItem
);
1028 mnIterateType
= nIterateType
;
1030 // if we have an iteration container, we must set its type
1033 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY_THROW
);
1034 xIter
->setIterateType( nIterateType
);
1041 OSL_FAIL( "sd::CustomAnimationEffect::setIterateType(), Exception caught!" );
1045 void CustomAnimationEffect::setIterateInterval( double fIterateInterval
)
1047 if( mfIterateInterval
== fIterateInterval
)
1050 Reference
< XIterateContainer
> xIter( mxNode
, UNO_QUERY
);
1052 DBG_ASSERT( xIter
.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" );
1055 mfIterateInterval
= fIterateInterval
;
1056 xIter
->setIterateInterval( fIterateInterval
);
1059 calculateIterateDuration();
1062 OUString
CustomAnimationEffect::getPath() const
1066 if( mxNode
.is() ) try
1068 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
1069 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1070 while( xEnumeration
->hasMoreElements() )
1072 Reference
< XAnimateMotion
> xMotion( xEnumeration
->nextElement(), UNO_QUERY
);
1075 xMotion
->getPath() >>= aPath
;
1082 OSL_FAIL("sd::CustomAnimationEffect::getPath(), exception caught!" );
1088 void CustomAnimationEffect::setPath( const OUString
& rPath
)
1095 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY_THROW
);
1096 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1097 while( xEnumeration
->hasMoreElements() )
1099 Reference
< XAnimateMotion
> xMotion( xEnumeration
->nextElement(), UNO_QUERY
);
1103 MainSequenceChangeGuard
aGuard( mpEffectSequence
);
1104 xMotion
->setPath( Any( rPath
) );
1111 OSL_FAIL("sd::CustomAnimationEffect::setPath(), exception caught!" );
1115 Any
CustomAnimationEffect::getProperty( sal_Int32 nNodeType
, const OUString
& rAttributeName
, EValue eValue
)
1118 if( mxNode
.is() ) try
1120 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1121 if( xEnumerationAccess
.is() )
1123 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1124 if( xEnumeration
.is() )
1126 while( xEnumeration
->hasMoreElements() && !aProperty
.hasValue() )
1128 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1129 if( !xAnimate
.is() )
1132 if( xAnimate
->getType() == nNodeType
)
1134 if( xAnimate
->getAttributeName() == rAttributeName
)
1138 case EValue::To
: aProperty
= xAnimate
->getTo(); break;
1139 case EValue::By
: aProperty
= xAnimate
->getBy(); break;
1149 OSL_FAIL("sd::CustomAnimationEffect::getProperty(), exception caught!" );
1155 bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType
, const OUString
& rAttributeName
, EValue eValue
, const Any
& rValue
)
1157 bool bChanged
= false;
1158 if( mxNode
.is() ) try
1160 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1161 if( xEnumerationAccess
.is() )
1163 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1164 if( xEnumeration
.is() )
1166 while( xEnumeration
->hasMoreElements() )
1168 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1169 if( !xAnimate
.is() )
1172 if( xAnimate
->getType() == nNodeType
)
1174 if( xAnimate
->getAttributeName() == rAttributeName
)
1179 if( xAnimate
->getTo() != rValue
)
1181 xAnimate
->setTo( rValue
);
1186 if( xAnimate
->getTo() != rValue
)
1188 xAnimate
->setBy( rValue
);
1201 OSL_FAIL("sd::CustomAnimationEffect::setProperty(), exception caught!" );
1207 static bool implIsColorAttribute( const OUString
& rAttributeName
)
1209 return rAttributeName
== "FillColor" || rAttributeName
== "LineColor" || rAttributeName
== "CharColor";
1212 Any
CustomAnimationEffect::getColor( sal_Int32 nIndex
)
1215 if( mxNode
.is() ) try
1217 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1218 if( xEnumerationAccess
.is() )
1220 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1221 if( xEnumeration
.is() )
1223 while( xEnumeration
->hasMoreElements() && !aColor
.hasValue() )
1225 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1226 if( !xAnimate
.is() )
1229 switch( xAnimate
->getType() )
1231 case AnimationNodeType::SET
:
1232 case AnimationNodeType::ANIMATE
:
1233 if( !implIsColorAttribute( xAnimate
->getAttributeName() ) )
1236 case AnimationNodeType::ANIMATECOLOR
:
1237 Sequence
<Any
> aValues( xAnimate
->getValues() );
1238 if( aValues
.hasElements() )
1240 if( aValues
.getLength() > nIndex
)
1241 aColor
= aValues
[nIndex
];
1243 else if( nIndex
== 0 )
1244 aColor
= xAnimate
->getFrom();
1246 aColor
= xAnimate
->getTo();
1254 OSL_FAIL("sd::CustomAnimationEffect::getColor(), exception caught!" );
1260 void CustomAnimationEffect::setColor( sal_Int32 nIndex
, const Any
& rColor
)
1267 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1268 if( xEnumerationAccess
.is() )
1270 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1271 if( xEnumeration
.is() )
1273 while( xEnumeration
->hasMoreElements() )
1275 Reference
< XAnimate
> xAnimate( xEnumeration
->nextElement(), UNO_QUERY
);
1276 if( !xAnimate
.is() )
1279 switch( xAnimate
->getType() )
1281 case AnimationNodeType::SET
:
1282 case AnimationNodeType::ANIMATE
:
1283 if( !implIsColorAttribute( xAnimate
->getAttributeName() ) )
1286 case AnimationNodeType::ANIMATECOLOR
:
1288 Sequence
<Any
> aValues( xAnimate
->getValues() );
1289 if( aValues
.hasElements() )
1291 if( aValues
.getLength() > nIndex
)
1293 aValues
[nIndex
] = rColor
;
1294 xAnimate
->setValues( aValues
);
1297 else if( (nIndex
== 0) && xAnimate
->getFrom().hasValue() )
1298 xAnimate
->setFrom(rColor
);
1299 else if( (nIndex
== 1) && xAnimate
->getTo().hasValue() )
1300 xAnimate
->setTo(rColor
);
1311 OSL_FAIL("sd::CustomAnimationEffect::setColor(), exception caught!" );
1315 Any
CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType
, EValue eValue
)
1318 if( mxNode
.is() ) try
1320 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1321 if( xEnumerationAccess
.is() )
1323 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1324 if( xEnumeration
.is() )
1326 while( xEnumeration
->hasMoreElements() && !aProperty
.hasValue() )
1328 Reference
< XAnimateTransform
> xTransform( xEnumeration
->nextElement(), UNO_QUERY
);
1329 if( !xTransform
.is() )
1332 if( xTransform
->getTransformType() == nTransformType
)
1336 case EValue::To
: aProperty
= xTransform
->getTo(); break;
1337 case EValue::By
: aProperty
= xTransform
->getBy(); break;
1346 OSL_FAIL("sd::CustomAnimationEffect::getTransformationProperty(), exception caught!" );
1352 bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType
, EValue eValue
, const Any
& rValue
)
1354 bool bChanged
= false;
1355 if( mxNode
.is() ) try
1357 Reference
< XEnumerationAccess
> xEnumerationAccess( mxNode
, UNO_QUERY
);
1358 if( xEnumerationAccess
.is() )
1360 Reference
< XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1361 if( xEnumeration
.is() )
1363 while( xEnumeration
->hasMoreElements() )
1365 Reference
< XAnimateTransform
> xTransform( xEnumeration
->nextElement(), UNO_QUERY
);
1366 if( !xTransform
.is() )
1369 if( xTransform
->getTransformType() == nTransformType
)
1374 if( xTransform
->getTo() != rValue
)
1376 xTransform
->setTo( rValue
);
1381 if( xTransform
->getBy() != rValue
)
1383 xTransform
->setBy( rValue
);
1395 OSL_FAIL("sd::CustomAnimationEffect::setTransformationProperty(), exception caught!" );
1401 void CustomAnimationEffect::createAudio( const css::uno::Any
& rSource
)
1403 DBG_ASSERT( !mxAudio
.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" );
1410 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1411 Reference
< XAudio
> xAudio( Audio::create( xContext
) );
1412 xAudio
->setSource( rSource
);
1413 xAudio
->setVolume( 1.0 );
1418 OSL_FAIL("sd::CustomAnimationEffect::createAudio(), exception caught!" );
1422 static Reference
< XCommand
> findCommandNode( const Reference
< XAnimationNode
>& xRootNode
)
1424 Reference
< XCommand
> xCommand
;
1426 if( xRootNode
.is() ) try
1428 Reference
< XEnumerationAccess
> xEnumerationAccess( xRootNode
, UNO_QUERY_THROW
);
1429 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1430 while( !xCommand
.is() && xEnumeration
->hasMoreElements() )
1432 Reference
< XAnimationNode
> xNode( xEnumeration
->nextElement(), UNO_QUERY
);
1433 if( xNode
.is() && (xNode
->getType() == AnimationNodeType::COMMAND
) )
1434 xCommand
.set( xNode
, UNO_QUERY_THROW
);
1439 OSL_FAIL("sd::findCommandNode(), exception caught!" );
1445 void CustomAnimationEffect::removeAudio()
1449 Reference
< XAnimationNode
> xChild
;
1456 else if( mnCommand
== EffectCommands::STOPAUDIO
)
1458 xChild
= findCommandNode( mxNode
);
1464 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY
);
1465 if( xContainer
.is() )
1466 xContainer
->removeChild( xChild
);
1471 OSL_FAIL("sd::CustomAnimationEffect::removeAudio(), exception caught!" );
1476 void CustomAnimationEffect::setAudio( const Reference
< css::animations::XAudio
>& xAudio
)
1478 if( mxAudio
== xAudio
)
1485 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY
);
1486 if( xContainer
.is() && mxAudio
.is() )
1487 xContainer
->appendChild( mxAudio
);
1491 OSL_FAIL("sd::CustomAnimationEffect::setAudio(), exception caught!" );
1495 void CustomAnimationEffect::setStopAudio()
1497 if( mnCommand
== EffectCommands::STOPAUDIO
)
1505 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1506 Reference
< XCommand
> xCommand( Command::create( xContext
) );
1508 xCommand
->setCommand( EffectCommands::STOPAUDIO
);
1510 Reference
< XTimeContainer
> xContainer( mxNode
, UNO_QUERY_THROW
);
1511 xContainer
->appendChild( xCommand
);
1513 mnCommand
= EffectCommands::STOPAUDIO
;
1517 OSL_FAIL("sd::CustomAnimationEffect::setStopAudio(), exception caught!" );
1521 bool CustomAnimationEffect::getStopAudio() const
1523 return mnCommand
== EffectCommands::STOPAUDIO
;
1526 SdrPathObj
* CustomAnimationEffect::createSdrPathObjFromPath(SdrModel
& rTargetModel
)
1528 SdrPathObj
* pPathObj
= new SdrPathObj(rTargetModel
, OBJ_PATHLINE
);
1529 updateSdrPathObjFromPath( *pPathObj
);
1533 void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj
& rPathObj
)
1535 ::basegfx::B2DPolyPolygon aPolyPoly
;
1536 if( ::basegfx::utils::importFromSvgD( aPolyPoly
, getPath(), true, nullptr ) )
1538 SdrObject
* pObj
= GetSdrObjectFromXShape( getTargetShape() );
1541 SdrPage
* pPage
= pObj
->getSdrPageFromSdrObject();
1544 const Size
aPageSize( pPage
->GetSize() );
1545 aPolyPoly
.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast<double>(aPageSize
.Width()), static_cast<double>(aPageSize
.Height())));
1548 const ::tools::Rectangle
aBoundRect( pObj
->GetCurrentBoundRect() );
1549 const Point
aCenter( aBoundRect
.Center() );
1550 aPolyPoly
.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter
.X(), aCenter
.Y()));
1554 rPathObj
.SetPathPoly( aPolyPoly
);
1557 void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj
& rPathObj
)
1559 ::basegfx::B2DPolyPolygon
aPolyPoly( rPathObj
.GetPathPoly() );
1561 SdrObject
* pObj
= GetSdrObjectFromXShape( getTargetShape() );
1564 ::tools::Rectangle
aBoundRect(0,0,0,0);
1566 const drawinglayer::primitive2d::Primitive2DContainer
xPrimitives(pObj
->GetViewContact().getViewIndependentPrimitive2DContainer());
1567 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
1568 const basegfx::B2DRange
aRange(xPrimitives
.getB2DRange(aViewInformation2D
));
1570 if(!aRange
.isEmpty())
1572 aBoundRect
= ::tools::Rectangle(
1573 static_cast<sal_Int32
>(floor(aRange
.getMinX())), static_cast<sal_Int32
>(floor(aRange
.getMinY())),
1574 static_cast<sal_Int32
>(ceil(aRange
.getMaxX())), static_cast<sal_Int32
>(ceil(aRange
.getMaxY())));
1577 const Point
aCenter( aBoundRect
.Center() );
1579 aPolyPoly
.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter
.X(), -aCenter
.Y()));
1581 SdrPage
* pPage
= pObj
->getSdrPageFromSdrObject();
1584 const Size
aPageSize( pPage
->GetSize() );
1585 aPolyPoly
.transform(basegfx::utils::createScaleB2DHomMatrix(
1586 1.0 / static_cast<double>(aPageSize
.Width()), 1.0 / static_cast<double>(aPageSize
.Height())));
1590 setPath( ::basegfx::utils::exportToSvgD( aPolyPoly
, true, true, true) );
1593 EffectSequenceHelper::EffectSequenceHelper()
1594 : mnSequenceType( EffectNodeType::DEFAULT
)
1598 EffectSequenceHelper::EffectSequenceHelper( const css::uno::Reference
< css::animations::XTimeContainer
>& xSequenceRoot
)
1599 : mxSequenceRoot( xSequenceRoot
), mnSequenceType( EffectNodeType::DEFAULT
)
1601 Reference
< XAnimationNode
> xNode( mxSequenceRoot
, UNO_QUERY_THROW
);
1605 EffectSequenceHelper::~EffectSequenceHelper()
1610 void EffectSequenceHelper::reset()
1612 for( CustomAnimationEffectPtr
& pEffect
: maEffects
)
1614 pEffect
->setEffectSequence(nullptr);
1619 Reference
< XAnimationNode
> EffectSequenceHelper::getRootNode()
1621 return mxSequenceRoot
;
1624 void EffectSequenceHelper::append( const CustomAnimationEffectPtr
& pEffect
)
1626 pEffect
->setEffectSequence( this );
1627 maEffects
.push_back(pEffect
);
1631 CustomAnimationEffectPtr
EffectSequenceHelper::append( const CustomAnimationPresetPtr
& pPreset
, const Any
& rTarget
, double fDuration
/* = -1.0 */ )
1633 CustomAnimationEffectPtr pEffect
;
1637 Reference
< XAnimationNode
> xNode( pPreset
->create( "" ) );
1640 // first, filter all only ui relevant user data
1641 std::vector
< NamedValue
> aNewUserData
;
1642 Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
1644 std::copy_if(aUserData
.begin(), aUserData
.end(), std::back_inserter(aNewUserData
),
1645 [](const NamedValue
& rProp
) { return rProp
.Name
!= "text-only" && rProp
.Name
!= "preset-property"; });
1647 if( !aNewUserData
.empty() )
1649 aUserData
= ::comphelper::containerToSequence( aNewUserData
);
1650 xNode
->setUserData( aUserData
);
1653 // check target, maybe we need to force it to text
1654 sal_Int16 nSubItem
= ShapeAnimationSubType::AS_WHOLE
;
1656 if( rTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
1658 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1660 else if( pPreset
->isTextOnly() )
1662 Reference
< XShape
> xShape
;
1666 // that's bad, we target a shape here but the effect is only for text
1667 // so change subitem
1668 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1672 // now create effect from preset
1673 pEffect
.reset( new CustomAnimationEffect( xNode
) );
1674 pEffect
->setEffectSequence( this );
1675 pEffect
->setTarget( rTarget
);
1676 pEffect
->setTargetSubItem( nSubItem
);
1677 if( fDuration
!= -1.0 )
1678 pEffect
->setDuration( fDuration
);
1680 maEffects
.push_back(pEffect
);
1686 DBG_ASSERT( pEffect
.get(), "sd::EffectSequenceHelper::append(), failed!" );
1690 CustomAnimationEffectPtr
EffectSequenceHelper::append( const SdrPathObj
& rPathObj
, const Any
& rTarget
, double fDuration
/* = -1.0 */ )
1692 CustomAnimationEffectPtr pEffect
;
1694 if( fDuration
<= 0.0 )
1699 Reference
< XTimeContainer
> xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1700 Reference
< XAnimationNode
> xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) );
1702 xAnimateMotion
->setDuration( Any( fDuration
) );
1703 xAnimateMotion
->setFill( AnimationFill::HOLD
);
1704 xEffectContainer
->appendChild( xAnimateMotion
);
1706 sal_Int16 nSubItem
= ShapeAnimationSubType::AS_WHOLE
;
1708 if( rTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
1709 nSubItem
= ShapeAnimationSubType::ONLY_TEXT
;
1711 pEffect
.reset( new CustomAnimationEffect( xEffectContainer
) );
1712 pEffect
->setEffectSequence( this );
1713 pEffect
->setTarget( rTarget
);
1714 pEffect
->setTargetSubItem( nSubItem
);
1715 pEffect
->setNodeType( css::presentation::EffectNodeType::ON_CLICK
);
1716 pEffect
->setPresetClass( css::presentation::EffectPresetClass::MOTIONPATH
);
1717 pEffect
->setAcceleration( 0.5 );
1718 pEffect
->setDecelerate( 0.5 );
1719 pEffect
->setFill( AnimationFill::HOLD
);
1720 pEffect
->setBegin( 0.0 );
1721 pEffect
->updatePathFromSdrPathObj( rPathObj
);
1722 if( fDuration
!= -1.0 )
1723 pEffect
->setDuration( fDuration
);
1725 maEffects
.push_back(pEffect
);
1731 OSL_FAIL( "sd::EffectSequenceHelper::append(), exception caught!" );
1737 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationPresetPtr
& pPreset
, const OUString
& rPresetSubType
, double fDuration
/* = -1.0 */ )
1739 if( !(pEffect
.get() && pPreset
.get()) )
1744 Reference
< XAnimationNode
> xNewNode( pPreset
->create( rPresetSubType
) );
1747 pEffect
->replaceNode( xNewNode
);
1748 if( fDuration
!= -1.0 )
1749 pEffect
->setDuration( fDuration
);
1756 OSL_FAIL( "sd::EffectSequenceHelper::replace(), exception caught!" );
1760 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationPresetPtr
& pPreset
, double fDuration
/* = -1.0 */ )
1762 replace( pEffect
, pPreset
, "", fDuration
);
1765 void EffectSequenceHelper::remove( const CustomAnimationEffectPtr
& pEffect
)
1769 pEffect
->setEffectSequence( nullptr );
1770 maEffects
.remove( pEffect
);
1776 void EffectSequenceHelper::moveToBeforeEffect( const CustomAnimationEffectPtr
& pEffect
, const CustomAnimationEffectPtr
& pInsertBefore
)
1778 if ( pEffect
.get() )
1780 maEffects
.remove( pEffect
);
1781 EffectSequence::iterator
aInsertIter( find( pInsertBefore
) );
1783 // aInsertIter being end() is OK: pInsertBefore could be null, so put at end.
1784 maEffects
.insert( aInsertIter
, pEffect
);
1790 void EffectSequenceHelper::rebuild()
1795 void EffectSequenceHelper::implRebuild()
1799 // first we delete all time containers on the first two levels
1800 Reference
< XEnumerationAccess
> xEnumerationAccess( mxSequenceRoot
, UNO_QUERY_THROW
);
1801 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1802 while( xEnumeration
->hasMoreElements() )
1804 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
1805 Reference
< XTimeContainer
> xChildContainer( xChildNode
, UNO_QUERY_THROW
);
1807 Reference
< XEnumerationAccess
> xChildEnumerationAccess( xChildNode
, UNO_QUERY_THROW
);
1808 Reference
< XEnumeration
> xChildEnumeration( xChildEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1809 while( xChildEnumeration
->hasMoreElements() )
1811 Reference
< XAnimationNode
> xNode( xChildEnumeration
->nextElement(), UNO_QUERY_THROW
);
1812 xChildContainer
->removeChild( xNode
);
1815 mxSequenceRoot
->removeChild( xChildNode
);
1818 // second, rebuild main sequence
1819 EffectSequence::iterator
aIter( maEffects
.begin() );
1820 EffectSequence::iterator
aEnd( maEffects
.end() );
1823 std::vector
< sd::AfterEffectNode
> aAfterEffects
;
1825 CustomAnimationEffectPtr pEffect
= *aIter
++;
1830 // create a par container for the next click node and all following with and after effects
1831 Reference
< XTimeContainer
> xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1834 if( mxEventSource
.is() )
1836 aEvent
.Source
<<= mxEventSource
;
1837 aEvent
.Trigger
= EventTrigger::ON_CLICK
;
1841 aEvent
.Trigger
= EventTrigger::ON_NEXT
;
1845 Any
aBegin( makeAny( aEvent
) );
1848 // if the first node is not a click action, this click container
1849 // must not have INDEFINITE begin but start at 0s
1851 if( pEffect
->getNodeType() != EffectNodeType::ON_CLICK
)
1855 xOnClickContainer
->setBegin( aBegin
);
1857 mxSequenceRoot
->appendChild( xOnClickContainer
);
1859 double fBegin
= 0.0;
1863 // create a par container for the current click or after effect node and all following with effects
1864 Reference
< XTimeContainer
> xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW
);
1865 xWithContainer
->setBegin( makeAny( fBegin
) );
1866 xOnClickContainer
->appendChild( xWithContainer
);
1868 double fDuration
= 0.0;
1871 Reference
< XAnimationNode
> xEffectNode( pEffect
->getNode() );
1872 xWithContainer
->appendChild( xEffectNode
);
1874 if( pEffect
->hasAfterEffect() )
1876 Reference
< XAnimationNode
> xAfterEffect( pEffect
->createAfterEffectNode() );
1877 AfterEffectNode
a( xAfterEffect
, xEffectNode
, pEffect
->IsAfterEffectOnNext() );
1878 aAfterEffects
.push_back( a
);
1881 double fTemp
= pEffect
->getBegin() + pEffect
->getAbsoluteDuration();
1882 if( fTemp
> fDuration
)
1890 while( pEffect
.get() && (pEffect
->getNodeType() == EffectNodeType::WITH_PREVIOUS
) );
1892 fBegin
+= fDuration
;
1894 while( pEffect
.get() && (pEffect
->getNodeType() != EffectNodeType::ON_CLICK
) );
1896 while( pEffect
.get() );
1898 // process after effect nodes
1899 std::for_each( aAfterEffects
.begin(), aAfterEffects
.end(), stl_process_after_effect_node_func
);
1903 // reset duration, might have been altered (see below)
1904 mxSequenceRoot
->setDuration( Any() );
1908 // empty sequence, set duration to 0.0 explicitly
1909 // (otherwise, this sequence will never end)
1910 mxSequenceRoot
->setDuration( makeAny(0.0) );
1915 OSL_FAIL( "sd::EffectSequenceHelper::rebuild(), exception caught!" );
1919 stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference
< css::animations::XAnimationNode
>& xSearchNode
)
1920 : mxSearchNode( xSearchNode
)
1924 bool stl_CustomAnimationEffect_search_node_predict::operator()( const CustomAnimationEffectPtr
& pEffect
) const
1926 return pEffect
->getNode() == mxSearchNode
;
1929 /// @throws Exception
1930 static bool implFindNextContainer( Reference
< XTimeContainer
> const & xParent
, Reference
< XTimeContainer
> const & xCurrent
, Reference
< XTimeContainer
>& xNext
)
1932 Reference
< XEnumerationAccess
> xEnumerationAccess( xParent
, UNO_QUERY_THROW
);
1933 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration() );
1934 if( xEnumeration
.is() )
1936 Reference
< XInterface
> x
;
1937 while( xEnumeration
->hasMoreElements() && !xNext
.is() )
1939 if( (xEnumeration
->nextElement() >>= x
) && (x
== xCurrent
) )
1941 if( xEnumeration
->hasMoreElements() )
1942 xEnumeration
->nextElement() >>= xNext
;
1949 void stl_process_after_effect_node_func(AfterEffectNode
const & rNode
)
1953 if( rNode
.mxNode
.is() && rNode
.mxMaster
.is() )
1956 Reference
< XAnimationNode
> xMasterNode( rNode
.mxMaster
, UNO_SET_THROW
);
1957 Sequence
< NamedValue
> aUserData( rNode
.mxNode
->getUserData() );
1958 sal_Int32 nSize
= aUserData
.getLength();
1959 aUserData
.realloc(nSize
+1);
1960 aUserData
[nSize
].Name
= "master-element";
1961 aUserData
[nSize
].Value
<<= xMasterNode
;
1962 rNode
.mxNode
->setUserData( aUserData
);
1964 // insert after effect node into timeline
1965 Reference
< XTimeContainer
> xContainer( rNode
.mxMaster
->getParent(), UNO_QUERY_THROW
);
1967 if( !rNode
.mbOnNextEffect
) // sameClick
1969 // insert the aftereffect after its effect is animated
1970 xContainer
->insertAfter( rNode
.mxNode
, rNode
.mxMaster
);
1974 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1975 // insert the aftereffect in the next group
1977 Reference
< XTimeContainer
> xClickContainer( xContainer
->getParent(), UNO_QUERY_THROW
);
1978 Reference
< XTimeContainer
> xSequenceContainer( xClickContainer
->getParent(), UNO_QUERY_THROW
);
1980 Reference
< XTimeContainer
> xNextContainer
;
1982 // first try if we have an after effect container
1983 if( !implFindNextContainer( xClickContainer
, xContainer
, xNextContainer
) )
1985 Reference
< XTimeContainer
> xNextClickContainer
;
1986 // if not, try to find the next click effect container
1987 if( implFindNextContainer( xSequenceContainer
, xClickContainer
, xNextClickContainer
) )
1989 Reference
< XEnumerationAccess
> xEnumerationAccess( xNextClickContainer
, UNO_QUERY_THROW
);
1990 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
1991 if( xEnumeration
->hasMoreElements() )
1993 // the next container is the first child container
1994 xEnumeration
->nextElement() >>= xNextContainer
;
1998 // this does not yet have a child container, create one
1999 xNextContainer
.set( ParallelTimeContainer::create(xContext
), UNO_QUERY_THROW
);
2001 xNextContainer
->setBegin( makeAny( 0.0 ) );
2002 xNextClickContainer
->appendChild( xNextContainer
);
2004 DBG_ASSERT( xNextContainer
.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" );
2008 // if we don't have a next container, we add one to the sequence container
2009 if( !xNextContainer
.is() )
2011 Reference
< XTimeContainer
> xNewClickContainer( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
2014 aEvent
.Trigger
= EventTrigger::ON_NEXT
;
2016 xNewClickContainer
->setBegin( makeAny( aEvent
) );
2018 xSequenceContainer
->insertAfter( xNewClickContainer
, xClickContainer
);
2020 xNextContainer
.set( ParallelTimeContainer::create( xContext
), UNO_QUERY_THROW
);
2022 xNextContainer
->setBegin( makeAny( 0.0 ) );
2023 xNewClickContainer
->appendChild( xNextContainer
);
2026 if( xNextContainer
.is() )
2028 // find begin time of first element
2029 Reference
< XEnumerationAccess
> xEnumerationAccess( xNextContainer
, UNO_QUERY_THROW
);
2030 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2031 if( xEnumeration
->hasMoreElements() )
2033 Reference
< XAnimationNode
> xChild
;
2034 // the next container is the first child container
2035 xEnumeration
->nextElement() >>= xChild
;
2038 Any
aBegin( xChild
->getBegin() );
2039 double fBegin
= 0.0;
2040 if( (aBegin
>>= fBegin
) && (fBegin
>= 0.0))
2041 rNode
.mxNode
->setBegin( aBegin
);
2045 xNextContainer
->appendChild( rNode
.mxNode
);
2052 OSL_FAIL( "ppt::stl_process_after_effect_node_func::operator(), exception caught!" );
2056 EffectSequence::iterator
EffectSequenceHelper::find( const CustomAnimationEffectPtr
& pEffect
)
2058 return std::find( maEffects
.begin(), maEffects
.end(), pEffect
);
2061 CustomAnimationEffectPtr
EffectSequenceHelper::findEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
) const
2063 CustomAnimationEffectPtr pEffect
;
2065 EffectSequence::const_iterator aIter
= std::find_if(maEffects
.begin(), maEffects
.end(),
2066 [&xNode
](const CustomAnimationEffectPtr
& rxEffect
) { return rxEffect
->getNode() == xNode
; });
2067 if (aIter
!= maEffects
.end())
2073 sal_Int32
EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr
& xEffect
) const
2075 auto aIter
= std::find(maEffects
.begin(), maEffects
.end(), xEffect
);
2076 if (aIter
!= maEffects
.end())
2077 return static_cast<sal_Int32
>(std::distance(maEffects
.begin(), aIter
));
2082 CustomAnimationEffectPtr
EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset
) const
2084 EffectSequence::const_iterator
aIter( maEffects
.begin() );
2085 nOffset
= std::min(nOffset
, static_cast<sal_Int32
>(maEffects
.size()));
2086 std::advance(aIter
, nOffset
);
2088 CustomAnimationEffectPtr pEffect
;
2089 if( aIter
!= maEffects
.end() )
2095 bool EffectSequenceHelper::disposeShape( const Reference
< XShape
>& xShape
)
2097 bool bChanges
= false;
2099 EffectSequence::iterator
aIter( maEffects
.begin() );
2100 while( aIter
!= maEffects
.end() )
2102 if( (*aIter
)->getTargetShape() == xShape
)
2104 (*aIter
)->setEffectSequence( nullptr );
2106 aIter
= maEffects
.erase( aIter
);
2117 bool EffectSequenceHelper::hasEffect( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
2119 return std::any_of(maEffects
.begin(), maEffects
.end(),
2120 [&xShape
](const CustomAnimationEffectPtr
& rxEffect
) { return rxEffect
->getTargetShape() == xShape
; });
2123 void EffectSequenceHelper::insertTextRange( const css::uno::Any
& aTarget
)
2125 ParagraphTarget aParaTarget
;
2126 if( !(aTarget
>>= aParaTarget
) )
2129 bool bChanges
= std::accumulate(maEffects
.begin(), maEffects
.end(), false,
2130 [&aParaTarget
](const bool bCheck
, const CustomAnimationEffectPtr
& rxEffect
) {
2132 if (rxEffect
->getTargetShape() == aParaTarget
.Shape
)
2133 bRes
|= rxEffect
->checkForText();
2141 static bool isParagraphTargetTextEmpty( ParagraphTarget aParaTarget
)
2144 Reference
< XText
> xText ( aParaTarget
.Shape
, UNO_QUERY
);
2147 Reference
< XEnumerationAccess
> xEA( xText
, UNO_QUERY
);
2150 Reference
< XEnumeration
> xEnumeration
= xEA
->createEnumeration();
2151 if( xEnumeration
.is() )
2153 // advance to the Nth paragraph
2154 sal_Int32 nPara
= aParaTarget
.Paragraph
;
2155 while( xEnumeration
->hasMoreElements() && nPara
-- )
2156 xEnumeration
->nextElement();
2158 // get Nth paragraph's text and check if it's empty
2159 if( xEnumeration
->hasMoreElements() )
2161 Reference
< XTextRange
> xRange( xEnumeration
->nextElement(), UNO_QUERY
);
2164 OUString text
= xRange
->getString();
2165 return text
.isEmpty();
2174 void EffectSequenceHelper::disposeTextRange( const css::uno::Any
& aTarget
)
2176 ParagraphTarget aParaTarget
;
2177 if( !(aTarget
>>= aParaTarget
) )
2180 bool bChanges
= false;
2182 // building list of effects for target shape; process effects not on target shape
2183 EffectSequence aTargetParagraphEffects
;
2184 for( const auto &pEffect
: maEffects
)
2186 Any
aIterTarget( pEffect
->getTarget() );
2187 if( aIterTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2189 ParagraphTarget aIterParaTarget
;
2190 if( (aIterTarget
>>= aIterParaTarget
) && (aIterParaTarget
.Shape
== aParaTarget
.Shape
) )
2192 aTargetParagraphEffects
.push_back(pEffect
);
2195 else if( pEffect
->getTargetShape() == aParaTarget
.Shape
)
2197 bChanges
|= pEffect
->checkForText();
2201 // select effect to delete:
2202 // if paragraph before target is blank, then delete its animation effect (if any) instead
2203 ParagraphTarget aPreviousParagraph
= aParaTarget
;
2204 --aPreviousParagraph
.Paragraph
;
2205 bool bIsPreviousParagraphEmpty
= isParagraphTargetTextEmpty( aPreviousParagraph
);
2206 sal_Int16 anParaNumToDelete
= bIsPreviousParagraphEmpty
? aPreviousParagraph
.Paragraph
: aParaTarget
.Paragraph
;
2209 for( const auto &pEffect
: aTargetParagraphEffects
)
2211 Any
aIterTarget( pEffect
->getTarget() );
2213 ParagraphTarget aIterParaTarget
;
2214 aIterTarget
>>= aIterParaTarget
;
2216 // delete effect for target paragraph (may have effects in more than one text group)
2217 if( aIterParaTarget
.Paragraph
== anParaNumToDelete
)
2219 auto aItr
= find( pEffect
);
2220 DBG_ASSERT( aItr
!= maEffects
.end(), "sd::EffectSequenceHelper::disposeTextRange(), Expected effect missing.");
2221 if( aItr
!= maEffects
.end() )
2223 (*aItr
)->setEffectSequence( nullptr );
2224 maEffects
.erase(aItr
);
2229 // shift all paragraphs after disposed paragraph
2230 if( aIterParaTarget
.Paragraph
> anParaNumToDelete
)
2232 --aIterParaTarget
.Paragraph
;
2233 pEffect
->setTarget( makeAny( aIterParaTarget
) );
2244 CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference
< XShape
>& rTarget
, sal_Int32 nGroupId
)
2245 : maTarget( rTarget
),
2246 mnGroupId( nGroupId
)
2251 void CustomAnimationTextGroup::reset()
2253 mnTextGrouping
= -1;
2254 mbAnimateForm
= false;
2255 mbTextReverse
= false;
2256 mfGroupingAuto
= -1.0;
2257 mnLastPara
= -1; // used to check for TextReverse
2259 for (sal_Int8
& rn
: mnDepthFlags
)
2267 void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr
const & pEffect
)
2269 maEffects
.push_back( pEffect
);
2271 Any
aTarget( pEffect
->getTarget() );
2272 if( aTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2274 // now look at the paragraph
2275 ParagraphTarget aParaTarget
;
2276 aTarget
>>= aParaTarget
;
2278 if( mnLastPara
!= -1 )
2279 mbTextReverse
= mnLastPara
> aParaTarget
.Paragraph
;
2281 mnLastPara
= aParaTarget
.Paragraph
;
2283 const sal_Int32 nParaDepth
= pEffect
->getParaDepth();
2285 // only look at the first PARA_LEVELS levels
2286 if( nParaDepth
< PARA_LEVELS
)
2288 // our first paragraph with this level?
2289 if( mnDepthFlags
[nParaDepth
] == 0 )
2291 // so set it to the first found
2292 mnDepthFlags
[nParaDepth
] = static_cast<sal_Int8
>(pEffect
->getNodeType());
2294 else if( mnDepthFlags
[nParaDepth
] != pEffect
->getNodeType() )
2296 mnDepthFlags
[nParaDepth
] = -1;
2299 if( pEffect
->getNodeType() == EffectNodeType::AFTER_PREVIOUS
)
2300 mfGroupingAuto
= pEffect
->getBegin();
2302 mnTextGrouping
= PARA_LEVELS
;
2303 while( (mnTextGrouping
> 0)
2304 && (mnDepthFlags
[mnTextGrouping
- 1] <= 0) )
2310 // if we have an effect with the shape as a target, we animate the background
2311 mbAnimateForm
= pEffect
->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT
;
2315 CustomAnimationTextGroupPtr
EffectSequenceHelper::findGroup( sal_Int32 nGroupId
)
2317 CustomAnimationTextGroupPtr aPtr
;
2319 CustomAnimationTextGroupMap::iterator
aIter( maGroupMap
.find( nGroupId
) );
2320 if( aIter
!= maGroupMap
.end() )
2321 aPtr
= (*aIter
).second
;
2326 void EffectSequenceHelper::updateTextGroups()
2330 // first create all the groups
2331 for( CustomAnimationEffectPtr
& pEffect
: maEffects
)
2333 const sal_Int32 nGroupId
= pEffect
->getGroupId();
2335 if( nGroupId
== -1 )
2336 continue; // trivial case, no group
2338 CustomAnimationTextGroupPtr pGroup
= findGroup( nGroupId
);
2341 pGroup
.reset( new CustomAnimationTextGroup( pEffect
->getTargetShape(), nGroupId
) );
2342 maGroupMap
[nGroupId
] = pGroup
;
2345 pGroup
->addEffect( pEffect
);
2348 // Now that all the text groups have been cleared up and rebuilt, we need to update its
2349 // text grouping. addEffect() already make mnTextGrouping the last possible level,
2350 // so just continue to find the last level that is not EffectNodeType::WITH_PREVIOUS.
2351 for(const auto &rGroupMapItem
: maGroupMap
)
2353 const CustomAnimationTextGroupPtr
&pGroup
= rGroupMapItem
.second
;
2354 while(pGroup
->mnTextGrouping
> 0 && pGroup
->mnDepthFlags
[pGroup
->mnTextGrouping
- 1] == EffectNodeType::WITH_PREVIOUS
)
2355 --pGroup
->mnTextGrouping
;
2359 CustomAnimationTextGroupPtr
2360 EffectSequenceHelper::createTextGroup(const CustomAnimationEffectPtr
& pEffect
,
2361 sal_Int32 nTextGrouping
, double fTextGroupingAuto
,
2362 bool bAnimateForm
, bool bTextReverse
)
2364 // first find a free group-id
2365 sal_Int32 nGroupId
= 0;
2367 CustomAnimationTextGroupMap::iterator
aIter( maGroupMap
.begin() );
2368 const CustomAnimationTextGroupMap::iterator
aEnd( maGroupMap
.end() );
2369 while( aIter
!= aEnd
)
2371 if( (*aIter
).first
== nGroupId
)
2374 aIter
= maGroupMap
.begin();
2382 Reference
< XShape
> xTarget( pEffect
->getTargetShape() );
2384 CustomAnimationTextGroupPtr
pTextGroup( new CustomAnimationTextGroup( xTarget
, nGroupId
) );
2385 maGroupMap
[nGroupId
] = pTextGroup
;
2389 // do we need to target the shape?
2390 if( (nTextGrouping
== 0) || bAnimateForm
)
2393 if( nTextGrouping
== 0)
2394 nSubItem
= bAnimateForm
? ShapeAnimationSubType::AS_WHOLE
: ShapeAnimationSubType::ONLY_TEXT
;
2396 nSubItem
= ShapeAnimationSubType::ONLY_BACKGROUND
;
2398 pEffect
->setTarget( makeAny( xTarget
) );
2399 pEffect
->setTargetSubItem( nSubItem
);
2400 pEffect
->setEffectSequence( this );
2401 pEffect
->setGroupId( nGroupId
);
2403 pTextGroup
->addEffect( pEffect
);
2407 pTextGroup
->mnTextGrouping
= nTextGrouping
;
2408 pTextGroup
->mfGroupingAuto
= fTextGroupingAuto
;
2409 pTextGroup
->mbTextReverse
= bTextReverse
;
2411 // now add an effect for each paragraph
2412 createTextGroupParagraphEffects( pTextGroup
, pEffect
, bUsed
);
2419 void EffectSequenceHelper::createTextGroupParagraphEffects( const CustomAnimationTextGroupPtr
& pTextGroup
, const CustomAnimationEffectPtr
& pEffect
, bool bUsed
)
2421 Reference
< XShape
> xTarget( pTextGroup
->maTarget
);
2423 sal_Int32 nTextGrouping
= pTextGroup
->mnTextGrouping
;
2424 double fTextGroupingAuto
= pTextGroup
->mfGroupingAuto
;
2425 bool bTextReverse
= pTextGroup
->mbTextReverse
;
2427 // now add an effect for each paragraph
2428 if( nTextGrouping
< 0 )
2433 EffectSequence::iterator
aInsertIter( find( pEffect
) );
2435 Reference
< XEnumerationAccess
> xText( xTarget
, UNO_QUERY_THROW
);
2436 Reference
< XEnumeration
> xEnumeration( xText
->createEnumeration(), UNO_SET_THROW
);
2438 std::deque
< sal_Int16
> aParaList
;
2441 // fill the list with all valid paragraphs
2442 for( nPara
= 0; xEnumeration
->hasMoreElements(); nPara
++ )
2444 Reference
< XTextRange
> xRange( xEnumeration
->nextElement(), UNO_QUERY
);
2445 if( xRange
.is() && !xRange
->getString().isEmpty() )
2447 if( bTextReverse
) // sort them
2448 aParaList
.push_front( nPara
);
2450 aParaList
.push_back( nPara
);
2454 ParagraphTarget aTarget
;
2455 aTarget
.Shape
= xTarget
;
2457 for( const auto i
: aParaList
)
2459 aTarget
.Paragraph
= i
;
2461 CustomAnimationEffectPtr pNewEffect
;
2464 // clone a new effect from first effect
2465 pNewEffect
= pEffect
->clone();
2467 aInsertIter
= maEffects
.insert( aInsertIter
, pNewEffect
);
2471 // reuse first effect if it's not yet used
2472 pNewEffect
= pEffect
;
2474 aInsertIter
= find( pNewEffect
);
2477 // set target and group-id
2478 pNewEffect
->setTarget( makeAny( aTarget
) );
2479 pNewEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT
);
2480 pNewEffect
->setGroupId( pTextGroup
->mnGroupId
);
2481 pNewEffect
->setEffectSequence( this );
2483 // set correct node type
2484 if( pNewEffect
->getParaDepth() < nTextGrouping
)
2486 if( fTextGroupingAuto
== -1.0 )
2488 pNewEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2489 pNewEffect
->setBegin( 0.0 );
2493 pNewEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2494 pNewEffect
->setBegin( fTextGroupingAuto
);
2499 pNewEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2500 pNewEffect
->setBegin( 0.0 );
2503 pTextGroup
->addEffect( pNewEffect
);
2509 OSL_FAIL("sd::EffectSequenceHelper::createTextGroup(), exception caught!" );
2513 void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr
& pTextGroup
, sal_Int32 nTextGrouping
)
2515 if( pTextGroup
->mnTextGrouping
== nTextGrouping
)
2517 // first case, trivial case, do nothing
2519 else if( (pTextGroup
->mnTextGrouping
== -1) && (nTextGrouping
>= 0) )
2521 // second case, we need to add new effects for each paragraph
2523 CustomAnimationEffectPtr
pEffect( pTextGroup
->maEffects
.front() );
2525 pTextGroup
->mnTextGrouping
= nTextGrouping
;
2526 createTextGroupParagraphEffects( pTextGroup
, pEffect
, true );
2529 else if( (pTextGroup
->mnTextGrouping
>= 0) && (nTextGrouping
== -1 ) )
2531 // third case, we need to remove effects for each paragraph
2533 EffectSequence
aEffects( pTextGroup
->maEffects
);
2534 pTextGroup
->reset();
2536 for( CustomAnimationEffectPtr
& pEffect
: aEffects
)
2538 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2541 pTextGroup
->addEffect( pEffect
);
2547 // fourth case, we need to change the node types for the text nodes
2548 double fTextGroupingAuto
= pTextGroup
->mfGroupingAuto
;
2550 EffectSequence
aEffects( pTextGroup
->maEffects
);
2551 pTextGroup
->reset();
2553 for( CustomAnimationEffectPtr
& pEffect
: aEffects
)
2555 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2557 // set correct node type
2558 if( pEffect
->getParaDepth() < nTextGrouping
)
2560 if( fTextGroupingAuto
== -1.0 )
2562 pEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2563 pEffect
->setBegin( 0.0 );
2567 pEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2568 pEffect
->setBegin( fTextGroupingAuto
);
2573 pEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2574 pEffect
->setBegin( 0.0 );
2578 pTextGroup
->addEffect( pEffect
);
2585 void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr
& pTextGroup
, bool bAnimateForm
)
2587 if( pTextGroup
->mbAnimateForm
== bAnimateForm
)
2589 // trivial case, do nothing
2593 EffectSequence
aEffects( pTextGroup
->maEffects
);
2594 pTextGroup
->reset();
2596 SAL_WARN_IF(aEffects
.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" );
2598 if (aEffects
.empty())
2601 EffectSequence::iterator
aIter( aEffects
.begin() );
2602 const EffectSequence::iterator
aEnd( aEffects
.end() );
2604 // first insert if we have to
2607 EffectSequence::iterator
aInsertIter( find( *aIter
) );
2609 CustomAnimationEffectPtr pEffect
;
2610 if( (aEffects
.size() == 1) && ((*aIter
)->getTarget().getValueType() != ::cppu::UnoType
<ParagraphTarget
>::get() ) )
2612 // special case, only one effect and that targets whole text,
2613 // convert this to target whole shape
2615 pEffect
->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE
);
2619 pEffect
= (*aIter
)->clone();
2620 pEffect
->setTarget( makeAny( (*aIter
)->getTargetShape() ) );
2621 pEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND
);
2622 maEffects
.insert( aInsertIter
, pEffect
);
2625 pTextGroup
->addEffect( pEffect
);
2628 if( !bAnimateForm
&& (aEffects
.size() == 1) )
2630 CustomAnimationEffectPtr
pEffect( *aIter
);
2631 pEffect
->setTarget( makeAny( (*aIter
)->getTargetShape() ) );
2632 pEffect
->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT
);
2633 pTextGroup
->addEffect( pEffect
);
2637 // read the rest to the group again
2638 while( aIter
!= aEnd
)
2640 CustomAnimationEffectPtr
pEffect( *aIter
++ );
2642 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2644 pTextGroup
->addEffect( pEffect
);
2648 DBG_ASSERT( !bAnimateForm
, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" );
2657 void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr
& pTextGroup
, double fTextGroupingAuto
)
2659 sal_Int32 nTextGrouping
= pTextGroup
->mnTextGrouping
;
2661 EffectSequence
aEffects( pTextGroup
->maEffects
);
2662 pTextGroup
->reset();
2664 for( CustomAnimationEffectPtr
& pEffect
: aEffects
)
2666 if( pEffect
->getTarget().getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2668 // set correct node type
2669 if( pEffect
->getParaDepth() < nTextGrouping
)
2671 if( fTextGroupingAuto
== -1.0 )
2673 pEffect
->setNodeType( EffectNodeType::ON_CLICK
);
2674 pEffect
->setBegin( 0.0 );
2678 pEffect
->setNodeType( EffectNodeType::AFTER_PREVIOUS
);
2679 pEffect
->setBegin( fTextGroupingAuto
);
2684 pEffect
->setNodeType( EffectNodeType::WITH_PREVIOUS
);
2685 pEffect
->setBegin( 0.0 );
2689 pTextGroup
->addEffect( pEffect
);
2695 struct ImplStlTextGroupSortHelper
2697 explicit ImplStlTextGroupSortHelper( bool bReverse
) : mbReverse( bReverse
) {};
2698 bool operator()( const CustomAnimationEffectPtr
& p1
, const CustomAnimationEffectPtr
& p2
);
2699 bool const mbReverse
;
2700 sal_Int32
getTargetParagraph( const CustomAnimationEffectPtr
& p1
);
2703 sal_Int32
ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr
& p1
)
2705 const Any
aTarget(p1
->getTarget());
2706 if( aTarget
.hasValue() && aTarget
.getValueType() == ::cppu::UnoType
<ParagraphTarget
>::get() )
2708 ParagraphTarget aParaTarget
;
2709 aTarget
>>= aParaTarget
;
2710 return aParaTarget
.Paragraph
;
2714 return mbReverse
? 0x7fffffff : -1;
2718 bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr
& p1
, const CustomAnimationEffectPtr
& p2
)
2722 return getTargetParagraph( p2
) < getTargetParagraph( p1
);
2726 return getTargetParagraph( p1
) < getTargetParagraph( p2
);
2730 void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr
& pTextGroup
, bool bTextReverse
)
2732 if( pTextGroup
->mbTextReverse
== bTextReverse
)
2738 std::vector
< CustomAnimationEffectPtr
> aSortedVector(pTextGroup
->maEffects
.size());
2739 std::copy( pTextGroup
->maEffects
.begin(), pTextGroup
->maEffects
.end(), aSortedVector
.begin() );
2740 ImplStlTextGroupSortHelper
aSortHelper( bTextReverse
);
2741 std::sort( aSortedVector
.begin(), aSortedVector
.end(), aSortHelper
);
2743 pTextGroup
->reset();
2745 std::vector
< CustomAnimationEffectPtr
>::iterator
aIter( aSortedVector
.begin() );
2746 const std::vector
< CustomAnimationEffectPtr
>::iterator
aEnd( aSortedVector
.end() );
2750 pTextGroup
->addEffect( *aIter
);
2751 EffectSequence::iterator
aInsertIter( find( *aIter
++ ) );
2752 while( aIter
!= aEnd
)
2754 CustomAnimationEffectPtr
pEffect( *aIter
++ );
2755 maEffects
.erase( find( pEffect
) );
2756 aInsertIter
= maEffects
.insert( ++aInsertIter
, pEffect
);
2757 pTextGroup
->addEffect( pEffect
);
2764 void EffectSequenceHelper::addListener( ISequenceListener
* pListener
)
2766 if( std::find( maListeners
.begin(), maListeners
.end(), pListener
) == maListeners
.end() )
2767 maListeners
.push_back( pListener
);
2770 void EffectSequenceHelper::removeListener( ISequenceListener
* pListener
)
2772 maListeners
.remove( pListener
);
2775 struct stl_notify_listeners_func
2777 stl_notify_listeners_func() {}
2778 void operator()(ISequenceListener
* pListener
) { pListener
->notify_change(); }
2781 void EffectSequenceHelper::notify_listeners()
2783 stl_notify_listeners_func aFunc
;
2784 std::for_each( maListeners
.begin(), maListeners
.end(), aFunc
);
2787 void EffectSequenceHelper::create( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
2789 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::create(), illegal argument" );
2796 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2797 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2798 while( xEnumeration
->hasMoreElements() )
2800 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2801 createEffectsequence( xChildNode
);
2806 OSL_FAIL( "sd::EffectSequenceHelper::create(), exception caught!" );
2810 void EffectSequenceHelper::createEffectsequence( const Reference
< XAnimationNode
>& xNode
)
2812 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" );
2819 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2820 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2821 while( xEnumeration
->hasMoreElements() )
2823 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2825 createEffects( xChildNode
);
2830 OSL_FAIL( "sd::EffectSequenceHelper::createEffectsequence(), exception caught!" );
2834 void EffectSequenceHelper::createEffects( const Reference
< XAnimationNode
>& xNode
)
2836 DBG_ASSERT( xNode
.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" );
2843 Reference
< XEnumerationAccess
> xEnumerationAccess( xNode
, UNO_QUERY_THROW
);
2844 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
2845 while( xEnumeration
->hasMoreElements() )
2847 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
2849 switch( xChildNode
->getType() )
2852 case AnimationNodeType::PAR
:
2853 case AnimationNodeType::ITERATE
:
2855 CustomAnimationEffectPtr
pEffect( new CustomAnimationEffect( xChildNode
) );
2857 if( pEffect
->mnNodeType
!= -1 )
2859 pEffect
->setEffectSequence( this );
2860 maEffects
.push_back(pEffect
);
2865 // found an after effect
2866 case AnimationNodeType::SET
:
2867 case AnimationNodeType::ANIMATECOLOR
:
2869 processAfterEffect( xChildNode
);
2877 OSL_FAIL( "sd::EffectSequenceHelper::createEffects(), exception caught!" );
2881 void EffectSequenceHelper::processAfterEffect( const Reference
< XAnimationNode
>& xNode
)
2885 Reference
< XAnimationNode
> xMaster
;
2887 Sequence
< NamedValue
> aUserData( xNode
->getUserData() );
2888 const NamedValue
* pProp
= std::find_if(aUserData
.begin(), aUserData
.end(),
2889 [](const NamedValue
& rProp
) { return rProp
.Name
== "master-element"; });
2891 if (pProp
!= aUserData
.end())
2892 pProp
->Value
>>= xMaster
;
2894 // only process if this is a valid after effect
2897 CustomAnimationEffectPtr pMasterEffect
;
2899 // find the master effect
2900 stl_CustomAnimationEffect_search_node_predict
aSearchPredict( xMaster
);
2901 EffectSequence::iterator
aIter( std::find_if( maEffects
.begin(), maEffects
.end(), aSearchPredict
) );
2902 if( aIter
!= maEffects
.end() )
2903 pMasterEffect
= *aIter
;
2905 if( pMasterEffect
.get() )
2907 pMasterEffect
->setHasAfterEffect( true );
2909 // find out what kind of after effect this is
2910 if( xNode
->getType() == AnimationNodeType::ANIMATECOLOR
)
2913 Reference
< XAnimate
> xAnimate( xNode
, UNO_QUERY_THROW
);
2914 pMasterEffect
->setDimColor( xAnimate
->getTo() );
2915 pMasterEffect
->setAfterEffectOnNext( true );
2920 pMasterEffect
->setAfterEffectOnNext( xNode
->getParent() != xMaster
->getParent() );
2927 OSL_FAIL( "sd::EffectSequenceHelper::processAfterEffect(), exception caught!" );
2931 class AnimationChangeListener
: public cppu::WeakImplHelper
< XChangesListener
>
2934 explicit AnimationChangeListener( MainSequence
* pMainSequence
) : mpMainSequence( pMainSequence
) {}
2936 virtual void SAL_CALL
changesOccurred( const css::util::ChangesEvent
& Event
) override
;
2937 virtual void SAL_CALL
disposing( const css::lang::EventObject
& Source
) override
;
2939 MainSequence
* mpMainSequence
;
2942 void SAL_CALL
AnimationChangeListener::changesOccurred( const css::util::ChangesEvent
& )
2944 if( mpMainSequence
)
2945 mpMainSequence
->startRecreateTimer();
2948 void SAL_CALL
AnimationChangeListener::disposing( const css::lang::EventObject
& )
2952 MainSequence::MainSequence()
2953 : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext()))
2954 , mbTimerMode(false)
2955 , mbRebuilding( false )
2956 , mnRebuildLockGuard( 0 )
2957 , mbPendingRebuildRequest( false )
2958 , mbIgnoreChanges( 0 )
2960 if( mxTimingRootNode
.is() )
2962 Sequence
< css::beans::NamedValue
> aUserData
2963 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::MAIN_SEQUENCE
) } };
2964 mxTimingRootNode
->setUserData( aUserData
);
2969 MainSequence::MainSequence( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
)
2970 : mxTimingRootNode( xNode
, UNO_QUERY
)
2971 , mbTimerMode( false )
2972 , mbRebuilding( false )
2973 , mnRebuildLockGuard( 0 )
2974 , mbPendingRebuildRequest( false )
2975 , mbIgnoreChanges( 0 )
2980 MainSequence::~MainSequence()
2985 void MainSequence::init()
2987 mnSequenceType
= EffectNodeType::MAIN_SEQUENCE
;
2989 maTimer
.SetInvokeHandler( LINK(this, MainSequence
, onTimerHdl
) );
2990 maTimer
.SetTimeout(50);
2992 mxChangesListener
.set( new AnimationChangeListener( this ) );
2994 createMainSequence();
2997 void MainSequence::reset( const css::uno::Reference
< css::animations::XAnimationNode
>& xTimingRootNode
)
3001 mxTimingRootNode
.set( xTimingRootNode
, UNO_QUERY
);
3003 createMainSequence();
3006 Reference
< css::animations::XAnimationNode
> MainSequence::getRootNode()
3008 DBG_ASSERT( mnRebuildLockGuard
== 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" );
3010 if( maTimer
.IsActive() && mbTimerMode
)
3012 // force a rebuild NOW if one is pending
3017 return EffectSequenceHelper::getRootNode();
3020 void MainSequence::createMainSequence()
3022 if( mxTimingRootNode
.is() ) try
3024 Reference
< XEnumerationAccess
> xEnumerationAccess( mxTimingRootNode
, UNO_QUERY_THROW
);
3025 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
3026 while( xEnumeration
->hasMoreElements() )
3028 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
3029 sal_Int32 nNodeType
= CustomAnimationEffect::get_node_type( xChildNode
);
3030 if( nNodeType
== EffectNodeType::MAIN_SEQUENCE
)
3032 mxSequenceRoot
.set( xChildNode
, UNO_QUERY
);
3033 EffectSequenceHelper::create( xChildNode
);
3035 else if( nNodeType
== EffectNodeType::INTERACTIVE_SEQUENCE
)
3037 Reference
< XTimeContainer
> xInteractiveRoot( xChildNode
, UNO_QUERY_THROW
);
3038 InteractiveSequencePtr
pIS( new InteractiveSequence( xInteractiveRoot
, this ) );
3039 pIS
->addListener( this );
3040 maInteractiveSequenceVector
.push_back( pIS
);
3044 // see if we have a mainsequence at all. if not, create one...
3045 if( !mxSequenceRoot
.is() )
3047 mxSequenceRoot
= SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3049 uno::Sequence
< css::beans::NamedValue
> aUserData
3050 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::MAIN_SEQUENCE
) } };
3051 mxSequenceRoot
->setUserData( aUserData
);
3053 // empty sequence until now, set duration to 0.0
3054 // explicitly (otherwise, this sequence will never
3056 mxSequenceRoot
->setDuration( makeAny(0.0) );
3058 Reference
< XAnimationNode
> xMainSequenceNode( mxSequenceRoot
, UNO_QUERY_THROW
);
3059 mxTimingRootNode
->appendChild( xMainSequenceNode
);
3066 Reference
< XChangesNotifier
> xNotifier( mxTimingRootNode
, UNO_QUERY
);
3067 if( xNotifier
.is() )
3068 xNotifier
->addChangesListener( mxChangesListener
);
3072 OSL_FAIL( "sd::MainSequence::create(), exception caught!" );
3076 DBG_ASSERT( mxSequenceRoot
.is(), "sd::MainSequence::create(), found no main sequence!" );
3079 void MainSequence::reset()
3081 EffectSequenceHelper::reset();
3083 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3084 interactiveSequence
->reset();
3085 maInteractiveSequenceVector
.clear();
3089 Reference
< XChangesNotifier
> xNotifier( mxTimingRootNode
, UNO_QUERY
);
3090 if( xNotifier
.is() )
3091 xNotifier
->removeChangesListener( mxChangesListener
);
3099 InteractiveSequencePtr
MainSequence::createInteractiveSequence( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
3101 InteractiveSequencePtr pIS
;
3103 // create a new interactive sequence container
3104 Reference
< XTimeContainer
> xISRoot
= SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3106 uno::Sequence
< css::beans::NamedValue
> aUserData
3107 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE
) } };
3108 xISRoot
->setUserData( aUserData
);
3109 xISRoot
->setRestart( css::animations::AnimationRestart::WHEN_NOT_ACTIVE
);
3111 Reference
< XChild
> xChild( mxSequenceRoot
, UNO_QUERY_THROW
);
3112 Reference
< XTimeContainer
> xParent( xChild
->getParent(), UNO_QUERY_THROW
);
3113 xParent
->appendChild( xISRoot
);
3115 pIS
.reset( new InteractiveSequence( xISRoot
, this) );
3116 pIS
->setTriggerShape( xShape
);
3117 pIS
->addListener( this );
3118 maInteractiveSequenceVector
.push_back( pIS
);
3122 CustomAnimationEffectPtr
MainSequence::findEffect( const css::uno::Reference
< css::animations::XAnimationNode
>& xNode
) const
3124 CustomAnimationEffectPtr pEffect
= EffectSequenceHelper::findEffect( xNode
);
3126 if( pEffect
.get() == nullptr )
3128 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3130 pEffect
= interactiveSequence
->findEffect( xNode
);
3138 sal_Int32
MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr
& pEffect
) const
3140 sal_Int32 nOffset
= EffectSequenceHelper::getOffsetFromEffect( pEffect
);
3145 nOffset
= EffectSequenceHelper::getCount();
3147 for (auto const& interactiveSequence
: maInteractiveSequenceVector
)
3149 sal_Int32 nTemp
= interactiveSequence
->getOffsetFromEffect( pEffect
);
3151 return nOffset
+ nTemp
;
3153 nOffset
+= interactiveSequence
->getCount();
3159 CustomAnimationEffectPtr
MainSequence::getEffectFromOffset( sal_Int32 nOffset
) const
3163 if( nOffset
< getCount() )
3164 return EffectSequenceHelper::getEffectFromOffset( nOffset
);
3166 nOffset
-= getCount();
3168 auto aIter( maInteractiveSequenceVector
.begin() );
3170 while( (aIter
!= maInteractiveSequenceVector
.end()) && (nOffset
> (*aIter
)->getCount()) )
3171 nOffset
-= (*aIter
++)->getCount();
3173 if( (aIter
!= maInteractiveSequenceVector
.end()) && (nOffset
>= 0) )
3174 return (*aIter
)->getEffectFromOffset( nOffset
);
3177 CustomAnimationEffectPtr pEffect
;
3181 bool MainSequence::disposeShape( const Reference
< XShape
>& xShape
)
3183 bool bChanges
= EffectSequenceHelper::disposeShape( xShape
);
3185 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3187 bChanges
|= iterativeSequence
->disposeShape( xShape
);
3191 startRebuildTimer();
3196 bool MainSequence::hasEffect( const css::uno::Reference
< css::drawing::XShape
>& xShape
)
3198 if( EffectSequenceHelper::hasEffect( xShape
) )
3201 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3203 if( iterativeSequence
->getTriggerShape() == xShape
)
3206 if( iterativeSequence
->hasEffect( xShape
) )
3213 void MainSequence::insertTextRange( const css::uno::Any
& aTarget
)
3215 EffectSequenceHelper::insertTextRange( aTarget
);
3217 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3219 iterativeSequence
->insertTextRange( aTarget
);
3223 void MainSequence::disposeTextRange( const css::uno::Any
& aTarget
)
3225 EffectSequenceHelper::disposeTextRange( aTarget
);
3227 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3229 iterativeSequence
->disposeTextRange( aTarget
);
3233 /** callback from the sd::View when an object just left text edit mode */
3234 void MainSequence::onTextChanged( const Reference
< XShape
>& xShape
)
3236 EffectSequenceHelper::onTextChanged( xShape
);
3238 for (auto const& iterativeSequence
: maInteractiveSequenceVector
)
3240 iterativeSequence
->onTextChanged( xShape
);
3244 void EffectSequenceHelper::onTextChanged( const Reference
< XShape
>& xShape
)
3246 bool bChanges
= std::accumulate(maEffects
.begin(), maEffects
.end(), false,
3247 [&xShape
](const bool bCheck
, const CustomAnimationEffectPtr
& rxEffect
) {
3249 if (rxEffect
->getTargetShape() == xShape
)
3250 bRes
|= rxEffect
->checkForText();
3258 void MainSequence::rebuild()
3260 startRebuildTimer();
3263 void MainSequence::lockRebuilds()
3265 mnRebuildLockGuard
++;
3268 void MainSequence::unlockRebuilds()
3270 DBG_ASSERT( mnRebuildLockGuard
, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" );
3271 if( mnRebuildLockGuard
)
3272 mnRebuildLockGuard
--;
3274 if( (mnRebuildLockGuard
== 0) && mbPendingRebuildRequest
)
3276 mbPendingRebuildRequest
= false;
3277 startRebuildTimer();
3281 void MainSequence::implRebuild()
3283 if( mnRebuildLockGuard
)
3285 mbPendingRebuildRequest
= true;
3289 mbRebuilding
= true;
3291 EffectSequenceHelper::implRebuild();
3293 auto aIter( maInteractiveSequenceVector
.begin() );
3294 while( aIter
!= maInteractiveSequenceVector
.end() )
3296 InteractiveSequencePtr
pIS( *aIter
);
3297 if( pIS
->maEffects
.empty() )
3299 // remove empty interactive sequences
3300 aIter
= maInteractiveSequenceVector
.erase( aIter
);
3302 Reference
< XChild
> xChild( mxSequenceRoot
, UNO_QUERY_THROW
);
3303 Reference
< XTimeContainer
> xParent( xChild
->getParent(), UNO_QUERY_THROW
);
3304 Reference
< XAnimationNode
> xISNode( pIS
->mxSequenceRoot
, UNO_QUERY_THROW
);
3305 xParent
->removeChild( xISNode
);
3315 mbRebuilding
= false;
3318 void MainSequence::notify_change()
3323 bool MainSequence::setTrigger( const CustomAnimationEffectPtr
& pEffect
, const css::uno::Reference
< css::drawing::XShape
>& xTriggerShape
)
3325 EffectSequenceHelper
* pOldSequence
= pEffect
->getEffectSequence();
3327 EffectSequenceHelper
* pNewSequence
= nullptr;
3328 if( xTriggerShape
.is() )
3330 for (InteractiveSequencePtr
const& pIS
: maInteractiveSequenceVector
)
3332 if( pIS
->getTriggerShape() == xTriggerShape
)
3334 pNewSequence
= pIS
.get();
3340 pNewSequence
= createInteractiveSequence( xTriggerShape
).get();
3344 pNewSequence
= this;
3347 if( pOldSequence
!= pNewSequence
)
3350 pOldSequence
->maEffects
.remove( pEffect
);
3352 pNewSequence
->maEffects
.push_back( pEffect
);
3353 pEffect
->setEffectSequence( pNewSequence
);
3363 IMPL_LINK_NOARG(MainSequence
, onTimerHdl
, Timer
*, void)
3372 createMainSequence();
3376 /** starts a timer that recreates the internal structure from the API core */
3377 void MainSequence::startRecreateTimer()
3379 if( !mbRebuilding
&& (mbIgnoreChanges
== 0) )
3381 mbTimerMode
= false;
3387 * starts a timer that rebuilds the API core from the internal structure
3388 * This is used to reduce the number of screen redraws due to animation changes.
3390 void MainSequence::startRebuildTimer()
3396 InteractiveSequence::InteractiveSequence( const Reference
< XTimeContainer
>& xSequenceRoot
, MainSequence
* pMainSequence
)
3397 : EffectSequenceHelper( xSequenceRoot
), mpMainSequence( pMainSequence
)
3399 mnSequenceType
= EffectNodeType::INTERACTIVE_SEQUENCE
;
3403 if( mxSequenceRoot
.is() )
3405 Reference
< XEnumerationAccess
> xEnumerationAccess( mxSequenceRoot
, UNO_QUERY_THROW
);
3406 Reference
< XEnumeration
> xEnumeration( xEnumerationAccess
->createEnumeration(), UNO_SET_THROW
);
3407 while( !mxEventSource
.is() && xEnumeration
->hasMoreElements() )
3409 Reference
< XAnimationNode
> xChildNode( xEnumeration
->nextElement(), UNO_QUERY_THROW
);
3412 if( (xChildNode
->getBegin() >>= aEvent
) && (aEvent
.Trigger
== EventTrigger::ON_CLICK
) )
3413 aEvent
.Source
>>= mxEventSource
;
3419 OSL_FAIL( "sd::InteractiveSequence::InteractiveSequence(), exception caught!" );
3424 void InteractiveSequence::rebuild()
3426 mpMainSequence
->rebuild();
3429 void InteractiveSequence::implRebuild()
3431 EffectSequenceHelper::implRebuild();
3434 MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr
& pMainSequence
)
3435 : mpMainSequence( pMainSequence
)
3437 if( mpMainSequence
.get() )
3438 mpMainSequence
->lockRebuilds();
3441 MainSequenceRebuildGuard::~MainSequenceRebuildGuard()
3443 if( mpMainSequence
.get() )
3444 mpMainSequence
->unlockRebuilds();
3449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */