Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / sd / source / core / CustomAnimationEffect.cxx
blob1473358706fc7471fa24e010082859b5b181c078
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <com/sun/star/animations/AnimationNodeType.hpp>
22 #include <com/sun/star/animations/AnimateColor.hpp>
23 #include <com/sun/star/animations/AnimateMotion.hpp>
24 #include <com/sun/star/animations/AnimateSet.hpp>
25 #include <com/sun/star/animations/AnimationFill.hpp>
26 #include <com/sun/star/animations/Audio.hpp>
27 #include <com/sun/star/animations/Command.hpp>
28 #include <com/sun/star/animations/Event.hpp>
29 #include <com/sun/star/animations/EventTrigger.hpp>
30 #include <com/sun/star/animations/IterateContainer.hpp>
31 #include <com/sun/star/animations/ParallelTimeContainer.hpp>
32 #include <com/sun/star/animations/SequenceTimeContainer.hpp>
33 #include <com/sun/star/animations/Timing.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/beans/NamedValue.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/container/XEnumerationAccess.hpp>
42 #include <com/sun/star/lang/XInitialization.hpp>
43 #include <com/sun/star/presentation/EffectNodeType.hpp>
44 #include <com/sun/star/presentation/EffectCommands.hpp>
45 #include <com/sun/star/presentation/EffectPresetClass.hpp>
46 #include <com/sun/star/presentation/ParagraphTarget.hpp>
47 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
48 #include <com/sun/star/text/XText.hpp>
49 #include <com/sun/star/util/XCloneable.hpp>
50 #include <com/sun/star/util/XChangesNotifier.hpp>
51 #include <comphelper/processfactory.hxx>
52 #include <comphelper/sequence.hxx>
53 #include <com/sun/star/lang/Locale.hpp>
54 #include <com/sun/star/i18n/BreakIterator.hpp>
55 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
56 #include <com/sun/star/i18n/WordType.hpp>
57 #include <com/sun/star/presentation/TextAnimationType.hpp>
59 #include <basegfx/polygon/b2dpolypolygon.hxx>
60 #include <basegfx/polygon/b2dpolypolygontools.hxx>
61 #include <basegfx/matrix/b2dhommatrix.hxx>
62 #include <basegfx/range/b2drange.hxx>
63 #include <basegfx/matrix/b2dhommatrixtools.hxx>
65 #include <algorithm>
66 #include <deque>
68 #include <cppuhelper/implbase.hxx>
70 #include <drawinglayer/geometry/viewinformation2d.hxx>
71 #include <svx/sdr/contact/viewcontact.hxx>
72 #include <svx/svdopath.hxx>
73 #include <svx/svdpage.hxx>
74 #include <svx/unoapi.hxx>
75 #include <CustomAnimationEffect.hxx>
76 #include <CustomAnimationPreset.hxx>
77 #include <animations.hxx>
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::uno;
81 using namespace ::com::sun::star::presentation;
82 using namespace ::com::sun::star::animations;
84 using ::com::sun::star::container::XEnumerationAccess;
85 using ::com::sun::star::container::XEnumeration;
86 using ::com::sun::star::beans::NamedValue;
87 using ::com::sun::star::container::XChild;
88 using ::com::sun::star::drawing::XShape;
89 using ::com::sun::star::lang::XInitialization;
90 using ::com::sun::star::text::XText;
91 using ::com::sun::star::text::XTextRange;
92 using ::com::sun::star::beans::XPropertySet;
93 using ::com::sun::star::util::XCloneable;
94 using ::com::sun::star::lang::Locale;
95 using ::com::sun::star::util::XChangesNotifier;
96 using ::com::sun::star::util::XChangesListener;
98 namespace sd
100 class MainSequenceChangeGuard
102 public:
103 explicit MainSequenceChangeGuard( EffectSequenceHelper* pSequence )
105 mpMainSequence = dynamic_cast< MainSequence* >( pSequence );
106 if( mpMainSequence == nullptr )
108 InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence );
109 if( pI )
110 mpMainSequence = pI->mpMainSequence;
112 DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" );
114 if( mpMainSequence )
115 mpMainSequence->mbIgnoreChanges++;
118 ~MainSequenceChangeGuard()
120 if( mpMainSequence )
121 mpMainSequence->mbIgnoreChanges++;
124 private:
125 MainSequence* mpMainSequence;
128 CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
129 : mnNodeType(-1),
130 mnPresetClass(-1),
131 mfBegin(-1.0),
132 mfDuration(-1.0),
133 mfAbsoluteDuration(-1.0),
134 mnGroupId(-1),
135 mnIterateType(0),
136 mfIterateInterval(0.0),
137 mnParaDepth( -1 ),
138 mbHasText(false),
139 mfAcceleration( 1.0 ),
140 mfDecelerate( 1.0 ),
141 mbAutoReverse(false),
142 mnTargetSubItem(0),
143 mnCommand(0),
144 mpEffectSequence( nullptr ),
145 mbHasAfterEffect(false),
146 mbAfterEffectOnNextEffect(false)
148 setNode( xNode );
151 void CustomAnimationEffect::setNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
153 mxNode = xNode;
154 mxAudio.clear();
156 Sequence< NamedValue > aUserData( mxNode->getUserData() );
157 sal_Int32 nLength = aUserData.getLength();
158 const NamedValue* p = aUserData.getConstArray();
160 while( nLength-- )
162 if ( p->Name == "node-type" )
164 p->Value >>= mnNodeType;
166 else if ( p->Name == "preset-id" )
168 p->Value >>= maPresetId;
170 else if ( p->Name == "preset-sub-type" )
172 p->Value >>= maPresetSubType;
174 else if ( p->Name == "preset-class" )
176 p->Value >>= mnPresetClass;
178 else if ( p->Name == "preset-property" )
180 p->Value >>= maProperty;
182 else if ( p->Name == "group-id" )
184 p->Value >>= mnGroupId;
187 p++;
190 // get effect start time
191 mxNode->getBegin() >>= mfBegin;
193 mfAcceleration = mxNode->getAcceleration();
194 mfDecelerate = mxNode->getDecelerate();
195 mbAutoReverse = mxNode->getAutoReverse();
197 // get iteration data
198 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
199 if( xIter.is() )
201 mfIterateInterval = xIter->getIterateInterval();
202 mnIterateType = xIter->getIterateType();
203 maTarget = xIter->getTarget();
204 mnTargetSubItem = xIter->getSubItem();
206 else
208 mfIterateInterval = 0.0f;
209 mnIterateType = 0;
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(), UNO_QUERY );
217 if( xEnumeration.is() )
219 while( xEnumeration->hasMoreElements() )
221 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
222 if( !xChildNode.is() )
223 continue;
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 );
232 if( xCommand.is() )
234 mnCommand = xCommand->getCommand();
235 if( !maTarget.hasValue() )
236 maTarget = xCommand->getTarget();
239 else
241 double fBegin = 0.0;
242 double fDuration = 0.0;
243 xChildNode->getBegin() >>= fBegin;
244 xChildNode->getDuration() >>= fDuration;
246 fDuration += fBegin;
247 if( fDuration > mfDuration )
248 mfDuration = fDuration;
250 // no target shape yet?
251 if( !maTarget.hasValue() )
253 // go get it boys!
254 Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY );
255 if( xAnimate.is() )
257 maTarget = xAnimate->getTarget();
258 mnTargetSubItem = xAnimate->getSubItem();
266 mfAbsoluteDuration = mfDuration;
267 double fRepeatCount = 1.0;
268 if( (mxNode->getRepeatCount()) >>= fRepeatCount )
269 mfAbsoluteDuration *= fRepeatCount;
271 checkForText();
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;
284 aTarget >>= xShape;
285 if( !xShape.is() )
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
298 if( xShape.is() )
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_QUERY_THROW );
306 css::lang::Locale aLocale;
307 const OUString aStrLocaleName( "CharLocale" );
308 Reference< XTextRange > xParagraph;
310 sal_Int32 nPara = 0;
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 ) )
317 continue;
319 if( nIterateType == TextAnimationType::BY_PARAGRAPH )
321 nSubItems++;
323 else
325 const OUString aText( xParagraph->getString() );
326 Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW );
327 xSet->getPropertyValue( aStrLocaleName ) >>= aLocale;
329 sal_Int32 nPos;
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;
337 nSubItems++;
339 break;
341 else
343 sal_Int32 nDone;
344 for( nPos = 0; nPos < nEndPos; nPos++ )
346 nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone);
347 nSubItems++;
352 if( nPara == nOnlyPara )
353 break;
355 nPara++;
359 catch( Exception& )
361 nSubItems = 0;
362 OSL_FAIL( "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" );
365 return nSubItems;
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() );
378 return pEffect;
381 sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode )
383 sal_Int16 nNodeType = -1;
385 if( xNode.is() )
387 Sequence< NamedValue > aUserData( xNode->getUserData() );
388 sal_Int32 nLength = aUserData.getLength();
389 if( nLength )
391 const NamedValue* p = aUserData.getConstArray();
392 while( nLength-- )
394 if ( p->Name == "node-type" )
396 p->Value >>= nNodeType;
397 break;
399 p++;
404 return nNodeType;
407 void CustomAnimationEffect::setPresetClass( sal_Int16 nPresetClass )
409 if( mnPresetClass != nPresetClass )
411 mnPresetClass = nPresetClass;
412 if( mxNode.is() )
414 // first try to find a "preset-class" entry in the user data
415 // and change it
416 Sequence< NamedValue > aUserData( mxNode->getUserData() );
417 sal_Int32 nLength = aUserData.getLength();
418 bool bFound = false;
419 if( nLength )
421 NamedValue* p = aUserData.getArray();
422 while( nLength-- )
424 if ( p->Name == "preset-class" )
426 p->Value <<= mnPresetClass;
427 bFound = true;
428 break;
430 p++;
434 // no "node-type" entry inside user data, so add it
435 if( !bFound )
437 nLength = aUserData.getLength();
438 aUserData.realloc( nLength + 1);
439 aUserData[nLength].Name = "preset-class";
440 aUserData[nLength].Value <<= mnPresetClass;
443 mxNode->setUserData( aUserData );
448 void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType )
450 if( mnNodeType != nNodeType )
452 mnNodeType = nNodeType;
453 if( mxNode.is() )
455 // first try to find a "node-type" entry in the user data
456 // and change it
457 Sequence< NamedValue > aUserData( mxNode->getUserData() );
458 sal_Int32 nLength = aUserData.getLength();
459 bool bFound = false;
460 if( nLength )
462 NamedValue* p = aUserData.getArray();
463 while( nLength-- )
465 if ( p->Name == "node-type" )
467 p->Value <<= mnNodeType;
468 bFound = true;
469 break;
471 p++;
475 // no "node-type" entry inside user data, so add it
476 if( !bFound )
478 nLength = aUserData.getLength();
479 aUserData.realloc( nLength + 1);
480 aUserData[nLength].Name = "node-type";
481 aUserData[nLength].Value <<= mnNodeType;
484 mxNode->setUserData( aUserData );
489 void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId )
491 mnGroupId = nGroupId;
492 if( mxNode.is() )
494 // first try to find a "group-id" entry in the user data
495 // and change it
496 Sequence< NamedValue > aUserData( mxNode->getUserData() );
497 sal_Int32 nLength = aUserData.getLength();
498 bool bFound = false;
499 if( nLength )
501 NamedValue* p = aUserData.getArray();
502 while( nLength-- )
504 if ( p->Name == "group-id" )
506 p->Value <<= mnGroupId;
507 bFound = true;
508 break;
510 p++;
514 // no "node-type" entry inside user data, so add it
515 if( !bFound )
517 nLength = aUserData.getLength();
518 aUserData.realloc( nLength + 1);
519 aUserData[nLength].Name = "group-id";
520 aUserData[nLength].Value <<= mnGroupId;
523 mxNode->setUserData( aUserData );
527 /** checks if the text for this effect has changed and updates internal flags.
528 returns true if something changed.
530 bool CustomAnimationEffect::checkForText()
532 bool bChange = false;
534 Reference< XText > xText;
536 if( maTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
538 // calc para depth
539 ParagraphTarget aParaTarget;
540 maTarget >>= aParaTarget;
542 xText.set( aParaTarget.Shape, UNO_QUERY );
544 // get paragraph
545 if( xText.is() )
547 Reference< XEnumerationAccess > xEA( xText, UNO_QUERY );
548 if( xEA.is() )
550 Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_QUERY );
551 if( xEnumeration.is() )
553 bool bHasText = xEnumeration->hasMoreElements();
554 bChange |= bHasText != mbHasText;
555 mbHasText = bHasText;
557 sal_Int32 nPara = aParaTarget.Paragraph;
559 while( xEnumeration->hasMoreElements() && nPara-- )
560 xEnumeration->nextElement();
562 if( xEnumeration->hasMoreElements() )
564 Reference< XPropertySet > xParaSet;
565 xEnumeration->nextElement() >>= xParaSet;
566 if( xParaSet.is() )
568 sal_Int32 nParaDepth = 0;
569 const OUString strNumberingLevel( "NumberingLevel" );
570 xParaSet->getPropertyValue( strNumberingLevel ) >>= nParaDepth;
571 bChange |= nParaDepth != mnParaDepth;
572 mnParaDepth = nParaDepth;
579 else
581 maTarget >>= xText;
582 bool bHasText = xText.is() && !xText->getString().isEmpty();
583 bChange |= bHasText != mbHasText;
584 mbHasText = bHasText;
587 bChange |= calculateIterateDuration();
588 return bChange;
591 bool CustomAnimationEffect::calculateIterateDuration()
593 bool bChange = false;
595 // if we have an iteration, we must also calculate the
596 // 'true' container duration, that is
597 // ( ( is form animated ) ? [contained effects duration] : 0 ) +
598 // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration]
599 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
600 if( xIter.is() )
602 double fDuration = mfDuration;
603 const double fSubEffectDuration = mfDuration;
605 if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check
607 const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType );
608 if( nSubItems )
610 const double f = (nSubItems-1) * mfIterateInterval;
611 fDuration += f;
615 // if we also animate the form first, we have to add the
616 // sub effect duration to the whole effect duration
617 if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE )
618 fDuration += fSubEffectDuration;
620 bChange |= fDuration != mfAbsoluteDuration;
621 mfAbsoluteDuration = fDuration;
624 return bChange;
627 void CustomAnimationEffect::setTarget( const css::uno::Any& rTarget )
631 maTarget = rTarget;
633 // first, check special case for random node
634 Reference< XInitialization > xInit( mxNode, UNO_QUERY );
635 if( xInit.is() )
637 const Sequence< Any > aArgs( &maTarget, 1 );
638 xInit->initialize( aArgs );
640 else
642 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
643 if( xIter.is() )
645 xIter->setTarget(maTarget);
647 else
649 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
650 if( xEnumerationAccess.is() )
652 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
653 if( xEnumeration.is() )
655 while( xEnumeration->hasMoreElements() )
657 const Any aElem( xEnumeration->nextElement() );
658 Reference< XAnimate > xAnimate( aElem, UNO_QUERY );
659 if( xAnimate.is() )
660 xAnimate->setTarget( rTarget );
661 else
663 Reference< XCommand > xCommand( aElem, UNO_QUERY );
664 if( xCommand.is() )
665 xCommand->setTarget( rTarget );
672 checkForText();
674 catch( Exception& )
676 OSL_FAIL( "sd::CustomAnimationEffect::setTarget(), exception caught!" );
680 void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem )
684 mnTargetSubItem = nSubItem;
686 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
687 if( xIter.is() )
689 xIter->setSubItem(mnTargetSubItem);
691 else
693 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
694 if( xEnumerationAccess.is() )
696 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
697 if( xEnumeration.is() )
699 while( xEnumeration->hasMoreElements() )
701 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
702 if( xAnimate.is() )
703 xAnimate->setSubItem( mnTargetSubItem );
709 catch( Exception& )
711 OSL_FAIL( "sd::CustomAnimationEffect::setTargetSubItem(), exception caught!" );
715 void CustomAnimationEffect::setDuration( double fDuration )
717 if( (mfDuration != -1.0) && (mfDuration != fDuration) ) try
719 double fScale = fDuration / mfDuration;
720 mfDuration = fDuration;
721 double fRepeatCount = 1.0;
722 getRepeatCount() >>= fRepeatCount;
723 mfAbsoluteDuration = mfDuration * fRepeatCount;
725 // calculate effect duration and get target shape
726 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
727 if( xEnumerationAccess.is() )
729 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
730 if( xEnumeration.is() )
732 while( xEnumeration->hasMoreElements() )
734 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
735 if( !xChildNode.is() )
736 continue;
738 double fChildBegin = 0.0;
739 xChildNode->getBegin() >>= fChildBegin;
740 if( fChildBegin != 0.0 )
742 fChildBegin *= fScale;
743 xChildNode->setBegin( makeAny( fChildBegin ) );
746 double fChildDuration = 0.0;
747 xChildNode->getDuration() >>= fChildDuration;
748 if( fChildDuration != 0.0 )
750 fChildDuration *= fScale;
751 xChildNode->setDuration( makeAny( fChildDuration ) );
756 calculateIterateDuration();
758 catch( Exception& )
760 OSL_FAIL( "sd::CustomAnimationEffect::setDuration(), exception caught!" );
764 void CustomAnimationEffect::setBegin( double fBegin )
766 if( mxNode.is() ) try
768 mfBegin = fBegin;
769 mxNode->setBegin( makeAny( fBegin ) );
771 catch( Exception& )
773 OSL_FAIL( "sd::CustomAnimationEffect::setBegin(), exception caught!" );
777 void CustomAnimationEffect::setAcceleration( double fAcceleration )
779 if( mxNode.is() ) try
781 mfAcceleration = fAcceleration;
782 mxNode->setAcceleration( fAcceleration );
784 catch( Exception& )
786 OSL_FAIL( "sd::CustomAnimationEffect::setAcceleration(), exception caught!" );
790 void CustomAnimationEffect::setDecelerate( double fDecelerate )
792 if( mxNode.is() ) try
794 mfDecelerate = fDecelerate;
795 mxNode->setDecelerate( fDecelerate );
797 catch( Exception& )
799 OSL_FAIL( "sd::CustomAnimationEffect::setDecelerate(), exception caught!" );
803 void CustomAnimationEffect::setAutoReverse( bool bAutoReverse )
805 if( mxNode.is() ) try
807 mbAutoReverse = bAutoReverse;
808 mxNode->setAutoReverse( bAutoReverse );
810 catch( Exception& )
812 OSL_FAIL( "sd::CustomAnimationEffect::setAutoReverse(), exception caught!" );
816 void CustomAnimationEffect::replaceNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
818 sal_Int16 nNodeType = mnNodeType;
819 Any aTarget = maTarget;
821 double fBegin = mfBegin;
822 double fDuration = mfDuration;
823 double fAcceleration = mfAcceleration;
824 double fDecelerate = mfDecelerate ;
825 bool bAutoReverse = mbAutoReverse;
826 Reference< XAudio > xAudio( mxAudio );
827 sal_Int16 nIterateType = mnIterateType;
828 double fIterateInterval = mfIterateInterval;
829 sal_Int16 nSubItem = mnTargetSubItem;
831 setNode( xNode );
833 setAudio( xAudio );
834 setNodeType( nNodeType );
835 setTarget( aTarget );
836 setTargetSubItem( nSubItem );
837 setDuration( fDuration );
838 setBegin( fBegin );
840 setAcceleration( fAcceleration );
841 setDecelerate( fDecelerate );
842 setAutoReverse( bAutoReverse );
844 if( nIterateType != mnIterateType )
845 setIterateType( nIterateType );
847 if( mnIterateType && ( fIterateInterval != mfIterateInterval ) )
848 setIterateInterval( fIterateInterval );
851 Reference< XShape > CustomAnimationEffect::getTargetShape() const
853 Reference< XShape > xShape;
854 maTarget >>= xShape;
855 if( !xShape.is() )
857 ParagraphTarget aParaTarget;
858 if( maTarget >>= aParaTarget )
859 xShape = aParaTarget.Shape;
862 return xShape;
865 Any CustomAnimationEffect::getRepeatCount() const
867 if( mxNode.is() )
869 return mxNode->getRepeatCount();
871 else
873 Any aAny;
874 return aAny;
878 Any CustomAnimationEffect::getEnd() const
880 if( mxNode.is() )
882 return mxNode->getEnd();
884 else
886 Any aAny;
887 return aAny;
891 sal_Int16 CustomAnimationEffect::getFill() const
893 if( mxNode.is() )
894 return mxNode->getFill();
895 else
896 return 0;
899 void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount )
901 if( mxNode.is() )
903 mxNode->setRepeatCount( rRepeatCount );
904 double fRepeatCount = 1.0;
905 rRepeatCount >>= fRepeatCount;
906 mfAbsoluteDuration = mfDuration * fRepeatCount;
910 void CustomAnimationEffect::setEnd( const Any& rEnd )
912 if( mxNode.is() )
913 mxNode->setEnd( rEnd );
916 void CustomAnimationEffect::setFill( sal_Int16 nFill )
918 if( mxNode.is() )
919 mxNode->setFill( nFill );
922 Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const
924 DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" );
926 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
928 Reference< XAnimate > xAnimate;
929 if( maDimColor.hasValue() )
930 xAnimate = AnimateColor::create( xContext );
931 else
932 xAnimate = AnimateSet::create( xContext );
934 Any aTo;
935 OUString aAttributeName;
937 if( maDimColor.hasValue() )
939 aTo = maDimColor;
940 aAttributeName = "DimColor";
942 else
944 aTo <<= false;
945 aAttributeName = "Visibility";
948 Any aBegin;
949 if( !mbAfterEffectOnNextEffect ) // sameClick
951 Event aEvent;
953 aEvent.Source <<= getNode();
954 aEvent.Trigger = EventTrigger::END_EVENT;
955 aEvent.Repeat = 0;
957 aBegin <<= aEvent;
959 else
961 aBegin <<= 0.0;
964 xAnimate->setBegin( aBegin );
965 xAnimate->setTo( aTo );
966 xAnimate->setAttributeName( aAttributeName );
968 xAnimate->setDuration( makeAny( 0.001 ) );
969 xAnimate->setFill( AnimationFill::HOLD );
970 xAnimate->setTarget( maTarget );
972 return xAnimate;
975 void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType )
977 if( mnIterateType != nIterateType ) try
979 // do we need to exchange the container node?
980 if( (mnIterateType == 0) || (nIterateType == 0) )
982 sal_Int16 nTargetSubItem = mnTargetSubItem;
984 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
985 Reference< XTimeContainer > xNewContainer;
986 if(nIterateType)
988 xNewContainer.set( IterateContainer::create( xContext ) );
990 else
991 xNewContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
993 Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW );
994 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
995 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
996 while( xEnumeration->hasMoreElements() )
998 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
999 xOldContainer->removeChild( xChildNode );
1000 xNewContainer->appendChild( xChildNode );
1003 xNewContainer->setBegin( mxNode->getBegin() );
1004 xNewContainer->setDuration( mxNode->getDuration() );
1005 xNewContainer->setEnd( mxNode->getEnd() );
1006 xNewContainer->setEndSync( mxNode->getEndSync() );
1007 xNewContainer->setRepeatCount( mxNode->getRepeatCount() );
1008 xNewContainer->setFill( mxNode->getFill() );
1009 xNewContainer->setFillDefault( mxNode->getFillDefault() );
1010 xNewContainer->setRestart( mxNode->getRestart() );
1011 xNewContainer->setRestartDefault( mxNode->getRestartDefault() );
1012 xNewContainer->setAcceleration( mxNode->getAcceleration() );
1013 xNewContainer->setDecelerate( mxNode->getDecelerate() );
1014 xNewContainer->setAutoReverse( mxNode->getAutoReverse() );
1015 xNewContainer->setRepeatDuration( mxNode->getRepeatDuration() );
1016 xNewContainer->setEndSync( mxNode->getEndSync() );
1017 xNewContainer->setRepeatCount( mxNode->getRepeatCount() );
1018 xNewContainer->setUserData( mxNode->getUserData() );
1020 mxNode = xNewContainer;
1022 Any aTarget;
1023 if( nIterateType )
1025 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
1026 xIter->setTarget(maTarget);
1027 xIter->setSubItem( nTargetSubItem );
1029 else
1031 aTarget = maTarget;
1034 Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW );
1035 Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_QUERY_THROW );
1036 while( xE->hasMoreElements() )
1038 Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY );
1039 if( xAnimate.is() )
1041 xAnimate->setTarget( aTarget );
1042 xAnimate->setSubItem( nTargetSubItem );
1047 mnIterateType = nIterateType;
1049 // if we have an iteration container, we must set its type
1050 if( mnIterateType )
1052 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
1053 xIter->setIterateType( nIterateType );
1056 checkForText();
1058 catch( Exception& )
1060 OSL_FAIL( "sd::CustomAnimationEffect::setIterateType(), Exception caught!" );
1064 void CustomAnimationEffect::setIterateInterval( double fIterateInterval )
1066 if( mfIterateInterval != fIterateInterval )
1068 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
1070 DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" );
1071 if( xIter.is() )
1073 mfIterateInterval = fIterateInterval;
1074 xIter->setIterateInterval( fIterateInterval );
1077 calculateIterateDuration();
1081 OUString CustomAnimationEffect::getPath() const
1083 OUString aPath;
1085 if( mxNode.is() ) try
1087 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
1088 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1089 while( xEnumeration->hasMoreElements() )
1091 Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
1092 if( xMotion.is() )
1094 xMotion->getPath() >>= aPath;
1095 break;
1099 catch( Exception& )
1101 OSL_FAIL("sd::CustomAnimationEffect::getPath(), exception caught!" );
1104 return aPath;
1107 void CustomAnimationEffect::setPath( const OUString& rPath )
1109 if( mxNode.is() ) try
1111 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
1112 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1113 while( xEnumeration->hasMoreElements() )
1115 Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
1116 if( xMotion.is() )
1119 MainSequenceChangeGuard aGuard( mpEffectSequence );
1120 xMotion->setPath( Any( rPath ) );
1121 break;
1125 catch( Exception& )
1127 OSL_FAIL("sd::CustomAnimationEffect::setPath(), exception caught!" );
1131 Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue )
1133 Any aProperty;
1134 if( mxNode.is() ) try
1136 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1137 if( xEnumerationAccess.is() )
1139 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1140 if( xEnumeration.is() )
1142 while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
1144 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1145 if( !xAnimate.is() )
1146 continue;
1148 if( xAnimate->getType() == nNodeType )
1150 if( xAnimate->getAttributeName() == rAttributeName )
1152 switch( eValue )
1154 case EValue::To: aProperty = xAnimate->getTo(); break;
1155 case EValue::By: aProperty = xAnimate->getBy(); break;
1163 catch( Exception& )
1165 OSL_FAIL("sd::CustomAnimationEffect::getProperty(), exception caught!" );
1168 return aProperty;
1171 bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue, const Any& rValue )
1173 bool bChanged = false;
1174 if( mxNode.is() ) try
1176 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1177 if( xEnumerationAccess.is() )
1179 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1180 if( xEnumeration.is() )
1182 while( xEnumeration->hasMoreElements() )
1184 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1185 if( !xAnimate.is() )
1186 continue;
1188 if( xAnimate->getType() == nNodeType )
1190 if( xAnimate->getAttributeName() == rAttributeName )
1192 switch( eValue )
1194 case EValue::To:
1195 if( xAnimate->getTo() != rValue )
1197 xAnimate->setTo( rValue );
1198 bChanged = true;
1200 break;
1201 case EValue::By:
1202 if( xAnimate->getTo() != rValue )
1204 xAnimate->setBy( rValue );
1205 bChanged = true;
1207 break;
1215 catch( Exception& )
1217 OSL_FAIL("sd::CustomAnimationEffect::setProperty(), exception caught!" );
1220 return bChanged;
1223 static bool implIsColorAttribute( const OUString& rAttributeName )
1225 return rAttributeName == "FillColor" || rAttributeName == "LineColor" || rAttributeName == "CharColor";
1228 Any CustomAnimationEffect::getColor( sal_Int32 nIndex )
1230 Any aColor;
1231 if( mxNode.is() ) try
1233 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1234 if( xEnumerationAccess.is() )
1236 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1237 if( xEnumeration.is() )
1239 while( xEnumeration->hasMoreElements() && !aColor.hasValue() )
1241 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1242 if( !xAnimate.is() )
1243 continue;
1245 switch( xAnimate->getType() )
1247 case AnimationNodeType::SET:
1248 case AnimationNodeType::ANIMATE:
1249 if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
1250 break;
1251 SAL_FALLTHROUGH;
1252 case AnimationNodeType::ANIMATECOLOR:
1253 Sequence<Any> aValues( xAnimate->getValues() );
1254 if( aValues.hasElements() )
1256 if( aValues.getLength() > nIndex )
1257 aColor = aValues[nIndex];
1259 else if( nIndex == 0 )
1260 aColor = xAnimate->getFrom();
1261 else
1262 aColor = xAnimate->getTo();
1268 catch( Exception& )
1270 OSL_FAIL("sd::CustomAnimationEffect::getColor(), exception caught!" );
1273 return aColor;
1276 void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor )
1278 if( mxNode.is() ) try
1280 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1281 if( xEnumerationAccess.is() )
1283 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1284 if( xEnumeration.is() )
1286 while( xEnumeration->hasMoreElements() )
1288 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1289 if( !xAnimate.is() )
1290 continue;
1292 switch( xAnimate->getType() )
1294 case AnimationNodeType::SET:
1295 case AnimationNodeType::ANIMATE:
1296 if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
1297 break;
1298 SAL_FALLTHROUGH;
1299 case AnimationNodeType::ANIMATECOLOR:
1301 Sequence<Any> aValues( xAnimate->getValues() );
1302 if( aValues.hasElements() )
1304 if( aValues.getLength() > nIndex )
1306 aValues[nIndex] = rColor;
1307 xAnimate->setValues( aValues );
1310 else if( (nIndex == 0) && xAnimate->getFrom().hasValue() )
1311 xAnimate->setFrom(rColor);
1312 else if( (nIndex == 1) && xAnimate->getTo().hasValue() )
1313 xAnimate->setTo(rColor);
1315 break;
1322 catch( Exception& )
1324 OSL_FAIL("sd::CustomAnimationEffect::setColor(), exception caught!" );
1328 Any CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType, EValue eValue )
1330 Any aProperty;
1331 if( mxNode.is() ) try
1333 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1334 if( xEnumerationAccess.is() )
1336 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1337 if( xEnumeration.is() )
1339 while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
1341 Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
1342 if( !xTransform.is() )
1343 continue;
1345 if( xTransform->getTransformType() == nTransformType )
1347 switch( eValue )
1349 case EValue::To: aProperty = xTransform->getTo(); break;
1350 case EValue::By: aProperty = xTransform->getBy(); break;
1357 catch( Exception& )
1359 OSL_FAIL("sd::CustomAnimationEffect::getTransformationProperty(), exception caught!" );
1362 return aProperty;
1365 bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue )
1367 bool bChanged = false;
1368 if( mxNode.is() ) try
1370 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1371 if( xEnumerationAccess.is() )
1373 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY );
1374 if( xEnumeration.is() )
1376 while( xEnumeration->hasMoreElements() )
1378 Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
1379 if( !xTransform.is() )
1380 continue;
1382 if( xTransform->getTransformType() == nTransformType )
1384 switch( eValue )
1386 case EValue::To:
1387 if( xTransform->getTo() != rValue )
1389 xTransform->setTo( rValue );
1390 bChanged = true;
1392 break;
1393 case EValue::By:
1394 if( xTransform->getBy() != rValue )
1396 xTransform->setBy( rValue );
1397 bChanged = true;
1399 break;
1406 catch( Exception& )
1408 OSL_FAIL("sd::CustomAnimationEffect::setTransformationProperty(), exception caught!" );
1411 return bChanged;
1414 void CustomAnimationEffect::createAudio( const css::uno::Any& rSource )
1416 DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" );
1418 if( !mxAudio.is() ) try
1420 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1421 Reference< XAudio > xAudio( Audio::create( xContext ) );
1422 xAudio->setSource( rSource );
1423 xAudio->setVolume( 1.0 );
1424 setAudio( xAudio );
1426 catch( Exception& )
1428 OSL_FAIL("sd::CustomAnimationEffect::createAudio(), exception caught!" );
1432 static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode )
1434 Reference< XCommand > xCommand;
1436 if( xRootNode.is() ) try
1438 Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW );
1439 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1440 while( !xCommand.is() && xEnumeration->hasMoreElements() )
1442 Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY );
1443 if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) )
1444 xCommand.set( xNode, UNO_QUERY_THROW );
1447 catch( Exception& )
1449 OSL_FAIL("sd::findCommandNode(), exception caught!" );
1452 return xCommand;
1455 void CustomAnimationEffect::removeAudio()
1459 Reference< XAnimationNode > xChild;
1461 if( mxAudio.is() )
1463 xChild.set( mxAudio, UNO_QUERY );
1464 mxAudio.clear();
1466 else if( mnCommand == EffectCommands::STOPAUDIO )
1468 xChild.set( findCommandNode( mxNode ), UNO_QUERY );
1469 mnCommand = 0;
1472 if( xChild.is() )
1474 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
1475 if( xContainer.is() )
1476 xContainer->removeChild( xChild );
1479 catch( Exception& )
1481 OSL_FAIL("sd::CustomAnimationEffect::removeAudio(), exception caught!" );
1486 void CustomAnimationEffect::setAudio( const Reference< css::animations::XAudio >& xAudio )
1488 if( mxAudio != xAudio ) try
1490 removeAudio();
1491 mxAudio = xAudio;
1492 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
1493 Reference< XAnimationNode > xChild( mxAudio, UNO_QUERY );
1494 if( xContainer.is() && xChild.is() )
1495 xContainer->appendChild( xChild );
1497 catch( Exception& )
1499 OSL_FAIL("sd::CustomAnimationEffect::setAudio(), exception caught!" );
1503 void CustomAnimationEffect::setStopAudio()
1505 if( mnCommand != EffectCommands::STOPAUDIO ) try
1507 if( mxAudio.is() )
1508 removeAudio();
1510 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1511 Reference< XCommand > xCommand( Command::create( xContext ) );
1513 xCommand->setCommand( EffectCommands::STOPAUDIO );
1515 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW );
1516 xContainer->appendChild( xCommand );
1518 mnCommand = EffectCommands::STOPAUDIO;
1520 catch( Exception& )
1522 OSL_FAIL("sd::CustomAnimationEffect::setStopAudio(), exception caught!" );
1526 bool CustomAnimationEffect::getStopAudio() const
1528 return mnCommand == EffectCommands::STOPAUDIO;
1531 SdrPathObj* CustomAnimationEffect::createSdrPathObjFromPath(SdrModel& rTargetModel)
1533 SdrPathObj * pPathObj = new SdrPathObj(rTargetModel, OBJ_PATHLINE);
1534 updateSdrPathObjFromPath( *pPathObj );
1535 return pPathObj;
1538 void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj& rPathObj )
1540 ::basegfx::B2DPolyPolygon aPolyPoly;
1541 if( ::basegfx::utils::importFromSvgD( aPolyPoly, getPath(), true, nullptr ) )
1543 SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() );
1544 if( pObj )
1546 SdrPage* pPage = pObj->GetPage();
1547 if( pPage )
1549 const Size aPageSize( pPage->GetSize() );
1550 aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast<double>(aPageSize.Width()), static_cast<double>(aPageSize.Height())));
1553 const ::tools::Rectangle aBoundRect( pObj->GetCurrentBoundRect() );
1554 const Point aCenter( aBoundRect.Center() );
1555 aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y()));
1559 rPathObj.SetPathPoly( aPolyPoly );
1562 void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj& rPathObj )
1564 ::basegfx::B2DPolyPolygon aPolyPoly( rPathObj.GetPathPoly() );
1566 SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() );
1567 if( pObj )
1569 ::tools::Rectangle aBoundRect(0,0,0,0);
1571 const drawinglayer::primitive2d::Primitive2DContainer xPrimitives(pObj->GetViewContact().getViewIndependentPrimitive2DContainer());
1572 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
1573 const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
1575 if(!aRange.isEmpty())
1577 aBoundRect = ::tools::Rectangle(
1578 static_cast<sal_Int32>(floor(aRange.getMinX())), static_cast<sal_Int32>(floor(aRange.getMinY())),
1579 static_cast<sal_Int32>(ceil(aRange.getMaxX())), static_cast<sal_Int32>(ceil(aRange.getMaxY())));
1582 const Point aCenter( aBoundRect.Center() );
1584 aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
1586 SdrPage* pPage = pObj->GetPage();
1587 if( pPage )
1589 const Size aPageSize( pPage->GetSize() );
1590 aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(
1591 1.0 / static_cast<double>(aPageSize.Width()), 1.0 / static_cast<double>(aPageSize.Height())));
1595 setPath( ::basegfx::utils::exportToSvgD( aPolyPoly, true, true, true) );
1598 EffectSequenceHelper::EffectSequenceHelper()
1599 : mnSequenceType( EffectNodeType::DEFAULT )
1603 EffectSequenceHelper::EffectSequenceHelper( const css::uno::Reference< css::animations::XTimeContainer >& xSequenceRoot )
1604 : mxSequenceRoot( xSequenceRoot ), mnSequenceType( EffectNodeType::DEFAULT )
1606 Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW );
1607 create( xNode );
1610 EffectSequenceHelper::~EffectSequenceHelper()
1612 reset();
1615 void EffectSequenceHelper::reset()
1617 EffectSequence::iterator aIter( maEffects.begin() );
1618 EffectSequence::iterator aEnd( maEffects.end() );
1619 if( aIter != aEnd )
1621 CustomAnimationEffectPtr pEffect = (*aIter++);
1622 pEffect->setEffectSequence(nullptr);
1624 maEffects.clear();
1627 Reference< XAnimationNode > EffectSequenceHelper::getRootNode()
1629 Reference< XAnimationNode > xRoot( mxSequenceRoot, UNO_QUERY );
1630 return xRoot;
1633 void EffectSequenceHelper::append( const CustomAnimationEffectPtr& pEffect )
1635 pEffect->setEffectSequence( this );
1636 maEffects.push_back(pEffect);
1637 rebuild();
1640 CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ )
1642 CustomAnimationEffectPtr pEffect;
1644 if( pPreset.get() )
1646 Reference< XAnimationNode > xNode( pPreset->create( "" ) );
1647 if( xNode.is() )
1649 // first, filter all only ui relevant user data
1650 std::vector< NamedValue > aNewUserData;
1651 Sequence< NamedValue > aUserData( xNode->getUserData() );
1652 sal_Int32 nLength = aUserData.getLength();
1653 const NamedValue* p = aUserData.getConstArray();
1654 bool bFilter = false;
1656 while( nLength-- )
1658 if( p->Name != "text-only" && p->Name != "preset-property" )
1660 aNewUserData.push_back( *p );
1661 bFilter = true;
1663 p++;
1666 if( bFilter )
1668 aUserData = ::comphelper::containerToSequence( aNewUserData );
1669 xNode->setUserData( aUserData );
1672 // check target, maybe we need to force it to text
1673 sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
1675 if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
1677 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1679 else if( pPreset->isTextOnly() )
1681 Reference< XShape > xShape;
1682 rTarget >>= xShape;
1683 if( xShape.is() )
1685 // that's bad, we target a shape here but the effect is only for text
1686 // so change subitem
1687 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1691 // now create effect from preset
1692 pEffect.reset( new CustomAnimationEffect( xNode ) );
1693 pEffect->setEffectSequence( this );
1694 pEffect->setTarget( rTarget );
1695 pEffect->setTargetSubItem( nSubItem );
1696 if( fDuration != -1.0 )
1697 pEffect->setDuration( fDuration );
1699 maEffects.push_back(pEffect);
1701 rebuild();
1705 DBG_ASSERT( pEffect.get(), "sd::EffectSequenceHelper::append(), failed!" );
1706 return pEffect;
1709 CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */ )
1711 CustomAnimationEffectPtr pEffect;
1713 if( fDuration <= 0.0 )
1714 fDuration = 2.0;
1718 Reference< XTimeContainer > xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1719 Reference< XAnimationNode > xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) );
1721 xAnimateMotion->setDuration( Any( fDuration ) );
1722 xAnimateMotion->setFill( AnimationFill::HOLD );
1723 xEffectContainer->appendChild( xAnimateMotion );
1725 sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
1727 if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
1728 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1730 pEffect.reset( new CustomAnimationEffect( xEffectContainer ) );
1731 pEffect->setEffectSequence( this );
1732 pEffect->setTarget( rTarget );
1733 pEffect->setTargetSubItem( nSubItem );
1734 pEffect->setNodeType( css::presentation::EffectNodeType::ON_CLICK );
1735 pEffect->setPresetClass( css::presentation::EffectPresetClass::MOTIONPATH );
1736 pEffect->setAcceleration( 0.5 );
1737 pEffect->setDecelerate( 0.5 );
1738 pEffect->setFill( AnimationFill::HOLD );
1739 pEffect->setBegin( 0.0 );
1740 pEffect->updatePathFromSdrPathObj( rPathObj );
1741 if( fDuration != -1.0 )
1742 pEffect->setDuration( fDuration );
1744 maEffects.push_back(pEffect);
1746 rebuild();
1748 catch( Exception& )
1750 OSL_FAIL( "sd::EffectSequenceHelper::append(), exception caught!" );
1753 return pEffect;
1756 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ )
1758 if( pEffect.get() && pPreset.get() ) try
1760 Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) );
1761 if( xNewNode.is() )
1763 pEffect->replaceNode( xNewNode );
1764 if( fDuration != -1.0 )
1765 pEffect->setDuration( fDuration );
1768 rebuild();
1770 catch( Exception& )
1772 OSL_FAIL( "sd::EffectSequenceHelper::replace(), exception caught!" );
1776 void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ )
1778 replace( pEffect, pPreset, "", fDuration );
1781 void EffectSequenceHelper::remove( const CustomAnimationEffectPtr& pEffect )
1783 if( pEffect.get() )
1785 pEffect->setEffectSequence( nullptr );
1786 maEffects.remove( pEffect );
1789 rebuild();
1792 void EffectSequenceHelper::rebuild()
1794 implRebuild();
1797 void EffectSequenceHelper::implRebuild()
1801 // first we delete all time containers on the first two levels
1802 Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
1803 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1804 while( xEnumeration->hasMoreElements() )
1806 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
1807 Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW );
1809 Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW );
1810 Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1811 while( xChildEnumeration->hasMoreElements() )
1813 Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW );
1814 xChildContainer->removeChild( xNode );
1817 mxSequenceRoot->removeChild( xChildNode );
1820 // second, rebuild main sequence
1821 EffectSequence::iterator aIter( maEffects.begin() );
1822 EffectSequence::iterator aEnd( maEffects.end() );
1823 if( aIter != aEnd )
1825 std::vector< sd::AfterEffectNode > aAfterEffects;
1827 CustomAnimationEffectPtr pEffect = (*aIter++);
1829 bool bFirst = true;
1832 // create a par container for the next click node and all following with and after effects
1833 Reference< XTimeContainer > xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1835 Event aEvent;
1836 if( mxEventSource.is() )
1838 aEvent.Source <<= mxEventSource;
1839 aEvent.Trigger = EventTrigger::ON_CLICK;
1841 else
1843 aEvent.Trigger = EventTrigger::ON_NEXT;
1845 aEvent.Repeat = 0;
1847 Any aBegin( makeAny( aEvent ) );
1848 if( bFirst )
1850 // if the first node is not a click action, this click container
1851 // must not have INDEFINITE begin but start at 0s
1852 bFirst = false;
1853 if( pEffect->getNodeType() != EffectNodeType::ON_CLICK )
1854 aBegin <<= 0.0;
1857 xOnClickContainer->setBegin( aBegin );
1859 mxSequenceRoot->appendChild( xOnClickContainer );
1861 double fBegin = 0.0;
1865 // create a par container for the current click or after effect node and all following with effects
1866 Reference< XTimeContainer > xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1867 xWithContainer->setBegin( makeAny( fBegin ) );
1868 xOnClickContainer->appendChild( xWithContainer );
1870 double fDuration = 0.0;
1873 Reference< XAnimationNode > xEffectNode( pEffect->getNode() );
1874 xWithContainer->appendChild( xEffectNode );
1876 if( pEffect->hasAfterEffect() )
1878 Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() );
1879 AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() );
1880 aAfterEffects.push_back( a );
1883 double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration();
1884 if( fTemp > fDuration )
1885 fDuration = fTemp;
1887 if( aIter != aEnd )
1888 pEffect = (*aIter++);
1889 else
1890 pEffect.reset();
1892 while( pEffect.get() && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) );
1894 fBegin += fDuration;
1896 while( pEffect.get() && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) );
1898 while( pEffect.get() );
1900 // process after effect nodes
1901 std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func );
1903 updateTextGroups();
1905 // reset duration, might have been altered (see below)
1906 mxSequenceRoot->setDuration( Any() );
1908 else
1910 // empty sequence, set duration to 0.0 explicitly
1911 // (otherwise, this sequence will never end)
1912 mxSequenceRoot->setDuration( makeAny(0.0) );
1915 catch( Exception& )
1917 OSL_FAIL( "sd::EffectSequenceHelper::rebuild(), exception caught!" );
1921 stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference< css::animations::XAnimationNode >& xSearchNode )
1922 : mxSearchNode( xSearchNode )
1926 bool stl_CustomAnimationEffect_search_node_predict::operator()( const CustomAnimationEffectPtr& pEffect ) const
1928 return pEffect->getNode() == mxSearchNode;
1931 /// @throws Exception
1932 static bool implFindNextContainer( Reference< XTimeContainer > const & xParent, Reference< XTimeContainer > const & xCurrent, Reference< XTimeContainer >& xNext )
1934 Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW );
1935 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() );
1936 if( xEnumeration.is() )
1938 Reference< XInterface > x;
1939 while( xEnumeration->hasMoreElements() && !xNext.is() )
1941 if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) )
1943 if( xEnumeration->hasMoreElements() )
1944 xEnumeration->nextElement() >>= xNext;
1948 return xNext.is();
1951 void stl_process_after_effect_node_func(AfterEffectNode const & rNode)
1955 if( rNode.mxNode.is() && rNode.mxMaster.is() )
1957 // set master node
1958 Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_QUERY_THROW );
1959 Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() );
1960 sal_Int32 nSize = aUserData.getLength();
1961 aUserData.realloc(nSize+1);
1962 aUserData[nSize].Name = "master-element";
1963 aUserData[nSize].Value <<= xMasterNode;
1964 rNode.mxNode->setUserData( aUserData );
1966 // insert after effect node into timeline
1967 Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW );
1969 if( !rNode.mbOnNextEffect ) // sameClick
1971 // insert the aftereffect after its effect is animated
1972 xContainer->insertAfter( rNode.mxNode, rNode.mxMaster );
1974 else // nextClick
1976 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1977 // insert the aftereffect in the next group
1979 Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW );
1980 Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW );
1982 Reference< XTimeContainer > xNextContainer;
1984 // first try if we have an after effect container
1985 if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) )
1987 Reference< XTimeContainer > xNextClickContainer;
1988 // if not, try to find the next click effect container
1989 if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) )
1991 Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW );
1992 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
1993 if( xEnumeration->hasMoreElements() )
1995 // the next container is the first child container
1996 xEnumeration->nextElement() >>= xNextContainer;
1998 else
2000 // this does not yet have a child container, create one
2001 xNextContainer.set( ParallelTimeContainer::create(xContext), UNO_QUERY_THROW );
2003 xNextContainer->setBegin( makeAny( 0.0 ) );
2004 xNextClickContainer->appendChild( xNextContainer );
2006 DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" );
2010 // if we don't have a next container, we add one to the sequence container
2011 if( !xNextContainer.is() )
2013 Reference< XTimeContainer > xNewClickContainer( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
2015 Event aEvent;
2016 aEvent.Trigger = EventTrigger::ON_NEXT;
2017 aEvent.Repeat = 0;
2018 xNewClickContainer->setBegin( makeAny( aEvent ) );
2020 xSequenceContainer->insertAfter( xNewClickContainer, xClickContainer );
2022 xNextContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
2024 DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not create container!" );
2025 if( xNextContainer.is() )
2027 xNextContainer->setBegin( makeAny( 0.0 ) );
2028 xNewClickContainer->appendChild( xNextContainer );
2032 if( xNextContainer.is() )
2034 // find begin time of first element
2035 Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW );
2036 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
2037 if( xEnumeration->hasMoreElements() )
2039 Reference< XAnimationNode > xChild;
2040 // the next container is the first child container
2041 xEnumeration->nextElement() >>= xChild;
2042 if( xChild.is() )
2044 Any aBegin( xChild->getBegin() );
2045 double fBegin = 0.0;
2046 if( (aBegin >>= fBegin) && (fBegin >= 0.0))
2047 rNode.mxNode->setBegin( aBegin );
2051 xNextContainer->appendChild( rNode.mxNode );
2056 catch( Exception& )
2058 OSL_FAIL( "ppt::stl_process_after_effect_node_func::operator(), exception caught!" );
2062 EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect )
2064 return std::find( maEffects.begin(), maEffects.end(), pEffect );
2067 CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const
2069 CustomAnimationEffectPtr pEffect;
2071 EffectSequence::const_iterator aIter( maEffects.begin() );
2072 for( ; aIter != maEffects.end(); ++aIter )
2074 if( (*aIter)->getNode() == xNode )
2076 pEffect = (*aIter);
2077 break;
2081 return pEffect;
2084 sal_Int32 EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr& xEffect ) const
2086 sal_Int32 nOffset = 0;
2088 EffectSequence::const_iterator aIter( maEffects.begin() );
2089 for( ; aIter != maEffects.end(); ++aIter, nOffset++ )
2091 if( (*aIter) == xEffect )
2092 return nOffset;
2095 return -1;
2098 CustomAnimationEffectPtr EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset ) const
2100 EffectSequence::const_iterator aIter( maEffects.begin() );
2101 while( nOffset-- && aIter != maEffects.end() )
2102 ++aIter;
2104 CustomAnimationEffectPtr pEffect;
2105 if( aIter != maEffects.end() )
2106 pEffect = (*aIter);
2108 return pEffect;
2111 bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape )
2113 bool bChanges = false;
2115 EffectSequence::iterator aIter( maEffects.begin() );
2116 while( aIter != maEffects.end() )
2118 if( (*aIter)->getTargetShape() == xShape )
2120 (*aIter)->setEffectSequence( nullptr );
2121 bChanges = true;
2122 aIter = maEffects.erase( aIter );
2124 else
2126 ++aIter;
2130 return bChanges;
2133 bool EffectSequenceHelper::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape )
2135 EffectSequence::iterator aIter( maEffects.begin() );
2136 while( aIter != maEffects.end() )
2138 if( (*aIter)->getTargetShape() == xShape )
2139 return true;
2140 ++aIter;
2143 return false;
2146 void EffectSequenceHelper::insertTextRange( const css::uno::Any& aTarget )
2148 bool bChanges = false;
2150 ParagraphTarget aParaTarget;
2151 if( !(aTarget >>= aParaTarget ) )
2152 return;
2154 EffectSequence::iterator aIter( maEffects.begin() );
2155 while( aIter != maEffects.end() )
2157 if( (*aIter)->getTargetShape() == aParaTarget.Shape )
2158 bChanges |= (*aIter)->checkForText();
2159 ++aIter;
2162 if( bChanges )
2163 rebuild();
2166 void EffectSequenceHelper::disposeTextRange( const css::uno::Any& aTarget )
2168 ParagraphTarget aParaTarget;
2169 if( !(aTarget >>= aParaTarget ) )
2170 return;
2172 bool bChanges = false;
2173 bool bErased = false;
2175 EffectSequence::iterator aIter( maEffects.begin() );
2176 while( aIter != maEffects.end() )
2178 Any aIterTarget( (*aIter)->getTarget() );
2179 if( aIterTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2181 ParagraphTarget aIterParaTarget;
2182 if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) )
2184 if( aIterParaTarget.Paragraph == aParaTarget.Paragraph )
2186 // delete this effect if it targets the disposed paragraph directly
2187 (*aIter)->setEffectSequence( nullptr );
2188 aIter = maEffects.erase( aIter );
2189 bChanges = true;
2190 bErased = true;
2192 else
2194 if( aIterParaTarget.Paragraph > aParaTarget.Paragraph )
2196 // shift all paragraphs after disposed paragraph
2197 aIterParaTarget.Paragraph--;
2198 (*aIter)->setTarget( makeAny( aIterParaTarget ) );
2203 else if( (*aIter)->getTargetShape() == aParaTarget.Shape )
2205 bChanges |= (*aIter)->checkForText();
2208 if( bErased )
2209 bErased = false;
2210 else
2211 ++aIter;
2214 if( bChanges )
2215 rebuild();
2218 CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId )
2219 : maTarget( rTarget ),
2220 mnGroupId( nGroupId )
2222 reset();
2225 void CustomAnimationTextGroup::reset()
2227 mnTextGrouping = -1;
2228 mbAnimateForm = false;
2229 mbTextReverse = false;
2230 mfGroupingAuto = -1.0;
2231 mnLastPara = -1; // used to check for TextReverse
2233 for (sal_Int8 & rn : mnDepthFlags)
2235 rn = 0;
2238 maEffects.clear();
2241 void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr const & pEffect )
2243 maEffects.push_back( pEffect );
2245 Any aTarget( pEffect->getTarget() );
2246 if( aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2248 // now look at the paragraph
2249 ParagraphTarget aParaTarget;
2250 aTarget >>= aParaTarget;
2252 if( mnLastPara != -1 )
2253 mbTextReverse = mnLastPara > aParaTarget.Paragraph;
2255 mnLastPara = aParaTarget.Paragraph;
2257 const sal_Int32 nParaDepth = pEffect->getParaDepth();
2259 // only look at the first PARA_LEVELS levels
2260 if( nParaDepth < PARA_LEVELS )
2262 // our first paragraph with this level?
2263 if( mnDepthFlags[nParaDepth] == 0 )
2265 // so set it to the first found
2266 mnDepthFlags[nParaDepth] = static_cast<sal_Int8>(pEffect->getNodeType());
2268 else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() )
2270 mnDepthFlags[nParaDepth] = -1;
2273 if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS )
2274 mfGroupingAuto = pEffect->getBegin();
2276 mnTextGrouping = PARA_LEVELS;
2277 while( (mnTextGrouping > 0)
2278 && (mnDepthFlags[mnTextGrouping - 1] <= 0) )
2279 --mnTextGrouping;
2282 else
2284 // if we have an effect with the shape as a target, we animate the background
2285 mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT;
2289 CustomAnimationTextGroupPtr EffectSequenceHelper::findGroup( sal_Int32 nGroupId )
2291 CustomAnimationTextGroupPtr aPtr;
2293 CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) );
2294 if( aIter != maGroupMap.end() )
2295 aPtr = (*aIter).second;
2297 return aPtr;
2300 void EffectSequenceHelper::updateTextGroups()
2302 maGroupMap.clear();
2304 // first create all the groups
2305 EffectSequence::iterator aIter( maEffects.begin() );
2306 const EffectSequence::iterator aEnd( maEffects.end() );
2307 while( aIter != aEnd )
2309 CustomAnimationEffectPtr pEffect( (*aIter++) );
2311 const sal_Int32 nGroupId = pEffect->getGroupId();
2313 if( nGroupId == -1 )
2314 continue; // trivial case, no group
2316 CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId );
2317 if( !pGroup.get() )
2319 pGroup.reset( new CustomAnimationTextGroup( pEffect->getTargetShape(), nGroupId ) );
2320 maGroupMap[nGroupId] = pGroup;
2323 pGroup->addEffect( pEffect );
2327 CustomAnimationTextGroupPtr EffectSequenceHelper::createTextGroup( CustomAnimationEffectPtr pEffect, sal_Int32 nTextGrouping, double fTextGroupingAuto, bool bAnimateForm, bool bTextReverse )
2329 // first finde a free group-id
2330 sal_Int32 nGroupId = 0;
2332 CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() );
2333 const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() );
2334 while( aIter != aEnd )
2336 if( (*aIter).first == nGroupId )
2338 nGroupId++;
2339 aIter = maGroupMap.begin();
2341 else
2343 ++aIter;
2347 Reference< XShape > xTarget( pEffect->getTargetShape() );
2349 CustomAnimationTextGroupPtr pTextGroup( new CustomAnimationTextGroup( xTarget, nGroupId ) );
2350 maGroupMap[nGroupId] = pTextGroup;
2352 bool bUsed = false;
2354 // do we need to target the shape?
2355 if( (nTextGrouping == 0) || bAnimateForm )
2357 sal_Int16 nSubItem;
2358 if( nTextGrouping == 0)
2359 nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT;
2360 else
2361 nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND;
2363 pEffect->setTarget( makeAny( xTarget ) );
2364 pEffect->setTargetSubItem( nSubItem );
2365 pEffect->setEffectSequence( this );
2366 pEffect->setGroupId( nGroupId );
2368 pTextGroup->addEffect( pEffect );
2369 bUsed = true;
2372 pTextGroup->mnTextGrouping = nTextGrouping;
2373 pTextGroup->mfGroupingAuto = fTextGroupingAuto;
2374 pTextGroup->mbTextReverse = bTextReverse;
2376 // now add an effect for each paragraph
2377 createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed );
2379 notify_listeners();
2381 return pTextGroup;
2384 void EffectSequenceHelper::createTextGroupParagraphEffects( const CustomAnimationTextGroupPtr& pTextGroup, const CustomAnimationEffectPtr& pEffect, bool bUsed )
2386 Reference< XShape > xTarget( pTextGroup->maTarget );
2388 sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
2389 double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
2390 bool bTextReverse = pTextGroup->mbTextReverse;
2392 // now add an effect for each paragraph
2393 if( nTextGrouping >= 0 ) try
2395 EffectSequence::iterator aInsertIter( find( pEffect ) );
2397 Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW );
2398 Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_QUERY_THROW );
2400 std::deque< sal_Int16 > aParaList;
2401 sal_Int16 nPara;
2403 // fill the list with all valid paragraphs
2404 for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ )
2406 Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY );
2407 if( xRange.is() && !xRange->getString().isEmpty() )
2409 if( bTextReverse ) // sort them
2410 aParaList.push_front( nPara );
2411 else
2412 aParaList.push_back( nPara );
2416 ParagraphTarget aTarget;
2417 aTarget.Shape = xTarget;
2419 for( const auto i : aParaList )
2421 aTarget.Paragraph = i;
2423 CustomAnimationEffectPtr pNewEffect;
2424 if( bUsed )
2426 // clone a new effect from first effect
2427 pNewEffect = pEffect->clone();
2428 ++aInsertIter;
2429 aInsertIter = maEffects.insert( aInsertIter, pNewEffect );
2431 else
2433 // reuse first effect if it's not yet used
2434 pNewEffect = pEffect;
2435 bUsed = true;
2436 aInsertIter = find( pNewEffect );
2439 // set target and group-id
2440 pNewEffect->setTarget( makeAny( aTarget ) );
2441 pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
2442 pNewEffect->setGroupId( pTextGroup->mnGroupId );
2443 pNewEffect->setEffectSequence( this );
2445 // set correct node type
2446 if( pNewEffect->getParaDepth() < nTextGrouping )
2448 if( fTextGroupingAuto == -1.0 )
2450 pNewEffect->setNodeType( EffectNodeType::ON_CLICK );
2451 pNewEffect->setBegin( 0.0 );
2453 else
2455 pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2456 pNewEffect->setBegin( fTextGroupingAuto );
2459 else
2461 pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2462 pNewEffect->setBegin( 0.0 );
2465 pTextGroup->addEffect( pNewEffect );
2467 notify_listeners();
2469 catch( Exception& )
2471 OSL_FAIL("sd::EffectSequenceHelper::createTextGroup(), exception caught!" );
2475 void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr& pTextGroup, sal_Int32 nTextGrouping )
2477 if( pTextGroup->mnTextGrouping == nTextGrouping )
2479 // first case, trivial case, do nothing
2481 else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) )
2483 // second case, we need to add new effects for each paragraph
2485 CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() );
2487 pTextGroup->mnTextGrouping = nTextGrouping;
2488 createTextGroupParagraphEffects( pTextGroup, pEffect, true );
2489 notify_listeners();
2491 else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) )
2493 // third case, we need to remove effects for each paragraph
2495 EffectSequence aEffects( pTextGroup->maEffects );
2496 pTextGroup->reset();
2498 EffectSequence::iterator aIter( aEffects.begin() );
2499 const EffectSequence::iterator aEnd( aEffects.end() );
2500 while( aIter != aEnd )
2502 CustomAnimationEffectPtr pEffect( (*aIter++) );
2504 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2505 remove( pEffect );
2506 else
2507 pTextGroup->addEffect( pEffect );
2509 notify_listeners();
2511 else
2513 // fourth case, we need to change the node types for the text nodes
2514 double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
2516 EffectSequence aEffects( pTextGroup->maEffects );
2517 pTextGroup->reset();
2519 EffectSequence::iterator aIter( aEffects.begin() );
2520 const EffectSequence::iterator aEnd( aEffects.end() );
2521 while( aIter != aEnd )
2523 CustomAnimationEffectPtr pEffect( (*aIter++) );
2525 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2527 // set correct node type
2528 if( pEffect->getParaDepth() < nTextGrouping )
2530 if( fTextGroupingAuto == -1.0 )
2532 pEffect->setNodeType( EffectNodeType::ON_CLICK );
2533 pEffect->setBegin( 0.0 );
2535 else
2537 pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2538 pEffect->setBegin( fTextGroupingAuto );
2541 else
2543 pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2544 pEffect->setBegin( 0.0 );
2548 pTextGroup->addEffect( pEffect );
2551 notify_listeners();
2555 void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr& pTextGroup, bool bAnimateForm )
2557 if( pTextGroup->mbAnimateForm == bAnimateForm )
2559 // trivial case, do nothing
2561 else
2563 EffectSequence aEffects( pTextGroup->maEffects );
2564 pTextGroup->reset();
2566 SAL_WARN_IF(aEffects.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" );
2568 if (aEffects.empty())
2569 return;
2571 EffectSequence::iterator aIter( aEffects.begin() );
2572 const EffectSequence::iterator aEnd( aEffects.end() );
2574 // first insert if we have to
2575 if( bAnimateForm )
2577 EffectSequence::iterator aInsertIter( find( *aIter ) );
2579 CustomAnimationEffectPtr pEffect;
2580 if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::cppu::UnoType<ParagraphTarget>::get() ) )
2582 // special case, only one effect and that targets whole text,
2583 // convert this to target whole shape
2584 pEffect = (*aIter++);
2585 pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE );
2587 else
2589 pEffect = (*aIter)->clone();
2590 pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) );
2591 pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND );
2592 maEffects.insert( aInsertIter, pEffect );
2595 pTextGroup->addEffect( pEffect );
2598 if( !bAnimateForm && (aEffects.size() == 1) )
2600 CustomAnimationEffectPtr pEffect( (*aIter) );
2601 pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) );
2602 pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
2603 pTextGroup->addEffect( pEffect );
2605 else
2607 // read the rest to the group again
2608 while( aIter != aEnd )
2610 CustomAnimationEffectPtr pEffect( (*aIter++) );
2612 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2614 pTextGroup->addEffect( pEffect );
2616 else
2618 DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" );
2619 remove( pEffect );
2623 notify_listeners();
2627 void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr& pTextGroup, double fTextGroupingAuto )
2629 sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
2631 EffectSequence aEffects( pTextGroup->maEffects );
2632 pTextGroup->reset();
2634 EffectSequence::iterator aIter( aEffects.begin() );
2635 const EffectSequence::iterator aEnd( aEffects.end() );
2636 while( aIter != aEnd )
2638 CustomAnimationEffectPtr pEffect( (*aIter++) );
2640 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2642 // set correct node type
2643 if( pEffect->getParaDepth() < nTextGrouping )
2645 if( fTextGroupingAuto == -1.0 )
2647 pEffect->setNodeType( EffectNodeType::ON_CLICK );
2648 pEffect->setBegin( 0.0 );
2650 else
2652 pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2653 pEffect->setBegin( fTextGroupingAuto );
2656 else
2658 pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2659 pEffect->setBegin( 0.0 );
2663 pTextGroup->addEffect( pEffect );
2666 notify_listeners();
2669 struct ImplStlTextGroupSortHelper
2671 explicit ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {};
2672 bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 );
2673 bool mbReverse;
2674 sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 );
2677 sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 )
2679 const Any aTarget(p1->getTarget());
2680 if( aTarget.hasValue() && aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2682 ParagraphTarget aParaTarget;
2683 aTarget >>= aParaTarget;
2684 return aParaTarget.Paragraph;
2686 else
2688 return mbReverse ? 0x7fffffff : -1;
2692 bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 )
2694 if( mbReverse )
2696 return getTargetParagraph( p2 ) < getTargetParagraph( p1 );
2698 else
2700 return getTargetParagraph( p1 ) < getTargetParagraph( p2 );
2704 void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr& pTextGroup, bool bTextReverse )
2706 if( pTextGroup->mbTextReverse == bTextReverse )
2708 // do nothing
2710 else
2712 std::vector< CustomAnimationEffectPtr > aSortedVector(pTextGroup->maEffects.size());
2713 std::copy( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end(), aSortedVector.begin() );
2714 ImplStlTextGroupSortHelper aSortHelper( bTextReverse );
2715 std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper );
2717 pTextGroup->reset();
2719 std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() );
2720 const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() );
2722 if( aIter != aEnd )
2724 pTextGroup->addEffect( *aIter );
2725 EffectSequence::iterator aInsertIter( find( *aIter++ ) );
2726 while( aIter != aEnd )
2728 CustomAnimationEffectPtr pEffect( (*aIter++) );
2729 maEffects.erase( find( pEffect ) );
2730 aInsertIter = maEffects.insert( ++aInsertIter, pEffect );
2731 pTextGroup->addEffect( pEffect );
2734 notify_listeners();
2738 void EffectSequenceHelper::addListener( ISequenceListener* pListener )
2740 if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() )
2741 maListeners.push_back( pListener );
2744 void EffectSequenceHelper::removeListener( ISequenceListener* pListener )
2746 maListeners.remove( pListener );
2749 struct stl_notify_listeners_func
2751 stl_notify_listeners_func() {}
2752 void operator()(ISequenceListener* pListener) { pListener->notify_change(); }
2755 void EffectSequenceHelper::notify_listeners()
2757 stl_notify_listeners_func aFunc;
2758 std::for_each( maListeners.begin(), maListeners.end(), aFunc );
2761 void EffectSequenceHelper::create( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
2763 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" );
2765 if( xNode.is() ) try
2767 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2768 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
2769 while( xEnumeration->hasMoreElements() )
2771 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2772 createEffectsequence( xChildNode );
2775 catch( Exception& )
2777 OSL_FAIL( "sd::EffectSequenceHelper::create(), exception caught!" );
2781 void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode )
2783 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" );
2785 if( xNode.is() ) try
2787 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2788 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
2789 while( xEnumeration->hasMoreElements() )
2791 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2793 createEffects( xChildNode );
2796 catch( Exception& )
2798 OSL_FAIL( "sd::EffectSequenceHelper::createEffectsequence(), exception caught!" );
2802 void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode )
2804 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" );
2806 if( xNode.is() ) try
2808 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2809 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
2810 while( xEnumeration->hasMoreElements() )
2812 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2814 switch( xChildNode->getType() )
2816 // found an effect
2817 case AnimationNodeType::PAR:
2818 case AnimationNodeType::ITERATE:
2820 CustomAnimationEffectPtr pEffect( new CustomAnimationEffect( xChildNode ) );
2822 if( pEffect->mnNodeType != -1 )
2824 pEffect->setEffectSequence( this );
2825 maEffects.push_back(pEffect);
2828 break;
2830 // found an after effect
2831 case AnimationNodeType::SET:
2832 case AnimationNodeType::ANIMATECOLOR:
2834 processAfterEffect( xChildNode );
2836 break;
2840 catch( Exception& )
2842 OSL_FAIL( "sd::EffectSequenceHelper::createEffects(), exception caught!" );
2846 void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode )
2850 Reference< XAnimationNode > xMaster;
2852 Sequence< NamedValue > aUserData( xNode->getUserData() );
2853 sal_Int32 nLength = aUserData.getLength();
2854 const NamedValue* p = aUserData.getConstArray();
2856 while( nLength-- )
2858 if ( p->Name == "master-element" )
2860 p->Value >>= xMaster;
2861 break;
2863 p++;
2866 // only process if this is a valid after effect
2867 if( xMaster.is() )
2869 CustomAnimationEffectPtr pMasterEffect;
2871 // find the master effect
2872 stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster );
2873 EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) );
2874 if( aIter != maEffects.end() )
2875 pMasterEffect = (*aIter );
2877 if( pMasterEffect.get() )
2879 pMasterEffect->setHasAfterEffect( true );
2881 // find out what kind of after effect this is
2882 if( xNode->getType() == AnimationNodeType::ANIMATECOLOR )
2884 // it's a dim
2885 Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
2886 pMasterEffect->setDimColor( xAnimate->getTo() );
2887 pMasterEffect->setAfterEffectOnNext( true );
2889 else
2891 // it's a hide
2892 pMasterEffect->setAfterEffectOnNext( xNode->getParent() != xMaster->getParent() );
2897 catch( Exception& )
2899 OSL_FAIL( "sd::EffectSequenceHelper::processAfterEffect(), exception caught!" );
2903 class AnimationChangeListener : public cppu::WeakImplHelper< XChangesListener >
2905 public:
2906 explicit AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {}
2908 virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override;
2909 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
2910 private:
2911 MainSequence* mpMainSequence;
2914 void SAL_CALL AnimationChangeListener::changesOccurred( const css::util::ChangesEvent& )
2916 if( mpMainSequence )
2917 mpMainSequence->startRecreateTimer();
2920 void SAL_CALL AnimationChangeListener::disposing( const css::lang::EventObject& )
2924 MainSequence::MainSequence()
2925 : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext()))
2926 , mbTimerMode(false)
2927 , mbRebuilding( false )
2928 , mnRebuildLockGuard( 0 )
2929 , mbPendingRebuildRequest( false )
2930 , mbIgnoreChanges( 0 )
2932 if( mxTimingRootNode.is() )
2934 Sequence< css::beans::NamedValue > aUserData
2935 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::MAIN_SEQUENCE) } };
2936 mxTimingRootNode->setUserData( aUserData );
2938 init();
2941 MainSequence::MainSequence( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
2942 : mxTimingRootNode( xNode, UNO_QUERY )
2943 , mbTimerMode( false )
2944 , mbRebuilding( false )
2945 , mnRebuildLockGuard( 0 )
2946 , mbPendingRebuildRequest( false )
2947 , mbIgnoreChanges( 0 )
2949 init();
2952 MainSequence::~MainSequence()
2954 reset();
2957 void MainSequence::init()
2959 mnSequenceType = EffectNodeType::MAIN_SEQUENCE;
2961 maTimer.SetInvokeHandler( LINK(this, MainSequence, onTimerHdl) );
2962 maTimer.SetTimeout(500);
2964 mxChangesListener.set( new AnimationChangeListener( this ) );
2966 createMainSequence();
2969 void MainSequence::reset( const css::uno::Reference< css::animations::XAnimationNode >& xTimingRootNode )
2971 reset();
2973 mxTimingRootNode.set( xTimingRootNode, UNO_QUERY );
2975 createMainSequence();
2978 Reference< css::animations::XAnimationNode > MainSequence::getRootNode()
2980 DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" );
2982 if( maTimer.IsActive() && mbTimerMode )
2984 // force a rebuild NOW if one is pending
2985 maTimer.Stop();
2986 implRebuild();
2989 return EffectSequenceHelper::getRootNode();
2992 void MainSequence::createMainSequence()
2994 if( mxTimingRootNode.is() ) try
2996 Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW );
2997 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
2998 while( xEnumeration->hasMoreElements() )
3000 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
3001 sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode );
3002 if( nNodeType == EffectNodeType::MAIN_SEQUENCE )
3004 mxSequenceRoot.set( xChildNode, UNO_QUERY );
3005 EffectSequenceHelper::create( xChildNode );
3007 else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE )
3009 Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW );
3010 InteractiveSequencePtr pIS( new InteractiveSequence( xInteractiveRoot, this ) );
3011 pIS->addListener( this );
3012 maInteractiveSequenceVector.push_back( pIS );
3016 // see if we have a mainsequence at all. if not, create one...
3017 if( !mxSequenceRoot.is() )
3019 mxSequenceRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3021 uno::Sequence< css::beans::NamedValue > aUserData
3022 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::MAIN_SEQUENCE) } };
3023 mxSequenceRoot->setUserData( aUserData );
3025 // empty sequence until now, set duration to 0.0
3026 // explicitly (otherwise, this sequence will never
3027 // end)
3028 mxSequenceRoot->setDuration( makeAny(0.0) );
3030 Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW );
3031 mxTimingRootNode->appendChild( xMainSequenceNode );
3034 updateTextGroups();
3036 notify_listeners();
3038 Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
3039 if( xNotifier.is() )
3040 xNotifier->addChangesListener( mxChangesListener );
3042 catch( Exception& )
3044 OSL_FAIL( "sd::MainSequence::create(), exception caught!" );
3045 return;
3048 DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" );
3051 void MainSequence::reset()
3053 EffectSequenceHelper::reset();
3055 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3056 interactiveSequence->reset();
3057 maInteractiveSequenceVector.clear();
3061 Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
3062 if( xNotifier.is() )
3063 xNotifier->removeChangesListener( mxChangesListener );
3065 catch( Exception& )
3071 InteractiveSequencePtr MainSequence::createInteractiveSequence( const css::uno::Reference< css::drawing::XShape >& xShape )
3073 InteractiveSequencePtr pIS;
3075 // create a new interactive sequence container
3076 Reference< XTimeContainer > xISRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3078 uno::Sequence< css::beans::NamedValue > aUserData
3079 { { "node-type", css::uno::makeAny(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE) } };
3080 xISRoot->setUserData( aUserData );
3082 Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
3083 Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
3084 xParent->appendChild( xISRoot );
3086 pIS.reset( new InteractiveSequence( xISRoot, this) );
3087 pIS->setTriggerShape( xShape );
3088 pIS->addListener( this );
3089 maInteractiveSequenceVector.push_back( pIS );
3090 return pIS;
3093 CustomAnimationEffectPtr MainSequence::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const
3095 CustomAnimationEffectPtr pEffect = EffectSequenceHelper::findEffect( xNode );
3097 if( pEffect.get() == nullptr )
3099 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3101 pEffect = interactiveSequence->findEffect( xNode );
3102 if (pEffect.get())
3103 break;
3106 return pEffect;
3109 sal_Int32 MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr& pEffect ) const
3111 sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect );
3113 if( nOffset != -1 )
3114 return nOffset;
3116 nOffset = EffectSequenceHelper::getCount();
3118 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3120 sal_Int32 nTemp = interactiveSequence->getOffsetFromEffect( pEffect );
3121 if( nTemp != -1 )
3122 return nOffset + nTemp;
3124 nOffset += interactiveSequence->getCount();
3127 return -1;
3130 CustomAnimationEffectPtr MainSequence::getEffectFromOffset( sal_Int32 nOffset ) const
3132 if( nOffset >= 0 )
3134 if( nOffset < getCount() )
3135 return EffectSequenceHelper::getEffectFromOffset( nOffset );
3137 nOffset -= getCount();
3139 auto aIter( maInteractiveSequenceVector.begin() );
3141 while( (aIter != maInteractiveSequenceVector.end()) && (nOffset > (*aIter)->getCount()) )
3142 nOffset -= (*aIter++)->getCount();
3144 if( (aIter != maInteractiveSequenceVector.end()) && (nOffset >= 0) )
3145 return (*aIter)->getEffectFromOffset( nOffset );
3148 CustomAnimationEffectPtr pEffect;
3149 return pEffect;
3152 bool MainSequence::disposeShape( const Reference< XShape >& xShape )
3154 bool bChanges = EffectSequenceHelper::disposeShape( xShape );
3156 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3158 bChanges |= iterativeSequence->disposeShape( xShape );
3161 if( bChanges )
3162 startRebuildTimer();
3164 return bChanges;
3167 bool MainSequence::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape )
3169 if( EffectSequenceHelper::hasEffect( xShape ) )
3170 return true;
3172 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3174 if( iterativeSequence->getTriggerShape() == xShape )
3175 return true;
3177 if( iterativeSequence->hasEffect( xShape ) )
3178 return true;
3181 return false;
3184 void MainSequence::insertTextRange( const css::uno::Any& aTarget )
3186 EffectSequenceHelper::insertTextRange( aTarget );
3188 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3190 iterativeSequence->insertTextRange( aTarget );
3194 void MainSequence::disposeTextRange( const css::uno::Any& aTarget )
3196 EffectSequenceHelper::disposeTextRange( aTarget );
3198 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3200 iterativeSequence->disposeTextRange( aTarget );
3204 /** callback from the sd::View when an object just left text edit mode */
3205 void MainSequence::onTextChanged( const Reference< XShape >& xShape )
3207 EffectSequenceHelper::onTextChanged( xShape );
3209 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3211 iterativeSequence->onTextChanged( xShape );
3215 void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape )
3217 bool bChanges = false;
3219 EffectSequence::iterator aIter;
3220 for( aIter = maEffects.begin(); aIter != maEffects.end(); ++aIter )
3222 if( (*aIter)->getTargetShape() == xShape )
3223 bChanges |= (*aIter)->checkForText();
3226 if( bChanges )
3227 EffectSequenceHelper::implRebuild();
3230 void MainSequence::rebuild()
3232 startRebuildTimer();
3235 void MainSequence::lockRebuilds()
3237 mnRebuildLockGuard++;
3240 void MainSequence::unlockRebuilds()
3242 DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" );
3243 if( mnRebuildLockGuard )
3244 mnRebuildLockGuard--;
3246 if( (mnRebuildLockGuard == 0) && mbPendingRebuildRequest )
3248 mbPendingRebuildRequest = false;
3249 startRebuildTimer();
3253 void MainSequence::implRebuild()
3255 if( mnRebuildLockGuard )
3257 mbPendingRebuildRequest = true;
3258 return;
3261 mbRebuilding = true;
3263 EffectSequenceHelper::implRebuild();
3265 auto aIter( maInteractiveSequenceVector.begin() );
3266 while( aIter != maInteractiveSequenceVector.end() )
3268 InteractiveSequencePtr pIS( (*aIter) );
3269 if( pIS->maEffects.empty() )
3271 // remove empty interactive sequences
3272 aIter = maInteractiveSequenceVector.erase( aIter );
3274 Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
3275 Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
3276 Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW );
3277 xParent->removeChild( xISNode );
3279 else
3281 pIS->implRebuild();
3282 ++aIter;
3286 notify_listeners();
3287 mbRebuilding = false;
3290 void MainSequence::notify_change()
3292 notify_listeners();
3295 bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const css::uno::Reference< css::drawing::XShape >& xTriggerShape )
3297 EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence();
3299 EffectSequenceHelper* pNewSequence = nullptr;
3300 if( xTriggerShape.is() )
3302 for (auto const& iteractiveSequence : maInteractiveSequenceVector)
3304 InteractiveSequencePtr pIS(iteractiveSequence);
3305 if( pIS->getTriggerShape() == xTriggerShape )
3307 pNewSequence = pIS.get();
3308 break;
3312 if( !pNewSequence )
3313 pNewSequence = createInteractiveSequence( xTriggerShape ).get();
3315 else
3317 pNewSequence = this;
3320 if( pOldSequence != pNewSequence )
3322 if( pOldSequence )
3323 pOldSequence->maEffects.remove( pEffect );
3324 if( pNewSequence )
3325 pNewSequence->maEffects.push_back( pEffect );
3326 pEffect->setEffectSequence( pNewSequence );
3327 return true;
3329 else
3331 return false;
3336 IMPL_LINK_NOARG(MainSequence, onTimerHdl, Timer *, void)
3338 if( mbTimerMode )
3340 implRebuild();
3342 else
3344 reset();
3345 createMainSequence();
3349 /** starts a timer that recreates the internal structure from the API core after 1 second */
3350 void MainSequence::startRecreateTimer()
3352 if( !mbRebuilding && (mbIgnoreChanges == 0) )
3354 mbTimerMode = false;
3355 maTimer.Start();
3359 /** starts a timer that rebuilds the API core from the internal structure after 1 second */
3360 void MainSequence::startRebuildTimer()
3362 mbTimerMode = true;
3363 maTimer.Start();
3366 InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence )
3367 : EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence )
3369 mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE;
3373 if( mxSequenceRoot.is() )
3375 Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
3376 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW );
3377 while( !mxEventSource.is() && xEnumeration->hasMoreElements() )
3379 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
3381 Event aEvent;
3382 if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) )
3383 aEvent.Source >>= mxEventSource;
3387 catch( Exception& )
3389 OSL_FAIL( "sd::InteractiveSequence::InteractiveSequence(), exception caught!" );
3390 return;
3394 void InteractiveSequence::rebuild()
3396 mpMainSequence->rebuild();
3399 void InteractiveSequence::implRebuild()
3401 EffectSequenceHelper::implRebuild();
3404 MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr& pMainSequence )
3405 : mpMainSequence( pMainSequence )
3407 if( mpMainSequence.get() )
3408 mpMainSequence->lockRebuilds();
3411 MainSequenceRebuildGuard::~MainSequenceRebuildGuard()
3413 if( mpMainSequence.get() )
3414 mpMainSequence->unlockRebuilds();
3419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */