LibreOffice Module sd (master) 1
CustomAnimationEffect.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <tools/debug.hxx>
22#include <sal/log.hxx>
23#include <com/sun/star/animations/AnimationNodeType.hpp>
24#include <com/sun/star/animations/AnimateColor.hpp>
25#include <com/sun/star/animations/AnimateMotion.hpp>
26#include <com/sun/star/animations/AnimateSet.hpp>
27#include <com/sun/star/animations/AnimationFill.hpp>
28#include <com/sun/star/animations/Audio.hpp>
29#include <com/sun/star/animations/Command.hpp>
30#include <com/sun/star/animations/Event.hpp>
31#include <com/sun/star/animations/EventTrigger.hpp>
32#include <com/sun/star/animations/IterateContainer.hpp>
33#include <com/sun/star/animations/ParallelTimeContainer.hpp>
34#include <com/sun/star/animations/SequenceTimeContainer.hpp>
35#include <com/sun/star/animations/XCommand.hpp>
36#include <com/sun/star/animations/XIterateContainer.hpp>
37#include <com/sun/star/animations/XAnimateTransform.hpp>
38#include <com/sun/star/animations/XAnimateMotion.hpp>
39#include <com/sun/star/animations/XAnimate.hpp>
40#include <com/sun/star/animations/AnimationRestart.hpp>
41#include <com/sun/star/beans/NamedValue.hpp>
42#include <com/sun/star/beans/XPropertySet.hpp>
43#include <com/sun/star/container/XEnumerationAccess.hpp>
44#include <com/sun/star/lang/XInitialization.hpp>
45#include <com/sun/star/presentation/EffectNodeType.hpp>
46#include <com/sun/star/presentation/EffectCommands.hpp>
47#include <com/sun/star/presentation/EffectPresetClass.hpp>
48#include <com/sun/star/presentation/ParagraphTarget.hpp>
49#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
50#include <com/sun/star/text/XText.hpp>
51#include <com/sun/star/util/XCloneable.hpp>
52#include <com/sun/star/util/XChangesNotifier.hpp>
55#include <com/sun/star/lang/Locale.hpp>
56#include <com/sun/star/i18n/BreakIterator.hpp>
57#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
58#include <com/sun/star/i18n/WordType.hpp>
59#include <com/sun/star/presentation/TextAnimationType.hpp>
60
65
66#include <algorithm>
67#include <deque>
68#include <numeric>
69
71
73#include <o3tl/safeint.hxx>
75#include <svx/svdopath.hxx>
76#include <svx/svdpage.hxx>
79#include <animations.hxx>
80#include <utility>
81
82using namespace ::com::sun::star;
83using namespace ::com::sun::star::uno;
84using namespace ::com::sun::star::presentation;
85using namespace ::com::sun::star::animations;
86
87using ::com::sun::star::container::XEnumerationAccess;
88using ::com::sun::star::container::XEnumeration;
89using ::com::sun::star::beans::NamedValue;
90using ::com::sun::star::container::XChild;
91using ::com::sun::star::drawing::XShape;
92using ::com::sun::star::lang::XInitialization;
93using ::com::sun::star::text::XText;
94using ::com::sun::star::text::XTextRange;
95using ::com::sun::star::beans::XPropertySet;
96using ::com::sun::star::util::XCloneable;
97using ::com::sun::star::lang::Locale;
98using ::com::sun::star::util::XChangesNotifier;
99using ::com::sun::star::util::XChangesListener;
100
101namespace sd
102{
104{
105public:
107 {
108 mpMainSequence = dynamic_cast< MainSequence* >( pSequence );
109 if( mpMainSequence == nullptr )
110 {
111 InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence );
112 if( pI )
114 }
115 DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" );
116
117 if( mpMainSequence )
119 }
120
122 {
123 if( mpMainSequence )
125 }
126
127private:
129};
130
131CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
132: mnNodeType(-1),
133 mnPresetClass(-1),
134 mnFill(AnimationFill::HOLD),
135 mfBegin(-1.0),
136 mfDuration(-1.0),
137 mfAbsoluteDuration(-1.0),
138 mnGroupId(-1),
139 mnIterateType(0),
141 mnParaDepth( -1 ),
142 mbHasText(false),
143 mfAcceleration( 1.0 ),
144 mfDecelerate( 1.0 ),
145 mbAutoReverse(false),
146 mnTargetSubItem(0),
147 mnCommand(0),
148 mpEffectSequence( nullptr ),
149 mbHasAfterEffect(false),
150 mbAfterEffectOnNextEffect(false)
151{
152 setNode( xNode );
153}
154
155void CustomAnimationEffect::setNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
156{
157 mxNode = xNode;
158 mxAudio.clear();
159 mnCommand = 0;
160
161 const Sequence< NamedValue > aUserData( mxNode->getUserData() );
162
163 for( const NamedValue& rProp : aUserData )
164 {
165 if ( rProp.Name == "node-type" )
166 {
167 rProp.Value >>= mnNodeType;
168 }
169 else if ( rProp.Name == "preset-id" )
170 {
171 rProp.Value >>= maPresetId;
172 }
173 else if ( rProp.Name == "preset-sub-type" )
174 {
175 rProp.Value >>= maPresetSubType;
176 }
177 else if ( rProp.Name == "preset-class" )
178 {
179 rProp.Value >>= mnPresetClass;
180 }
181 else if ( rProp.Name == "preset-property" )
182 {
183 rProp.Value >>= maProperty;
184 }
185 else if ( rProp.Name == "group-id" )
186 {
187 rProp.Value >>= mnGroupId;
188 }
189 }
190
191 // get effect start time
192 mxNode->getBegin() >>= mfBegin;
193
194 mfAcceleration = mxNode->getAcceleration();
195 mfDecelerate = mxNode->getDecelerate();
196 mbAutoReverse = mxNode->getAutoReverse();
197
198 mnFill = mxNode->getFill();
199
200 // get iteration data
201 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
202 if( xIter.is() )
203 {
204 mfIterateInterval = xIter->getIterateInterval();
205 mnIterateType = xIter->getIterateType();
206 maTarget = xIter->getTarget();
207 mnTargetSubItem = xIter->getSubItem();
208 }
209 else
210 {
211 mfIterateInterval = 0.0f;
212 mnIterateType = 0;
213 }
214
215 // calculate effect duration and get target shape
216 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
217 if( xEnumerationAccess.is() )
218 {
219 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
220 if( xEnumeration.is() )
221 {
222 while( xEnumeration->hasMoreElements() )
223 {
224 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
225 if( !xChildNode.is() )
226 continue;
227
228 if( xChildNode->getType() == AnimationNodeType::AUDIO )
229 {
230 mxAudio.set( xChildNode, UNO_QUERY );
231 }
232 else if( xChildNode->getType() == AnimationNodeType::COMMAND )
233 {
234 Reference< XCommand > xCommand( xChildNode, UNO_QUERY );
235 if( xCommand.is() )
236 {
237 mnCommand = xCommand->getCommand();
238 if( !maTarget.hasValue() )
239 maTarget = xCommand->getTarget();
240 }
241 }
242 else
243 {
244 double fBegin = 0.0;
245 double fDuration = 0.0;
246 xChildNode->getBegin() >>= fBegin;
247 xChildNode->getDuration() >>= fDuration;
248
249 fDuration += fBegin;
250 if( fDuration > mfDuration )
251 mfDuration = fDuration;
252
253 // no target shape yet?
254 if( !maTarget.hasValue() )
255 {
256 // go get it boys!
257 Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY );
258 if( xAnimate.is() )
259 {
260 maTarget = xAnimate->getTarget();
261 mnTargetSubItem = xAnimate->getSubItem();
262 }
263 }
264 }
265 }
266 }
267 }
268
270 double fRepeatCount = 1.0;
271 if( (mxNode->getRepeatCount()) >>= fRepeatCount )
272 mfAbsoluteDuration *= fRepeatCount;
273
274 checkForText();
275}
276
277sal_Int32 CustomAnimationEffect::getNumberOfSubitems( const Any& aTarget, sal_Int16 nIterateType )
278{
279 sal_Int32 nSubItems = 0;
280
281 try
282 {
283 // first get target text
284 sal_Int32 nOnlyPara = -1;
285
286 Reference< XText > xShape;
287 aTarget >>= xShape;
288 if( !xShape.is() )
289 {
290 ParagraphTarget aParaTarget;
291 if( aTarget >>= aParaTarget )
292 {
293 xShape.set( aParaTarget.Shape, UNO_QUERY );
294 nOnlyPara = aParaTarget.Paragraph;
295 }
296 }
297
298 // now use the break iterator to iterate over the given text
299 // and count the sub items
300
301 if( xShape.is() )
302 {
303 // TODO/LATER: Optimize this, don't create a break iterator each time
304 Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
305 Reference < i18n::XBreakIterator > xBI = i18n::BreakIterator::create(xContext);
306
307 Reference< XEnumerationAccess > xEA( xShape, UNO_QUERY_THROW );
308 Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_SET_THROW );
309 css::lang::Locale aLocale;
310 static constexpr OUStringLiteral aStrLocaleName( u"CharLocale" );
311 Reference< XTextRange > xParagraph;
312
313 sal_Int32 nPara = 0;
314 while( xEnumeration->hasMoreElements() )
315 {
316 xEnumeration->nextElement() >>= xParagraph;
317
318 // skip this if it's not the only paragraph we want to count
319 if( (nOnlyPara != -1) && (nOnlyPara != nPara ) )
320 continue;
321
322 if( nIterateType == TextAnimationType::BY_PARAGRAPH )
323 {
324 nSubItems++;
325 }
326 else
327 {
328 const OUString aText( xParagraph->getString() );
329 Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW );
330 xSet->getPropertyValue( aStrLocaleName ) >>= aLocale;
331
332 sal_Int32 nPos;
333 const sal_Int32 nEndPos = aText.getLength();
334
335 if( nIterateType == TextAnimationType::BY_WORD )
336 {
337 for( nPos = 0; nPos < nEndPos; nPos++ )
338 {
339 nPos = xBI->getWordBoundary(aText, nPos, aLocale, i18n::WordType::ANY_WORD, true).endPos;
340 nSubItems++;
341 }
342 break;
343 }
344 else
345 {
346 sal_Int32 nDone;
347 for( nPos = 0; nPos < nEndPos; nPos++ )
348 {
349 nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone);
350 nSubItems++;
351 }
352 }
353 }
354
355 if( nPara == nOnlyPara )
356 break;
357
358 nPara++;
359 }
360 }
361 }
362 catch( Exception& )
363 {
364 nSubItems = 0;
365 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" );
366 }
367
368 return nSubItems;
369}
370
372{
373}
374
376{
377 Reference< XCloneable > xCloneable( mxNode, UNO_QUERY_THROW );
378 Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW );
379 CustomAnimationEffectPtr pEffect = std::make_shared<CustomAnimationEffect>( xNode );
380 pEffect->setEffectSequence( getEffectSequence() );
381 return pEffect;
382}
383
384sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode )
385{
386 sal_Int16 nNodeType = -1;
387
388 if( xNode.is() )
389 {
390 const Sequence< NamedValue > aUserData( xNode->getUserData() );
391 if( aUserData.hasElements() )
392 {
393 const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(),
394 [](const NamedValue& rProp) { return rProp.Name == "node-type"; });
395 if (pProp != aUserData.end())
396 pProp->Value >>= nNodeType;
397 }
398 }
399
400 return nNodeType;
401}
402
403void CustomAnimationEffect::setPresetClassAndId( sal_Int16 nPresetClass, const OUString& rPresetId )
404{
405 if( mnPresetClass == nPresetClass && maPresetId == rPresetId )
406 return;
407
408 mnPresetClass = nPresetClass;
409 maPresetId = rPresetId;
410 if( !mxNode.is() )
411 return;
412
413 // first try to find a "preset-class" entry in the user data
414 // and change it
415 Sequence< NamedValue > aUserData( mxNode->getUserData() );
416 sal_Int32 nLength = aUserData.getLength();
417 bool bFoundPresetClass = false;
418 bool bFoundPresetId = false;
419 if( nLength )
420 {
421 auto [begin, end] = asNonConstRange(aUserData);
422 NamedValue* pProp = std::find_if(begin, end,
423 [](const NamedValue& rProp) { return rProp.Name == "preset-class"; });
424 if (pProp != end)
425 {
426 pProp->Value <<= mnPresetClass;
427 bFoundPresetClass = true;
428 }
429
430 pProp = std::find_if(begin, end,
431 [](const NamedValue& rProp) { return rProp.Name == "preset-id"; });
432 if (pProp != end)
433 {
434 pProp->Value <<= mnPresetClass;
435 bFoundPresetId = true;
436 }
437 }
438
439 // no "preset-class" entry inside user data, so add it
440 if( !bFoundPresetClass )
441 {
442 aUserData.realloc( nLength + 1);
443 auto& el = aUserData.getArray()[nLength];
444 el.Name = "preset-class";
445 el.Value <<= mnPresetClass;
446 ++nLength;
447 }
448
449 if( !bFoundPresetId && maPresetId.getLength() > 0 )
450 {
451 aUserData.realloc( nLength + 1);
452 auto& el = aUserData.getArray()[nLength];
453 el.Name = "preset-id";
454 el.Value <<= maPresetId;
455 }
456
457 mxNode->setUserData( aUserData );
458}
459
460void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType )
461{
462 if( mnNodeType == nNodeType )
463 return;
464
465 mnNodeType = nNodeType;
466 if( !mxNode.is() )
467 return;
468
469 // first try to find a "node-type" entry in the user data
470 // and change it
471 Sequence< NamedValue > aUserData( mxNode->getUserData() );
472 sal_Int32 nLength = aUserData.getLength();
473 bool bFound = false;
474 if( nLength )
475 {
476 auto [begin, end] = asNonConstRange(aUserData);
477 NamedValue* pProp = std::find_if(begin, end,
478 [](const NamedValue& rProp) { return rProp.Name == "node-type"; });
479 if (pProp != end)
480 {
481 pProp->Value <<= mnNodeType;
482 bFound = true;
483 }
484 }
485
486 // no "node-type" entry inside user data, so add it
487 if( !bFound )
488 {
489 aUserData.realloc( nLength + 1);
490 auto& el = aUserData.getArray()[nLength];
491 el.Name = "node-type";
492 el.Value <<= mnNodeType;
493 }
494
495 mxNode->setUserData( aUserData );
496}
497
498void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId )
499{
500 mnGroupId = nGroupId;
501 if( !mxNode.is() )
502 return;
503
504 // first try to find a "group-id" entry in the user data
505 // and change it
506 Sequence< NamedValue > aUserData( mxNode->getUserData() );
507 sal_Int32 nLength = aUserData.getLength();
508 bool bFound = false;
509 if( nLength )
510 {
511 auto [begin, end] = asNonConstRange(aUserData);
512 NamedValue* pProp = std::find_if(begin, end,
513 [](const NamedValue& rProp) { return rProp.Name == "group-id"; });
514 if (pProp != end)
515 {
516 pProp->Value <<= mnGroupId;
517 bFound = true;
518 }
519 }
520
521 // no "group-id" entry inside user data, so add it
522 if( !bFound )
523 {
524 aUserData.realloc( nLength + 1);
525 auto& el = aUserData.getArray()[nLength];
526 el.Name = "group-id";
527 el.Value <<= mnGroupId;
528 }
529
530 mxNode->setUserData( aUserData );
531}
532
536bool CustomAnimationEffect::checkForText( const std::vector<sal_Int32>* paragraphNumberingLevel )
537{
538 bool bChange = false;
539
540 Reference< XText > xText;
541
542 if( maTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
543 {
544 // calc para depth
545 ParagraphTarget aParaTarget;
546 maTarget >>= aParaTarget;
547
548 xText.set( aParaTarget.Shape, UNO_QUERY );
549
550 // get paragraph
551 if( xText.is() )
552 {
553 sal_Int32 nPara = aParaTarget.Paragraph;
554
555 bool bHasText = false;
556 sal_Int32 nParaDepth = 0;
557
558 if ( paragraphNumberingLevel )
559 {
560 bHasText = !paragraphNumberingLevel->empty();
561 if (nPara >= 0 && o3tl::make_unsigned(nPara) < paragraphNumberingLevel->size())
562 nParaDepth = paragraphNumberingLevel->at(nPara);
563 }
564 else
565 {
566 Reference< XEnumerationAccess > xEA( xText, UNO_QUERY );
567 if( xEA.is() )
568 {
569 Reference< XEnumeration > xEnumeration = xEA->createEnumeration();
570 if( xEnumeration.is() )
571 {
572 bHasText = xEnumeration->hasMoreElements();
573
574 while( xEnumeration->hasMoreElements() && nPara-- )
575 xEnumeration->nextElement();
576
577 if( xEnumeration->hasMoreElements() )
578 {
579 Reference< XPropertySet > xParaSet;
580 xEnumeration->nextElement() >>= xParaSet;
581 if( xParaSet.is() )
582 {
583 xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth;
584 }
585 }
586 }
587 }
588 }
589
590 if( bHasText )
591 {
592 bChange |= bHasText != mbHasText;
593 mbHasText = bHasText;
594
595 bChange |= nParaDepth != mnParaDepth;
596 mnParaDepth = nParaDepth;
597 }
598 }
599 }
600 else
601 {
602 maTarget >>= xText;
603 bool bHasText = xText.is() && !xText->getString().isEmpty();
604 bChange |= bHasText != mbHasText;
605 mbHasText = bHasText;
606 }
607
608 bChange |= calculateIterateDuration();
609 return bChange;
610}
611
613{
614 bool bChange = false;
615
616 // if we have an iteration, we must also calculate the
617 // 'true' container duration, that is
618 // ( ( is form animated ) ? [contained effects duration] : 0 ) +
619 // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration]
620 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
621 if( xIter.is() )
622 {
623 double fDuration = mfDuration;
624 const double fSubEffectDuration = mfDuration;
625
626 if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check
627 {
628 const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType );
629 if( nSubItems )
630 {
631 const double f = (nSubItems-1) * mfIterateInterval;
632 fDuration += f;
633 }
634 }
635
636 // if we also animate the form first, we have to add the
637 // sub effect duration to the whole effect duration
638 if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE )
639 fDuration += fSubEffectDuration;
640
641 bChange |= fDuration != mfAbsoluteDuration;
642 mfAbsoluteDuration = fDuration;
643 }
644
645 return bChange;
646}
647
648void CustomAnimationEffect::setTarget( const css::uno::Any& rTarget )
649{
650 try
651 {
653
654 // first, check special case for random node
655 Reference< XInitialization > xInit( mxNode, UNO_QUERY );
656 if( xInit.is() )
657 {
658 const Sequence< Any > aArgs( &maTarget, 1 );
659 xInit->initialize( aArgs );
660 }
661 else
662 {
663 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
664 if( xIter.is() )
665 {
666 xIter->setTarget(maTarget);
667 }
668 else
669 {
670 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
671 if( xEnumerationAccess.is() )
672 {
673 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
674 if( xEnumeration.is() )
675 {
676 while( xEnumeration->hasMoreElements() )
677 {
678 const Any aElem( xEnumeration->nextElement() );
679 Reference< XAnimate > xAnimate( aElem, UNO_QUERY );
680 if( xAnimate.is() )
681 xAnimate->setTarget( rTarget );
682 else
683 {
684 Reference< XCommand > xCommand( aElem, UNO_QUERY );
685 if( xCommand.is() )
686 xCommand->setTarget( rTarget );
687 }
688 }
689 }
690 }
691 }
692 }
693 checkForText();
694 }
695 catch( Exception& )
696 {
697 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTarget()" );
698 }
699}
700
702{
703 try
704 {
705 mnTargetSubItem = nSubItem;
706
707 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
708 if( xIter.is() )
709 {
710 xIter->setSubItem(mnTargetSubItem);
711 }
712 else
713 {
714 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
715 if( xEnumerationAccess.is() )
716 {
717 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
718 if( xEnumeration.is() )
719 {
720 while( xEnumeration->hasMoreElements() )
721 {
722 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
723 if( xAnimate.is() )
724 xAnimate->setSubItem( mnTargetSubItem );
725 }
726 }
727 }
728 }
729 }
730 catch( Exception& )
731 {
732 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTargetSubItem()" );
733 }
734}
735
737{
738 if( (mfDuration == -1.0) || (mfDuration == fDuration) )
739 return;
740
741 try
742 {
743 double fScale = fDuration / mfDuration;
744 mfDuration = fDuration;
745 double fRepeatCount = 1.0;
746 getRepeatCount() >>= fRepeatCount;
747 mfAbsoluteDuration = mfDuration * fRepeatCount;
748
749 // calculate effect duration and get target shape
750 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
751 if( xEnumerationAccess.is() )
752 {
753 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
754 if( xEnumeration.is() )
755 {
756 while( xEnumeration->hasMoreElements() )
757 {
758 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY );
759 if( !xChildNode.is() )
760 continue;
761
762 double fChildBegin = 0.0;
763 xChildNode->getBegin() >>= fChildBegin;
764 if( fChildBegin != 0.0 )
765 {
766 fChildBegin *= fScale;
767 xChildNode->setBegin( Any( fChildBegin ) );
768 }
769
770 double fChildDuration = 0.0;
771 xChildNode->getDuration() >>= fChildDuration;
772 if( fChildDuration != 0.0 )
773 {
774 fChildDuration *= fScale;
775 xChildNode->setDuration( Any( fChildDuration ) );
776 }
777 }
778 }
779 }
781 }
782 catch( Exception& )
783 {
784 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDuration()" );
785 }
786}
787
789{
790 if( mxNode.is() ) try
791 {
792 mfBegin = fBegin;
793 mxNode->setBegin( Any( fBegin ) );
794 }
795 catch( Exception& )
796 {
797 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setBegin()" );
798 }
799}
800
801void CustomAnimationEffect::setAcceleration( double fAcceleration )
802{
803 if( mxNode.is() ) try
804 {
805 mfAcceleration = fAcceleration;
806 mxNode->setAcceleration( fAcceleration );
807 }
808 catch( Exception& )
809 {
810 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAcceleration()" );
811 }
812}
813
814void CustomAnimationEffect::setDecelerate( double fDecelerate )
815{
816 if( mxNode.is() ) try
817 {
818 mfDecelerate = fDecelerate;
819 mxNode->setDecelerate( fDecelerate );
820 }
821 catch( Exception& )
822 {
823 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDecelerate()" );
824 }
825}
826
828{
829 if( mxNode.is() ) try
830 {
831 mbAutoReverse = bAutoReverse;
832 mxNode->setAutoReverse( bAutoReverse );
833 }
834 catch( Exception& )
835 {
836 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAutoReverse()" );
837 }
838}
839
840void CustomAnimationEffect::replaceNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
841{
842 sal_Int16 nNodeType = mnNodeType;
843 Any aTarget = maTarget;
844
845 sal_Int16 nFill = mnFill;
846 double fBegin = mfBegin;
847 double fDuration = mfDuration;
848 double fAcceleration = mfAcceleration;
849 double fDecelerate = mfDecelerate ;
850 bool bAutoReverse = mbAutoReverse;
851 Reference< XAudio > xAudio( mxAudio );
852 sal_Int16 nIterateType = mnIterateType;
853 double fIterateInterval = mfIterateInterval;
854 sal_Int16 nSubItem = mnTargetSubItem;
855
856 setNode( xNode );
857
858 setAudio( xAudio );
859 setNodeType( nNodeType );
860 setTarget( aTarget );
861 setTargetSubItem( nSubItem );
862 setDuration( fDuration );
863 setBegin( fBegin );
864 setFill( nFill );
865
866 setAcceleration( fAcceleration );
867 setDecelerate( fDecelerate );
868 setAutoReverse( bAutoReverse );
869
870 if( nIterateType != mnIterateType )
871 setIterateType( nIterateType );
872
873 if( mnIterateType && ( fIterateInterval != mfIterateInterval ) )
874 setIterateInterval( fIterateInterval );
875}
876
877Reference< XShape > CustomAnimationEffect::getTargetShape() const
878{
879 Reference< XShape > xShape;
880 maTarget >>= xShape;
881 if( !xShape.is() )
882 {
883 ParagraphTarget aParaTarget;
884 if( maTarget >>= aParaTarget )
885 xShape = aParaTarget.Shape;
886 }
887
888 return xShape;
889}
890
892{
893 if( mxNode.is() )
894 {
895 return mxNode->getRepeatCount();
896 }
897 else
898 {
899 Any aAny;
900 return aAny;
901 }
902}
903
905{
906 if( mxNode.is() )
907 {
908 return mxNode->getEnd();
909 }
910 else
911 {
912 Any aAny;
913 return aAny;
914 }
915}
916
917void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount )
918{
919 if( mxNode.is() )
920 {
921 mxNode->setRepeatCount( rRepeatCount );
922 double fRepeatCount = 1.0;
923 rRepeatCount >>= fRepeatCount;
924 mfAbsoluteDuration = mfDuration * fRepeatCount;
925 }
926}
927
928void CustomAnimationEffect::setEnd( const Any& rEnd )
929{
930 if( mxNode.is() )
931 mxNode->setEnd( rEnd );
932}
933
934void CustomAnimationEffect::setFill( sal_Int16 nFill )
935{
936 if (mxNode.is())
937 {
938 mnFill = nFill;
939 mxNode->setFill( nFill );
940 }
941}
942
943Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const
944{
945 DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" );
946
947 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
948
949 Reference< XAnimate > xAnimate;
950 if( maDimColor.hasValue() )
951 xAnimate = AnimateColor::create( xContext );
952 else
953 xAnimate = AnimateSet::create( xContext );
954
955 Any aTo;
956 OUString aAttributeName;
957
958 if( maDimColor.hasValue() )
959 {
960 aTo = maDimColor;
961 aAttributeName = "DimColor";
962 }
963 else
964 {
965 aTo <<= false;
966 aAttributeName = "Visibility";
967 }
968
969 Any aBegin;
970 if( !mbAfterEffectOnNextEffect ) // sameClick
971 {
972 Event aEvent;
973
974 aEvent.Source <<= getNode();
975 aEvent.Trigger = EventTrigger::END_EVENT;
976 aEvent.Repeat = 0;
977
978 aBegin <<= aEvent;
979 }
980 else
981 {
982 aBegin <<= 0.0;
983 }
984
985 xAnimate->setBegin( aBegin );
986 xAnimate->setTo( aTo );
987 xAnimate->setAttributeName( aAttributeName );
988
989 xAnimate->setDuration( Any( 0.001 ) );
990 xAnimate->setFill( AnimationFill::HOLD );
991 xAnimate->setTarget( maTarget );
992
993 return xAnimate;
994}
995
996void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType )
997{
998 if( mnIterateType == nIterateType )
999 return;
1000
1001 try
1002 {
1003 // do we need to exchange the container node?
1004 if( (mnIterateType == 0) || (nIterateType == 0) )
1005 {
1006 sal_Int16 nTargetSubItem = mnTargetSubItem;
1007
1008 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1009 Reference< XTimeContainer > xNewContainer;
1010 if(nIterateType)
1011 {
1012 xNewContainer.set( IterateContainer::create( xContext ) );
1013 }
1014 else
1015 xNewContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
1016
1017 Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW );
1018 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
1019 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1020 while( xEnumeration->hasMoreElements() )
1021 {
1022 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
1023 xOldContainer->removeChild( xChildNode );
1024 xNewContainer->appendChild( xChildNode );
1025 }
1026
1027 xNewContainer->setBegin( mxNode->getBegin() );
1028 xNewContainer->setDuration( mxNode->getDuration() );
1029 xNewContainer->setEnd( mxNode->getEnd() );
1030 xNewContainer->setEndSync( mxNode->getEndSync() );
1031 xNewContainer->setRepeatCount( mxNode->getRepeatCount() );
1032 xNewContainer->setFill( mxNode->getFill() );
1033 xNewContainer->setFillDefault( mxNode->getFillDefault() );
1034 xNewContainer->setRestart( mxNode->getRestart() );
1035 xNewContainer->setRestartDefault( mxNode->getRestartDefault() );
1036 xNewContainer->setAcceleration( mxNode->getAcceleration() );
1037 xNewContainer->setDecelerate( mxNode->getDecelerate() );
1038 xNewContainer->setAutoReverse( mxNode->getAutoReverse() );
1039 xNewContainer->setRepeatDuration( mxNode->getRepeatDuration() );
1040 xNewContainer->setEndSync( mxNode->getEndSync() );
1041 xNewContainer->setRepeatCount( mxNode->getRepeatCount() );
1042 xNewContainer->setUserData( mxNode->getUserData() );
1043
1044 mxNode = xNewContainer;
1045
1046 Any aTarget;
1047 if( nIterateType )
1048 {
1049 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
1050 xIter->setTarget(maTarget);
1051 xIter->setSubItem( nTargetSubItem );
1052 }
1053 else
1054 {
1055 aTarget = maTarget;
1056 }
1057
1058 Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW );
1059 Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_SET_THROW );
1060 while( xE->hasMoreElements() )
1061 {
1062 Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY );
1063 if( xAnimate.is() )
1064 {
1065 xAnimate->setTarget( aTarget );
1066 xAnimate->setSubItem( nTargetSubItem );
1067 }
1068 }
1069 }
1070
1071 mnIterateType = nIterateType;
1072
1073 // if we have an iteration container, we must set its type
1074 if( mnIterateType )
1075 {
1076 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW );
1077 xIter->setIterateType( nIterateType );
1078 }
1079
1080 checkForText();
1081 }
1082 catch( Exception& )
1083 {
1084 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setIterateType()" );
1085 }
1086}
1087
1088void CustomAnimationEffect::setIterateInterval( double fIterateInterval )
1089{
1090 if( mfIterateInterval == fIterateInterval )
1091 return;
1092
1093 Reference< XIterateContainer > xIter( mxNode, UNO_QUERY );
1094
1095 DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" );
1096 if( xIter.is() )
1097 {
1098 mfIterateInterval = fIterateInterval;
1099 xIter->setIterateInterval( fIterateInterval );
1100 }
1101
1103}
1104
1106{
1107 OUString aPath;
1108
1109 if( mxNode.is() ) try
1110 {
1111 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
1112 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1113 while( xEnumeration->hasMoreElements() )
1114 {
1115 Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
1116 if( xMotion.is() )
1117 {
1118 xMotion->getPath() >>= aPath;
1119 break;
1120 }
1121 }
1122 }
1123 catch( Exception& )
1124 {
1125 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getPath()" );
1126 }
1127
1128 return aPath;
1129}
1130
1131void CustomAnimationEffect::setPath( const OUString& rPath )
1132{
1133 if( !mxNode.is() )
1134 return;
1135
1136 try
1137 {
1138 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW );
1139 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1140 while( xEnumeration->hasMoreElements() )
1141 {
1142 Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY );
1143 if( xMotion.is() )
1144 {
1145
1147 xMotion->setPath( Any( rPath ) );
1148 break;
1149 }
1150 }
1151 }
1152 catch( Exception& )
1153 {
1154 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setPath()" );
1155 }
1156}
1157
1158Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue )
1159{
1160 Any aProperty;
1161 if( mxNode.is() ) try
1162 {
1163 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1164 if( xEnumerationAccess.is() )
1165 {
1166 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1167 if( xEnumeration.is() )
1168 {
1169 while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
1170 {
1171 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1172 if( !xAnimate.is() )
1173 continue;
1174
1175 if( xAnimate->getType() == nNodeType )
1176 {
1177 if( xAnimate->getAttributeName() == rAttributeName )
1178 {
1179 switch( eValue )
1180 {
1181 case EValue::To: aProperty = xAnimate->getTo(); break;
1182 case EValue::By: aProperty = xAnimate->getBy(); break;
1183 }
1184 }
1185 }
1186 }
1187 }
1188 }
1189 }
1190 catch( Exception& )
1191 {
1192 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getProperty()" );
1193 }
1194
1195 return aProperty;
1196}
1197
1198bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue, const Any& rValue )
1199{
1200 bool bChanged = false;
1201 if( mxNode.is() ) try
1202 {
1203 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1204 if( xEnumerationAccess.is() )
1205 {
1206 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1207 if( xEnumeration.is() )
1208 {
1209 while( xEnumeration->hasMoreElements() )
1210 {
1211 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1212 if( !xAnimate.is() )
1213 continue;
1214
1215 if( xAnimate->getType() == nNodeType )
1216 {
1217 if( xAnimate->getAttributeName() == rAttributeName )
1218 {
1219 switch( eValue )
1220 {
1221 case EValue::To:
1222 if( xAnimate->getTo() != rValue )
1223 {
1224 xAnimate->setTo( rValue );
1225 bChanged = true;
1226 }
1227 break;
1228 case EValue::By:
1229 if( xAnimate->getTo() != rValue )
1230 {
1231 xAnimate->setBy( rValue );
1232 bChanged = true;
1233 }
1234 break;
1235 }
1236 }
1237 }
1238 }
1239 }
1240 }
1241 }
1242 catch( Exception& )
1243 {
1244 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setProperty()" );
1245 }
1246
1247 return bChanged;
1248}
1249
1250static bool implIsColorAttribute( std::u16string_view rAttributeName )
1251{
1252 return rAttributeName == u"FillColor" || rAttributeName == u"LineColor" || rAttributeName == u"CharColor";
1253}
1254
1256{
1257 Any aColor;
1258 if( mxNode.is() ) try
1259 {
1260 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1261 if( xEnumerationAccess.is() )
1262 {
1263 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1264 if( xEnumeration.is() )
1265 {
1266 while( xEnumeration->hasMoreElements() && !aColor.hasValue() )
1267 {
1268 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1269 if( !xAnimate.is() )
1270 continue;
1271
1272 switch( xAnimate->getType() )
1273 {
1274 case AnimationNodeType::SET:
1275 case AnimationNodeType::ANIMATE:
1276 if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
1277 break;
1278 [[fallthrough]];
1279 case AnimationNodeType::ANIMATECOLOR:
1280 Sequence<Any> aValues( xAnimate->getValues() );
1281 if( aValues.hasElements() )
1282 {
1283 if( aValues.getLength() > nIndex )
1284 aColor = aValues[nIndex];
1285 }
1286 else if( nIndex == 0 )
1287 aColor = xAnimate->getFrom();
1288 else
1289 aColor = xAnimate->getTo();
1290 }
1291 }
1292 }
1293 }
1294 }
1295 catch( Exception& )
1296 {
1297 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getColor()" );
1298 }
1299
1300 return aColor;
1301}
1302
1303void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor )
1304{
1305 if( !mxNode.is() )
1306 return;
1307
1308 try
1309 {
1310 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1311 if( xEnumerationAccess.is() )
1312 {
1313 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1314 if( xEnumeration.is() )
1315 {
1316 while( xEnumeration->hasMoreElements() )
1317 {
1318 Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY );
1319 if( !xAnimate.is() )
1320 continue;
1321
1322 switch( xAnimate->getType() )
1323 {
1324 case AnimationNodeType::SET:
1325 case AnimationNodeType::ANIMATE:
1326 if( !implIsColorAttribute( xAnimate->getAttributeName() ) )
1327 break;
1328 [[fallthrough]];
1329 case AnimationNodeType::ANIMATECOLOR:
1330 {
1331 Sequence<Any> aValues( xAnimate->getValues() );
1332 if( aValues.hasElements() )
1333 {
1334 if( aValues.getLength() > nIndex )
1335 {
1336 aValues.getArray()[nIndex] = rColor;
1337 xAnimate->setValues( aValues );
1338 }
1339 }
1340 else if( (nIndex == 0) && xAnimate->getFrom().hasValue() )
1341 xAnimate->setFrom(rColor);
1342 else if( (nIndex == 1) && xAnimate->getTo().hasValue() )
1343 xAnimate->setTo(rColor);
1344 }
1345 break;
1346
1347 }
1348 }
1349 }
1350 }
1351 }
1352 catch( Exception& )
1353 {
1354 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setColor()" );
1355 }
1356}
1357
1359{
1360 Any aProperty;
1361 if( mxNode.is() ) try
1362 {
1363 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1364 if( xEnumerationAccess.is() )
1365 {
1366 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1367 if( xEnumeration.is() )
1368 {
1369 while( xEnumeration->hasMoreElements() && !aProperty.hasValue() )
1370 {
1371 Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
1372 if( !xTransform.is() )
1373 continue;
1374
1375 if( xTransform->getTransformType() == nTransformType )
1376 {
1377 switch( eValue )
1378 {
1379 case EValue::To: aProperty = xTransform->getTo(); break;
1380 case EValue::By: aProperty = xTransform->getBy(); break;
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387 catch( Exception& )
1388 {
1389 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getTransformationProperty()" );
1390 }
1391
1392 return aProperty;
1393}
1394
1395bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue )
1396{
1397 bool bChanged = false;
1398 if( mxNode.is() ) try
1399 {
1400 Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY );
1401 if( xEnumerationAccess.is() )
1402 {
1403 Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration();
1404 if( xEnumeration.is() )
1405 {
1406 while( xEnumeration->hasMoreElements() )
1407 {
1408 Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY );
1409 if( !xTransform.is() )
1410 continue;
1411
1412 if( xTransform->getTransformType() == nTransformType )
1413 {
1414 switch( eValue )
1415 {
1416 case EValue::To:
1417 if( xTransform->getTo() != rValue )
1418 {
1419 xTransform->setTo( rValue );
1420 bChanged = true;
1421 }
1422 break;
1423 case EValue::By:
1424 if( xTransform->getBy() != rValue )
1425 {
1426 xTransform->setBy( rValue );
1427 bChanged = true;
1428 }
1429 break;
1430 }
1431 }
1432 }
1433 }
1434 }
1435 }
1436 catch( Exception& )
1437 {
1438 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTransformationProperty()" );
1439 }
1440
1441 return bChanged;
1442}
1443
1444void CustomAnimationEffect::createAudio( const css::uno::Any& rSource )
1445{
1446 DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" );
1447
1448 if( mxAudio.is() )
1449 return;
1450
1451 try
1452 {
1453 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1454 Reference< XAudio > xAudio( Audio::create( xContext ) );
1455 xAudio->setSource( rSource );
1456 xAudio->setVolume( 1.0 );
1457 setAudio( xAudio );
1458 }
1459 catch( Exception& )
1460 {
1461 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::createAudio()" );
1462 }
1463}
1464
1465static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode )
1466{
1467 Reference< XCommand > xCommand;
1468
1469 if( xRootNode.is() ) try
1470 {
1471 Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW );
1472 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1473 while( !xCommand.is() && xEnumeration->hasMoreElements() )
1474 {
1475 Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY );
1476 if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) )
1477 xCommand.set( xNode, UNO_QUERY_THROW );
1478 }
1479 }
1480 catch( Exception& )
1481 {
1482 TOOLS_WARN_EXCEPTION( "sd", "sd::findCommandNode()" );
1483 }
1484
1485 return xCommand;
1486}
1487
1489{
1490 try
1491 {
1492 Reference< XAnimationNode > xChild;
1493
1494 if( mxAudio.is() )
1495 {
1496 xChild = mxAudio;
1497 mxAudio.clear();
1498 }
1499 else if( mnCommand == EffectCommands::STOPAUDIO )
1500 {
1501 xChild = findCommandNode( mxNode );
1502 mnCommand = 0;
1503 }
1504
1505 if( xChild.is() )
1506 {
1507 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
1508 if( xContainer.is() )
1509 xContainer->removeChild( xChild );
1510 }
1511 }
1512 catch( Exception& )
1513 {
1514 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::removeAudio()" );
1515 }
1516
1517}
1518
1519void CustomAnimationEffect::setAudio( const Reference< css::animations::XAudio >& xAudio )
1520{
1521 if( mxAudio == xAudio )
1522 return;
1523
1524 try
1525 {
1526 removeAudio();
1527 mxAudio = xAudio;
1528 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY );
1529 if( xContainer.is() && mxAudio.is() )
1530 xContainer->appendChild( mxAudio );
1531 }
1532 catch( Exception& )
1533 {
1534 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAudio()" );
1535 }
1536}
1537
1539{
1540 if( mnCommand == EffectCommands::STOPAUDIO )
1541 return;
1542
1543 try
1544 {
1545 if( mxAudio.is() )
1546 removeAudio();
1547
1548 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1549 Reference< XCommand > xCommand( Command::create( xContext ) );
1550
1551 xCommand->setCommand( EffectCommands::STOPAUDIO );
1552
1553 Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW );
1554 xContainer->appendChild( xCommand );
1555
1556 mnCommand = EffectCommands::STOPAUDIO;
1557 }
1558 catch( Exception& )
1559 {
1560 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setStopAudio()" );
1561 }
1562}
1563
1565{
1566 return mnCommand == EffectCommands::STOPAUDIO;
1567}
1568
1570{
1571 rtl::Reference<SdrPathObj> pPathObj = new SdrPathObj(rTargetModel, SdrObjKind::PathLine);
1572 updateSdrPathObjFromPath( *pPathObj );
1573 return pPathObj;
1574}
1575
1577{
1578 ::basegfx::B2DPolyPolygon aPolyPoly;
1579 if( ::basegfx::utils::importFromSvgD( aPolyPoly, getPath(), true, nullptr ) )
1580 {
1582 if( pObj )
1583 {
1584 SdrPage* pPage = pObj->getSdrPageFromSdrObject();
1585 if( pPage )
1586 {
1587 const Size aPageSize( pPage->GetSize() );
1588 aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast<double>(aPageSize.Width()), static_cast<double>(aPageSize.Height())));
1589 }
1590
1591 const ::tools::Rectangle aBoundRect( pObj->GetCurrentBoundRect() );
1592 const Point aCenter( aBoundRect.Center() );
1593 aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y()));
1594 }
1595 }
1596
1597 rPathObj.SetPathPoly( aPolyPoly );
1598}
1599
1601{
1602 ::basegfx::B2DPolyPolygon aPolyPoly( rPathObj.GetPathPoly() );
1603
1605 if( pObj )
1606 {
1607 ::tools::Rectangle aBoundRect(0,0,0,0);
1608
1611 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
1612 const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
1613
1614 if(!aRange.isEmpty())
1615 {
1616 aBoundRect = ::tools::Rectangle(
1617 static_cast<sal_Int32>(floor(aRange.getMinX())), static_cast<sal_Int32>(floor(aRange.getMinY())),
1618 static_cast<sal_Int32>(ceil(aRange.getMaxX())), static_cast<sal_Int32>(ceil(aRange.getMaxY())));
1619 }
1620
1621 const Point aCenter( aBoundRect.Center() );
1622
1623 aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
1624
1625 SdrPage* pPage = pObj->getSdrPageFromSdrObject();
1626 if( pPage )
1627 {
1628 const Size aPageSize( pPage->GetSize() );
1630 1.0 / static_cast<double>(aPageSize.Width()), 1.0 / static_cast<double>(aPageSize.Height())));
1631 }
1632 }
1633
1634 setPath( ::basegfx::utils::exportToSvgD( aPolyPoly, true, true, true) );
1635}
1636
1638: mnSequenceType( EffectNodeType::DEFAULT )
1639{
1640}
1641
1642EffectSequenceHelper::EffectSequenceHelper( css::uno::Reference< css::animations::XTimeContainer > xSequenceRoot )
1643: mxSequenceRoot(std::move( xSequenceRoot )), mnSequenceType( EffectNodeType::DEFAULT )
1644{
1645 Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW );
1646 create( xNode );
1647}
1648
1650{
1651 reset();
1652}
1653
1655{
1656 for( CustomAnimationEffectPtr& pEffect : maEffects )
1657 {
1658 pEffect->setEffectSequence(nullptr);
1659 }
1660 maEffects.clear();
1661}
1662
1663Reference< XAnimationNode > EffectSequenceHelper::getRootNode()
1664{
1665 return mxSequenceRoot;
1666}
1667
1669{
1670 pEffect->setEffectSequence( this );
1671 maEffects.push_back(pEffect);
1672 rebuild();
1673}
1674
1675CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ )
1676{
1678
1679 if( pPreset )
1680 {
1681 Reference< XAnimationNode > xNode( pPreset->create( "" ) );
1682 if( xNode.is() )
1683 {
1684 // first, filter all only ui relevant user data
1685 std::vector< NamedValue > aNewUserData;
1686 Sequence< NamedValue > aUserData( xNode->getUserData() );
1687
1688 std::copy_if(std::cbegin(aUserData), std::cend(aUserData), std::back_inserter(aNewUserData),
1689 [](const NamedValue& rProp) { return rProp.Name != "text-only" && rProp.Name != "preset-property"; });
1690
1691 if( !aNewUserData.empty() )
1692 {
1693 aUserData = ::comphelper::containerToSequence( aNewUserData );
1694 xNode->setUserData( aUserData );
1695 }
1696
1697 // check target, maybe we need to force it to text
1698 sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
1699
1700 if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
1701 {
1702 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1703 }
1704 else if( pPreset->isTextOnly() )
1705 {
1706 Reference< XShape > xShape;
1707 rTarget >>= xShape;
1708 if( xShape.is() )
1709 {
1710 // that's bad, we target a shape here but the effect is only for text
1711 // so change subitem
1712 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1713 }
1714 }
1715
1716 // now create effect from preset
1717 pEffect = std::make_shared<CustomAnimationEffect>( xNode );
1718 pEffect->setEffectSequence( this );
1719 pEffect->setTarget( rTarget );
1720 pEffect->setTargetSubItem( nSubItem );
1721 if( fDuration != -1.0 )
1722 pEffect->setDuration( fDuration );
1723
1724 maEffects.push_back(pEffect);
1725
1726 rebuild();
1727 }
1728 }
1729
1730 DBG_ASSERT( pEffect, "sd::EffectSequenceHelper::append(), failed!" );
1731 return pEffect;
1732}
1733
1734CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */, const OUString& rPresetId )
1735{
1737
1738 if( fDuration <= 0.0 )
1739 fDuration = 2.0;
1740
1741 try
1742 {
1743 Reference< XTimeContainer > xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1744 Reference< XAnimationNode > xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) );
1745
1746 xAnimateMotion->setDuration( Any( fDuration ) );
1747 xAnimateMotion->setFill( AnimationFill::HOLD );
1748 xEffectContainer->appendChild( xAnimateMotion );
1749
1750 sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE;
1751
1752 if( rTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
1753 nSubItem = ShapeAnimationSubType::ONLY_TEXT;
1754
1755 pEffect = std::make_shared<CustomAnimationEffect>( xEffectContainer );
1756 pEffect->setEffectSequence( this );
1757 pEffect->setTarget( rTarget );
1758 pEffect->setTargetSubItem( nSubItem );
1759 pEffect->setNodeType( css::presentation::EffectNodeType::ON_CLICK );
1760 pEffect->setPresetClassAndId( css::presentation::EffectPresetClass::MOTIONPATH, rPresetId );
1761 pEffect->setAcceleration( 0.5 );
1762 pEffect->setDecelerate( 0.5 );
1763 pEffect->setFill( AnimationFill::HOLD );
1764 pEffect->setBegin( 0.0 );
1765 pEffect->updatePathFromSdrPathObj( rPathObj );
1766 if( fDuration != -1.0 )
1767 pEffect->setDuration( fDuration );
1768
1769 maEffects.push_back(pEffect);
1770
1771 rebuild();
1772 }
1773 catch( Exception& )
1774 {
1775 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::append()" );
1776 }
1777
1778 return pEffect;
1779}
1780
1781void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ )
1782{
1783 if( !(pEffect && pPreset) )
1784 return;
1785
1786 try
1787 {
1788 Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) );
1789 if( xNewNode.is() )
1790 {
1791 pEffect->replaceNode( xNewNode );
1792 if( fDuration != -1.0 )
1793 pEffect->setDuration( fDuration );
1794 }
1795
1796 rebuild();
1797 }
1798 catch( Exception& )
1799 {
1800 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::replace()" );
1801 }
1802}
1803
1804void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ )
1805{
1806 replace( pEffect, pPreset, "", fDuration );
1807}
1808
1810{
1811 if( pEffect )
1812 {
1813 pEffect->setEffectSequence( nullptr );
1814 maEffects.remove( pEffect );
1815 }
1816
1817 rebuild();
1818}
1819
1821{
1822 if ( pEffect )
1823 {
1824 maEffects.remove( pEffect );
1825 EffectSequence::iterator aInsertIter( find( pInsertBefore ) );
1826
1827 // aInsertIter being end() is OK: pInsertBefore could be null, so put at end.
1828 maEffects.insert( aInsertIter, pEffect );
1829
1830 rebuild();
1831 }
1832}
1833
1835{
1836 implRebuild();
1837}
1838
1840{
1841 try
1842 {
1843 // first we delete all time containers on the first two levels
1844 Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
1845 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1846 while( xEnumeration->hasMoreElements() )
1847 {
1848 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
1849 Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW );
1850
1851 Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW );
1852 Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_SET_THROW );
1853 while( xChildEnumeration->hasMoreElements() )
1854 {
1855 Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW );
1856 xChildContainer->removeChild( xNode );
1857 }
1858
1859 mxSequenceRoot->removeChild( xChildNode );
1860 }
1861
1862 // second, rebuild main sequence
1863 EffectSequence::iterator aIter( maEffects.begin() );
1864 EffectSequence::iterator aEnd( maEffects.end() );
1865 if( aIter != aEnd )
1866 {
1867 std::vector< sd::AfterEffectNode > aAfterEffects;
1868
1869 CustomAnimationEffectPtr pEffect = *aIter++;
1870
1871 bool bFirst = true;
1872 do
1873 {
1874 // create a par container for the next click node and all following with and after effects
1875 Reference< XTimeContainer > xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1876
1877 Event aEvent;
1878 if( mxEventSource.is() )
1879 {
1880 aEvent.Source <<= mxEventSource;
1881 aEvent.Trigger = EventTrigger::ON_CLICK;
1882 }
1883 else
1884 {
1885 aEvent.Trigger = EventTrigger::ON_NEXT;
1886 }
1887 aEvent.Repeat = 0;
1888
1889 Any aBegin( aEvent );
1890 if( bFirst )
1891 {
1892 // if the first node is not a click action, this click container
1893 // must not have INDEFINITE begin but start at 0s
1894 bFirst = false;
1895 if( pEffect->getNodeType() != EffectNodeType::ON_CLICK )
1896 aBegin <<= 0.0;
1897 }
1898
1899 xOnClickContainer->setBegin( aBegin );
1900
1901 mxSequenceRoot->appendChild( xOnClickContainer );
1902
1903 double fBegin = 0.0;
1904
1905 do
1906 {
1907 // create a par container for the current click or after effect node and all following with effects
1908 Reference< XTimeContainer > xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
1909 xWithContainer->setBegin( Any( fBegin ) );
1910 xOnClickContainer->appendChild( xWithContainer );
1911
1912 double fDuration = 0.0;
1913 do
1914 {
1915 Reference< XAnimationNode > xEffectNode( pEffect->getNode() );
1916 xWithContainer->appendChild( xEffectNode );
1917
1918 if( pEffect->hasAfterEffect() )
1919 {
1920 Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() );
1921 AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() );
1922 aAfterEffects.push_back( a );
1923 }
1924
1925 double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration();
1926 if( fTemp > fDuration )
1927 fDuration = fTemp;
1928
1929 if( aIter != aEnd )
1930 pEffect = *aIter++;
1931 else
1932 pEffect.reset();
1933 }
1934 while( pEffect && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) );
1935
1936 fBegin += fDuration;
1937 }
1938 while( pEffect && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) );
1939 }
1940 while( pEffect );
1941
1942 // process after effect nodes
1943 std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func );
1944
1946
1947 // reset duration, might have been altered (see below)
1948 mxSequenceRoot->setDuration( Any() );
1949 }
1950 else
1951 {
1952 // empty sequence, set duration to 0.0 explicitly
1953 // (otherwise, this sequence will never end)
1954 mxSequenceRoot->setDuration( Any(0.0) );
1955 }
1956 }
1957 catch( Exception& )
1958 {
1959 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::rebuild()" );
1960 }
1961}
1962
1963stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference< css::animations::XAnimationNode >& xSearchNode )
1964: mxSearchNode( xSearchNode )
1965{
1966}
1967
1969{
1970 return pEffect->getNode() == mxSearchNode;
1971}
1972
1974static bool implFindNextContainer( Reference< XTimeContainer > const & xParent, Reference< XTimeContainer > const & xCurrent, Reference< XTimeContainer >& xNext )
1975{
1976 Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW );
1977 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() );
1978 if( xEnumeration.is() )
1979 {
1980 Reference< XInterface > x;
1981 while( xEnumeration->hasMoreElements() && !xNext.is() )
1982 {
1983 if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) )
1984 {
1985 if( xEnumeration->hasMoreElements() )
1986 xEnumeration->nextElement() >>= xNext;
1987 }
1988 }
1989 }
1990 return xNext.is();
1991}
1992
1994{
1995 try
1996 {
1997 if( rNode.mxNode.is() && rNode.mxMaster.is() )
1998 {
1999 // set master node
2000 Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_SET_THROW );
2001 Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() );
2002 sal_Int32 nSize = aUserData.getLength();
2003 aUserData.realloc(nSize+1);
2004 auto pUserData = aUserData.getArray();
2005 pUserData[nSize].Name = "master-element";
2006 pUserData[nSize].Value <<= xMasterNode;
2007 rNode.mxNode->setUserData( aUserData );
2008
2009 // insert after effect node into timeline
2010 Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW );
2011
2012 if( !rNode.mbOnNextEffect ) // sameClick
2013 {
2014 // insert the aftereffect after its effect is animated
2015 xContainer->insertAfter( rNode.mxNode, rNode.mxMaster );
2016 }
2017 else // nextClick
2018 {
2019 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
2020 // insert the aftereffect in the next group
2021
2022 Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW );
2023 Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW );
2024
2025 Reference< XTimeContainer > xNextContainer;
2026
2027 // first try if we have an after effect container
2028 if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) )
2029 {
2030 Reference< XTimeContainer > xNextClickContainer;
2031 // if not, try to find the next click effect container
2032 if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) )
2033 {
2034 Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW );
2035 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
2036 if( xEnumeration->hasMoreElements() )
2037 {
2038 // the next container is the first child container
2039 xEnumeration->nextElement() >>= xNextContainer;
2040 }
2041 else
2042 {
2043 // this does not yet have a child container, create one
2044 xNextContainer.set( ParallelTimeContainer::create(xContext), UNO_QUERY_THROW );
2045
2046 xNextContainer->setBegin( Any( 0.0 ) );
2047 xNextClickContainer->appendChild( xNextContainer );
2048 }
2049 DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" );
2050 }
2051 }
2052
2053 // if we don't have a next container, we add one to the sequence container
2054 if( !xNextContainer.is() )
2055 {
2056 Reference< XTimeContainer > xNewClickContainer( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
2057
2058 Event aEvent;
2059 aEvent.Trigger = EventTrigger::ON_NEXT;
2060 aEvent.Repeat = 0;
2061 xNewClickContainer->setBegin( Any( aEvent ) );
2062
2063 xSequenceContainer->insertAfter( xNewClickContainer, xClickContainer );
2064
2065 xNextContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW );
2066
2067 xNextContainer->setBegin( Any( 0.0 ) );
2068 xNewClickContainer->appendChild( xNextContainer );
2069 }
2070
2071 if( xNextContainer.is() )
2072 {
2073 // find begin time of first element
2074 Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW );
2075 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
2076 if( xEnumeration->hasMoreElements() )
2077 {
2078 Reference< XAnimationNode > xChild;
2079 // the next container is the first child container
2080 xEnumeration->nextElement() >>= xChild;
2081 if( xChild.is() )
2082 {
2083 Any aBegin( xChild->getBegin() );
2084 double fBegin = 0.0;
2085 if( (aBegin >>= fBegin) && (fBegin >= 0.0))
2086 rNode.mxNode->setBegin( aBegin );
2087 }
2088 }
2089
2090 xNextContainer->appendChild( rNode.mxNode );
2091 }
2092 }
2093 }
2094 }
2095 catch( Exception& )
2096 {
2097 TOOLS_WARN_EXCEPTION( "sd", "ppt::stl_process_after_effect_node_func::operator()" );
2098 }
2099}
2100
2101EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect )
2102{
2103 return std::find( maEffects.begin(), maEffects.end(), pEffect );
2104}
2105
2106CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const
2107{
2109
2110 EffectSequence::const_iterator aIter = std::find_if(maEffects.begin(), maEffects.end(),
2111 [&xNode](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getNode() == xNode; });
2112 if (aIter != maEffects.end())
2113 pEffect = *aIter;
2114
2115 return pEffect;
2116}
2117
2119{
2120 auto aIter = std::find(maEffects.begin(), maEffects.end(), xEffect);
2121 if (aIter != maEffects.end())
2122 return static_cast<sal_Int32>(std::distance(maEffects.begin(), aIter));
2123
2124 return -1;
2125}
2126
2128{
2129 EffectSequence::const_iterator aIter( maEffects.begin() );
2130 nOffset = std::min(nOffset, static_cast<sal_Int32>(maEffects.size()));
2131 std::advance(aIter, nOffset);
2132
2134 if( aIter != maEffects.end() )
2135 pEffect = *aIter;
2136
2137 return pEffect;
2138}
2139
2140bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape )
2141{
2142 bool bChanges = false;
2143
2144 EffectSequence::iterator aIter( maEffects.begin() );
2145 while( aIter != maEffects.end() )
2146 {
2147 if( (*aIter)->getTargetShape() == xShape )
2148 {
2149 (*aIter)->setEffectSequence( nullptr );
2150 bChanges = true;
2151 aIter = maEffects.erase( aIter );
2152 }
2153 else
2154 {
2155 ++aIter;
2156 }
2157 }
2158
2159 return bChanges;
2160}
2161
2162bool EffectSequenceHelper::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape )
2163{
2164 return std::any_of(maEffects.begin(), maEffects.end(),
2165 [&xShape](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getTargetShape() == xShape; });
2166}
2167
2168bool EffectSequenceHelper::getParagraphNumberingLevels( const Reference< XShape >& xShape, std::vector< sal_Int32 >& rParagraphNumberingLevel )
2169{
2170 rParagraphNumberingLevel.clear();
2171
2172 if( !hasEffect( xShape ) )
2173 return false;
2174
2175 Reference< XText > xText( xShape, UNO_QUERY );
2176 if( xText.is() )
2177 {
2178 Reference< XEnumerationAccess > xEA( xText, UNO_QUERY );
2179 if( xEA.is() )
2180 {
2181 Reference< XEnumeration > xEnumeration = xEA->createEnumeration();
2182
2183 if( xEnumeration.is() )
2184 {
2185 while( xEnumeration->hasMoreElements() )
2186 {
2187 Reference< XPropertySet > xParaSet;
2188 xEnumeration->nextElement() >>= xParaSet;
2189
2190 sal_Int32 nParaDepth = 0;
2191 if( xParaSet.is() )
2192 {
2193 xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth;
2194 }
2195
2196 rParagraphNumberingLevel.push_back( nParaDepth );
2197 }
2198 }
2199 }
2200 }
2201
2202 return true;
2203}
2204
2205void EffectSequenceHelper::insertTextRange( const css::uno::Any& aTarget )
2206{
2207 ParagraphTarget aParaTarget;
2208 if( !(aTarget >>= aParaTarget ) )
2209 return;
2210
2211 // get map [paragraph index] -> [NumberingLevel]
2212 // for following reusage inside all animation effects
2213 std::vector< sal_Int32 > paragraphNumberingLevel;
2214 std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr;
2215 if ( getParagraphNumberingLevels( aParaTarget.Shape, paragraphNumberingLevel ) )
2216 paragraphNumberingLevelParam = &paragraphNumberingLevel;
2217
2218 // update internal flags for each animation effect
2219 const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false,
2220 [&aParaTarget, &paragraphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) {
2221 bool bRes = bCheck;
2222 if (rxEffect->getTargetShape() == aParaTarget.Shape)
2223 bRes |= rxEffect->checkForText( paragraphNumberingLevelParam );
2224 return bRes;
2225 });
2226
2227 if( bChanges )
2228 rebuild();
2229}
2230
2231static bool isParagraphTargetTextEmpty( ParagraphTarget aParaTarget )
2232{
2233 // get paragraph
2234 Reference< XText > xText ( aParaTarget.Shape, UNO_QUERY );
2235 if( xText.is() )
2236 {
2237 Reference< XEnumerationAccess > xEA( xText, UNO_QUERY );
2238 if( xEA.is() )
2239 {
2240 Reference< XEnumeration > xEnumeration = xEA->createEnumeration();
2241 if( xEnumeration.is() )
2242 {
2243 // advance to the Nth paragraph
2244 sal_Int32 nPara = aParaTarget.Paragraph;
2245 while( xEnumeration->hasMoreElements() && nPara-- )
2246 xEnumeration->nextElement();
2247
2248 // get Nth paragraph's text and check if it's empty
2249 if( xEnumeration->hasMoreElements() )
2250 {
2251 Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY );
2252 if( xRange.is() )
2253 {
2254 OUString text = xRange->getString();
2255 return text.isEmpty();
2256 }
2257 }
2258 }
2259 }
2260 }
2261 return false;
2262}
2263
2264void EffectSequenceHelper::disposeTextRange( const css::uno::Any& aTarget )
2265{
2266 ParagraphTarget aParaTarget;
2267 if( !(aTarget >>= aParaTarget ) )
2268 return;
2269
2270 bool bChanges = false;
2271
2272 // building list of effects for target shape; process effects not on target shape
2273 EffectSequence aTargetParagraphEffects;
2274 for( const auto &pEffect : maEffects )
2275 {
2276 Any aIterTarget( pEffect->getTarget() );
2277 if( aIterTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2278 {
2279 ParagraphTarget aIterParaTarget;
2280 if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) )
2281 {
2282 aTargetParagraphEffects.push_back(pEffect);
2283 }
2284 }
2285 else if( pEffect->getTargetShape() == aParaTarget.Shape )
2286 {
2287 bChanges |= pEffect->checkForText();
2288 }
2289 }
2290
2291 // select effect to delete:
2292 // if paragraph before target is blank, then delete its animation effect (if any) instead
2293 ParagraphTarget aPreviousParagraph = aParaTarget;
2294 --aPreviousParagraph.Paragraph;
2295 bool bIsPreviousParagraphEmpty = isParagraphTargetTextEmpty( aPreviousParagraph );
2296 sal_Int16 anParaNumToDelete = bIsPreviousParagraphEmpty ? aPreviousParagraph.Paragraph : aParaTarget.Paragraph;
2297
2298 // update effects
2299 for( const auto &pEffect : aTargetParagraphEffects )
2300 {
2301 Any aIterTarget( pEffect->getTarget() );
2302
2303 ParagraphTarget aIterParaTarget;
2304 aIterTarget >>= aIterParaTarget;
2305
2306 // delete effect for target paragraph (may have effects in more than one text group)
2307 if( aIterParaTarget.Paragraph == anParaNumToDelete )
2308 {
2309 auto aItr = find( pEffect );
2310 DBG_ASSERT( aItr != maEffects.end(), "sd::EffectSequenceHelper::disposeTextRange(), Expected effect missing.");
2311 if( aItr != maEffects.end() )
2312 {
2313 (*aItr)->setEffectSequence( nullptr );
2314 maEffects.erase(aItr);
2315 bChanges = true;
2316 }
2317 }
2318
2319 // shift all paragraphs after disposed paragraph
2320 if( aIterParaTarget.Paragraph > anParaNumToDelete )
2321 {
2322 --aIterParaTarget.Paragraph;
2323 pEffect->setTarget( Any( aIterParaTarget ) );
2324 bChanges = true;
2325 }
2326 }
2327
2328 if( bChanges )
2329 {
2330 rebuild();
2331 }
2332}
2333
2334CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId )
2335: maTarget( rTarget ),
2336 mnGroupId( nGroupId )
2337{
2338 reset();
2339}
2340
2342{
2343 mnTextGrouping = -1;
2344 mbAnimateForm = false;
2345 mbTextReverse = false;
2346 mfGroupingAuto = -1.0;
2347 mnLastPara = -1; // used to check for TextReverse
2348
2349 for (sal_Int8 & rn : mnDepthFlags)
2350 {
2351 rn = 0;
2352 }
2353
2354 maEffects.clear();
2355}
2356
2358{
2359 maEffects.push_back( pEffect );
2360
2361 Any aTarget( pEffect->getTarget() );
2362 if( aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2363 {
2364 // now look at the paragraph
2365 ParagraphTarget aParaTarget;
2366 aTarget >>= aParaTarget;
2367
2368 if( mnLastPara != -1 )
2369 mbTextReverse = mnLastPara > aParaTarget.Paragraph;
2370
2371 mnLastPara = aParaTarget.Paragraph;
2372
2373 const sal_Int32 nParaDepth = pEffect->getParaDepth();
2374
2375 // only look at the first PARA_LEVELS levels
2376 if( nParaDepth < PARA_LEVELS )
2377 {
2378 // our first paragraph with this level?
2379 if( mnDepthFlags[nParaDepth] == 0 )
2380 {
2381 // so set it to the first found
2382 mnDepthFlags[nParaDepth] = static_cast<sal_Int8>(pEffect->getNodeType());
2383 }
2384 else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() )
2385 {
2386 mnDepthFlags[nParaDepth] = -1;
2387 }
2388
2389 if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS )
2390 mfGroupingAuto = pEffect->getBegin();
2391
2393 while( (mnTextGrouping > 0)
2394 && (mnDepthFlags[mnTextGrouping - 1] <= 0) )
2396 }
2397 }
2398 else
2399 {
2400 // if we have an effect with the shape as a target, we animate the background
2401 mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT;
2402 }
2403}
2404
2406{
2408
2409 CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) );
2410 if( aIter != maGroupMap.end() )
2411 aPtr = (*aIter).second;
2412
2413 return aPtr;
2414}
2415
2417{
2418 maGroupMap.clear();
2419
2420 // first create all the groups
2421 for( const CustomAnimationEffectPtr& pEffect : maEffects )
2422 {
2423 const sal_Int32 nGroupId = pEffect->getGroupId();
2424
2425 if( nGroupId == -1 )
2426 continue; // trivial case, no group
2427
2428 CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId );
2429 if( !pGroup )
2430 {
2431 pGroup = std::make_shared<CustomAnimationTextGroup>( pEffect->getTargetShape(), nGroupId );
2432 maGroupMap[nGroupId] = pGroup;
2433 }
2434
2435 pGroup->addEffect( pEffect );
2436 }
2437
2438 // Now that all the text groups have been cleared up and rebuilt, we need to update its
2439 // text grouping. addEffect() already make mnTextGrouping the last possible level,
2440 // so just continue to find the last level that is not EffectNodeType::WITH_PREVIOUS.
2441 for(const auto &rGroupMapItem: maGroupMap)
2442 {
2443 const CustomAnimationTextGroupPtr &pGroup = rGroupMapItem.second;
2444 while(pGroup->mnTextGrouping > 0 && pGroup->mnDepthFlags[pGroup->mnTextGrouping - 1] == EffectNodeType::WITH_PREVIOUS)
2445 --pGroup->mnTextGrouping;
2446 }
2447}
2448
2451 sal_Int32 nTextGrouping, double fTextGroupingAuto,
2452 bool bAnimateForm, bool bTextReverse)
2453{
2454 // first find a free group-id
2455 sal_Int32 nGroupId = 0;
2456
2457 CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() );
2458 const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() );
2459 while( aIter != aEnd )
2460 {
2461 if( (*aIter).first == nGroupId )
2462 {
2463 nGroupId++;
2464 aIter = maGroupMap.begin();
2465 }
2466 else
2467 {
2468 ++aIter;
2469 }
2470 }
2471
2472 Reference< XShape > xTarget( pEffect->getTargetShape() );
2473
2474 CustomAnimationTextGroupPtr pTextGroup = std::make_shared<CustomAnimationTextGroup>( xTarget, nGroupId );
2475 maGroupMap[nGroupId] = pTextGroup;
2476
2477 bool bUsed = false;
2478
2479 // do we need to target the shape?
2480 if( (nTextGrouping == 0) || bAnimateForm )
2481 {
2482 sal_Int16 nSubItem;
2483 if( nTextGrouping == 0)
2484 nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT;
2485 else
2486 nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND;
2487
2488 pEffect->setTarget( Any( xTarget ) );
2489 pEffect->setTargetSubItem( nSubItem );
2490 pEffect->setEffectSequence( this );
2491 pEffect->setGroupId( nGroupId );
2492
2493 pTextGroup->addEffect( pEffect );
2494 bUsed = true;
2495 }
2496
2497 pTextGroup->mnTextGrouping = nTextGrouping;
2498 pTextGroup->mfGroupingAuto = fTextGroupingAuto;
2499 pTextGroup->mbTextReverse = bTextReverse;
2500
2501 // now add an effect for each paragraph
2502 createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed );
2503
2505
2506 return pTextGroup;
2507}
2508
2510{
2511 Reference< XShape > xTarget( pTextGroup->maTarget );
2512
2513 sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
2514 double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
2515 bool bTextReverse = pTextGroup->mbTextReverse;
2516
2517 // now add an effect for each paragraph
2518 if( nTextGrouping < 0 )
2519 return;
2520
2521 try
2522 {
2523 EffectSequence::iterator aInsertIter( find( pEffect ) );
2524
2525 Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW );
2526 Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW );
2527
2528 std::deque< sal_Int16 > aParaList;
2529 sal_Int16 nPara;
2530
2531 // fill the list with all valid paragraphs
2532 for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ )
2533 {
2534 Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY );
2535 if( xRange.is() && !xRange->getString().isEmpty() )
2536 {
2537 if( bTextReverse ) // sort them
2538 aParaList.push_front( nPara );
2539 else
2540 aParaList.push_back( nPara );
2541 }
2542 }
2543
2544 ParagraphTarget aTarget;
2545 aTarget.Shape = xTarget;
2546
2547 for( const auto i : aParaList )
2548 {
2549 aTarget.Paragraph = i;
2550
2551 CustomAnimationEffectPtr pNewEffect;
2552 if( bUsed )
2553 {
2554 // clone a new effect from first effect
2555 pNewEffect = pEffect->clone();
2556 ++aInsertIter;
2557 aInsertIter = maEffects.insert( aInsertIter, pNewEffect );
2558 }
2559 else
2560 {
2561 // reuse first effect if it's not yet used
2562 pNewEffect = pEffect;
2563 bUsed = true;
2564 aInsertIter = find( pNewEffect );
2565 }
2566
2567 // set target and group-id
2568 pNewEffect->setTarget( Any( aTarget ) );
2569 pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
2570 pNewEffect->setGroupId( pTextGroup->mnGroupId );
2571 pNewEffect->setEffectSequence( this );
2572
2573 // set correct node type
2574 if( pNewEffect->getParaDepth() < nTextGrouping )
2575 {
2576 if( fTextGroupingAuto == -1.0 )
2577 {
2578 pNewEffect->setNodeType( EffectNodeType::ON_CLICK );
2579 pNewEffect->setBegin( 0.0 );
2580 }
2581 else
2582 {
2583 pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2584 pNewEffect->setBegin( fTextGroupingAuto );
2585 }
2586 }
2587 else
2588 {
2589 pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2590 pNewEffect->setBegin( 0.0 );
2591 }
2592
2593 pTextGroup->addEffect( pNewEffect );
2594 }
2596 }
2597 catch( Exception& )
2598 {
2599 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createTextGroup()" );
2600 }
2601}
2602
2603void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr& pTextGroup, sal_Int32 nTextGrouping )
2604{
2605 if( pTextGroup->mnTextGrouping == nTextGrouping )
2606 {
2607 // first case, trivial case, do nothing
2608 }
2609 else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) )
2610 {
2611 // second case, we need to add new effects for each paragraph
2612
2613 CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() );
2614
2615 pTextGroup->mnTextGrouping = nTextGrouping;
2616 createTextGroupParagraphEffects( pTextGroup, pEffect, true );
2618 }
2619 else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) )
2620 {
2621 // third case, we need to remove effects for each paragraph
2622
2623 EffectSequence aEffects( pTextGroup->maEffects );
2624 pTextGroup->reset();
2625
2626 for( const CustomAnimationEffectPtr& pEffect : aEffects )
2627 {
2628 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2629 remove( pEffect );
2630 else
2631 pTextGroup->addEffect( pEffect );
2632 }
2634 }
2635 else
2636 {
2637 // fourth case, we need to change the node types for the text nodes
2638 double fTextGroupingAuto = pTextGroup->mfGroupingAuto;
2639
2640 EffectSequence aEffects( pTextGroup->maEffects );
2641 pTextGroup->reset();
2642
2643 for( CustomAnimationEffectPtr& pEffect : aEffects )
2644 {
2645 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2646 {
2647 // set correct node type
2648 if( pEffect->getParaDepth() < nTextGrouping )
2649 {
2650 if( fTextGroupingAuto == -1.0 )
2651 {
2652 pEffect->setNodeType( EffectNodeType::ON_CLICK );
2653 pEffect->setBegin( 0.0 );
2654 }
2655 else
2656 {
2657 pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2658 pEffect->setBegin( fTextGroupingAuto );
2659 }
2660 }
2661 else
2662 {
2663 pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2664 pEffect->setBegin( 0.0 );
2665 }
2666 }
2667
2668 pTextGroup->addEffect( pEffect );
2669
2670 }
2672 }
2673}
2674
2675void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr& pTextGroup, bool bAnimateForm )
2676{
2677 if( pTextGroup->mbAnimateForm == bAnimateForm )
2678 {
2679 // trivial case, do nothing
2680 }
2681 else
2682 {
2683 EffectSequence aEffects( pTextGroup->maEffects );
2684 pTextGroup->reset();
2685
2686 SAL_WARN_IF(aEffects.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" );
2687
2688 if (aEffects.empty())
2689 return;
2690
2691 EffectSequence::iterator aIter( aEffects.begin() );
2692 const EffectSequence::iterator aEnd( aEffects.end() );
2693
2694 // first insert if we have to
2695 if( bAnimateForm )
2696 {
2697 EffectSequence::iterator aInsertIter( find( *aIter ) );
2698
2700 if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::cppu::UnoType<ParagraphTarget>::get() ) )
2701 {
2702 // special case, only one effect and that targets whole text,
2703 // convert this to target whole shape
2704 pEffect = *aIter++;
2705 pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE );
2706 }
2707 else
2708 {
2709 pEffect = (*aIter)->clone();
2710 pEffect->setTarget( Any( (*aIter)->getTargetShape() ) );
2711 pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND );
2712 maEffects.insert( aInsertIter, pEffect );
2713 }
2714
2715 pTextGroup->addEffect( pEffect );
2716 }
2717
2718 if( !bAnimateForm && (aEffects.size() == 1) )
2719 {
2720 CustomAnimationEffectPtr pEffect( *aIter );
2721 pEffect->setTarget( Any( (*aIter)->getTargetShape() ) );
2722 pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT );
2723 pTextGroup->addEffect( pEffect );
2724 }
2725 else
2726 {
2727 // read the rest to the group again
2728 while( aIter != aEnd )
2729 {
2730 CustomAnimationEffectPtr pEffect( *aIter++ );
2731
2732 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2733 {
2734 pTextGroup->addEffect( pEffect );
2735 }
2736 else
2737 {
2738 DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" );
2739 remove( pEffect );
2740 }
2741 }
2742 }
2744 }
2745}
2746
2747void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr& pTextGroup, double fTextGroupingAuto )
2748{
2749 sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping;
2750
2751 EffectSequence aEffects( pTextGroup->maEffects );
2752 pTextGroup->reset();
2753
2754 for( CustomAnimationEffectPtr& pEffect : aEffects )
2755 {
2756 if( pEffect->getTarget().getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2757 {
2758 // set correct node type
2759 if( pEffect->getParaDepth() < nTextGrouping )
2760 {
2761 if( fTextGroupingAuto == -1.0 )
2762 {
2763 pEffect->setNodeType( EffectNodeType::ON_CLICK );
2764 pEffect->setBegin( 0.0 );
2765 }
2766 else
2767 {
2768 pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS );
2769 pEffect->setBegin( fTextGroupingAuto );
2770 }
2771 }
2772 else
2773 {
2774 pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS );
2775 pEffect->setBegin( 0.0 );
2776 }
2777 }
2778
2779 pTextGroup->addEffect( pEffect );
2780
2781 }
2783}
2784
2785namespace {
2786
2787struct ImplStlTextGroupSortHelper
2788{
2789 explicit ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {};
2790 bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 );
2791 bool mbReverse;
2792 sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 );
2793};
2794
2795}
2796
2797sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 )
2798{
2799 const Any aTarget(p1->getTarget());
2800 if( aTarget.hasValue() && aTarget.getValueType() == ::cppu::UnoType<ParagraphTarget>::get() )
2801 {
2802 ParagraphTarget aParaTarget;
2803 aTarget >>= aParaTarget;
2804 return aParaTarget.Paragraph;
2805 }
2806 else
2807 {
2808 return mbReverse ? 0x7fffffff : -1;
2809 }
2810}
2811
2812bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 )
2813{
2814 if( mbReverse )
2815 {
2816 return getTargetParagraph( p2 ) < getTargetParagraph( p1 );
2817 }
2818 else
2819 {
2820 return getTargetParagraph( p1 ) < getTargetParagraph( p2 );
2821 }
2822}
2823
2824void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr& pTextGroup, bool bTextReverse )
2825{
2826 if( pTextGroup->mbTextReverse == bTextReverse )
2827 {
2828 // do nothing
2829 }
2830 else
2831 {
2832 std::vector< CustomAnimationEffectPtr > aSortedVector( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end() );
2833 ImplStlTextGroupSortHelper aSortHelper( bTextReverse );
2834 std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper );
2835
2836 pTextGroup->reset();
2837
2838 std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() );
2839 const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() );
2840
2841 if( aIter != aEnd )
2842 {
2843 pTextGroup->addEffect( *aIter );
2844 EffectSequence::iterator aInsertIter( find( *aIter++ ) );
2845 while( aIter != aEnd )
2846 {
2847 CustomAnimationEffectPtr pEffect( *aIter++ );
2848 maEffects.erase( find( pEffect ) );
2849 aInsertIter = maEffects.insert( ++aInsertIter, pEffect );
2850 pTextGroup->addEffect( pEffect );
2851 }
2852 }
2854 }
2855}
2856
2858{
2859 if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() )
2860 maListeners.push_back( pListener );
2861}
2862
2864{
2865 maListeners.remove( pListener );
2866}
2867
2868namespace {
2869
2870struct stl_notify_listeners_func
2871{
2872 stl_notify_listeners_func() {}
2873 void operator()(ISequenceListener* pListener) { pListener->notify_change(); }
2874};
2875
2876}
2877
2879{
2880 stl_notify_listeners_func aFunc;
2881 std::for_each( maListeners.begin(), maListeners.end(), aFunc );
2882}
2883
2884void EffectSequenceHelper::create( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
2885{
2886 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" );
2887
2888 if( !xNode.is() )
2889 return;
2890
2891 try
2892 {
2893 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2894 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
2895 while( xEnumeration->hasMoreElements() )
2896 {
2897 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2898 createEffectsequence( xChildNode );
2899 }
2900 }
2901 catch( Exception& )
2902 {
2903 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::create()" );
2904 }
2905}
2906
2907void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode )
2908{
2909 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" );
2910
2911 if( !xNode.is() )
2912 return;
2913
2914 try
2915 {
2916 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2917 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
2918 while( xEnumeration->hasMoreElements() )
2919 {
2920 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2921
2922 createEffects( xChildNode );
2923 }
2924 }
2925 catch( Exception& )
2926 {
2927 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffectsequence()" );
2928 }
2929}
2930
2931void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode )
2932{
2933 DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" );
2934
2935 if( !xNode.is() )
2936 return;
2937
2938 try
2939 {
2940 Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW );
2941 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
2942 while( xEnumeration->hasMoreElements() )
2943 {
2944 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
2945
2946 switch( xChildNode->getType() )
2947 {
2948 // found an effect
2949 case AnimationNodeType::PAR:
2950 case AnimationNodeType::ITERATE:
2951 {
2952 CustomAnimationEffectPtr pEffect = std::make_shared<CustomAnimationEffect>( xChildNode );
2953
2954 if( pEffect->mnNodeType != -1 )
2955 {
2956 pEffect->setEffectSequence( this );
2957 maEffects.push_back(pEffect);
2958 }
2959 }
2960 break;
2961
2962 // found an after effect
2963 case AnimationNodeType::SET:
2964 case AnimationNodeType::ANIMATECOLOR:
2965 {
2966 processAfterEffect( xChildNode );
2967 }
2968 break;
2969 }
2970 }
2971 }
2972 catch( Exception& )
2973 {
2974 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffects()" );
2975 }
2976}
2977
2978void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode )
2979{
2980 try
2981 {
2982 Reference< XAnimationNode > xMaster;
2983
2984 const Sequence< NamedValue > aUserData( xNode->getUserData() );
2985 const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(),
2986 [](const NamedValue& rProp) { return rProp.Name == "master-element"; });
2987
2988 if (pProp != aUserData.end())
2989 pProp->Value >>= xMaster;
2990
2991 // only process if this is a valid after effect
2992 if( xMaster.is() )
2993 {
2994 CustomAnimationEffectPtr pMasterEffect;
2995
2996 // find the master effect
2997 stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster );
2998 EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) );
2999 if( aIter != maEffects.end() )
3000 pMasterEffect = *aIter;
3001
3002 if( pMasterEffect )
3003 {
3004 pMasterEffect->setHasAfterEffect( true );
3005
3006 // find out what kind of after effect this is
3007 if( xNode->getType() == AnimationNodeType::ANIMATECOLOR )
3008 {
3009 // it's a dim
3010 Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW );
3011 pMasterEffect->setDimColor( xAnimate->getTo() );
3012 pMasterEffect->setAfterEffectOnNext( true );
3013 }
3014 else
3015 {
3016 // it's a hide
3017 pMasterEffect->setAfterEffectOnNext( xNode->getParent() != xMaster->getParent() );
3018 }
3019 }
3020 }
3021 }
3022 catch( Exception& )
3023 {
3024 TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::processAfterEffect()" );
3025 }
3026}
3027
3028namespace {
3029
3030class AnimationChangeListener : public cppu::WeakImplHelper< XChangesListener >
3031{
3032public:
3033 explicit AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {}
3034
3035 virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override;
3036 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
3037private:
3038 MainSequence* mpMainSequence;
3039};
3040
3041}
3042
3043void SAL_CALL AnimationChangeListener::changesOccurred( const css::util::ChangesEvent& )
3044{
3045 if( mpMainSequence )
3046 mpMainSequence->startRecreateTimer();
3047}
3048
3049void SAL_CALL AnimationChangeListener::disposing( const css::lang::EventObject& )
3050{
3051}
3052
3054 : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext()))
3055 , maTimer("sd MainSequence maTimer")
3056 , mbTimerMode(false)
3057 , mbRebuilding( false )
3058 , mnRebuildLockGuard( 0 )
3059 , mbPendingRebuildRequest( false )
3060 , mbIgnoreChanges( 0 )
3061{
3062 if( mxTimingRootNode.is() )
3063 {
3064 Sequence< css::beans::NamedValue > aUserData
3065 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } };
3066 mxTimingRootNode->setUserData( aUserData );
3067 }
3068 init();
3069}
3070
3071MainSequence::MainSequence( const css::uno::Reference< css::animations::XAnimationNode >& xNode )
3072 : mxTimingRootNode( xNode, UNO_QUERY )
3073 , maTimer("sd MainSequence maTimer")
3074 , mbTimerMode( false )
3075 , mbRebuilding( false )
3076 , mnRebuildLockGuard( 0 )
3077 , mbPendingRebuildRequest( false )
3078 , mbIgnoreChanges( 0 )
3079{
3080 init();
3081}
3082
3084{
3085 reset();
3086}
3087
3089{
3090 mnSequenceType = EffectNodeType::MAIN_SEQUENCE;
3091
3092 maTimer.SetInvokeHandler( LINK(this, MainSequence, onTimerHdl) );
3093 maTimer.SetTimeout(50);
3094
3095 mxChangesListener.set( new AnimationChangeListener( this ) );
3096
3098}
3099
3100void MainSequence::reset( const css::uno::Reference< css::animations::XAnimationNode >& xTimingRootNode )
3101{
3102 reset();
3103
3104 mxTimingRootNode.set( xTimingRootNode, UNO_QUERY );
3105
3107}
3108
3109Reference< css::animations::XAnimationNode > MainSequence::getRootNode()
3110{
3111 DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" );
3112
3113 if( maTimer.IsActive() && mbTimerMode )
3114 {
3115 // force a rebuild NOW if one is pending
3116 maTimer.Stop();
3117 implRebuild();
3118 }
3119
3121}
3122
3124{
3125 if( mxTimingRootNode.is() ) try
3126 {
3127 Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW );
3128 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
3129 while( xEnumeration->hasMoreElements() )
3130 {
3131 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
3132 sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode );
3133 if( nNodeType == EffectNodeType::MAIN_SEQUENCE )
3134 {
3135 mxSequenceRoot.set( xChildNode, UNO_QUERY );
3136 EffectSequenceHelper::create( xChildNode );
3137 }
3138 else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE )
3139 {
3140 Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW );
3141 InteractiveSequencePtr pIS = std::make_shared<InteractiveSequence>( xInteractiveRoot, this );
3142 pIS->addListener( this );
3143 maInteractiveSequenceVector.push_back( pIS );
3144 }
3145 }
3146
3147 // see if we have a mainsequence at all. if not, create one...
3148 if( !mxSequenceRoot.is() )
3149 {
3150 mxSequenceRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3151
3152 uno::Sequence< css::beans::NamedValue > aUserData
3153 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } };
3154 mxSequenceRoot->setUserData( aUserData );
3155
3156 // empty sequence until now, set duration to 0.0
3157 // explicitly (otherwise, this sequence will never
3158 // end)
3159 mxSequenceRoot->setDuration( Any(0.0) );
3160
3161 Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW );
3162 mxTimingRootNode->appendChild( xMainSequenceNode );
3163 }
3164
3166
3168
3169 Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
3170 if( xNotifier.is() )
3171 xNotifier->addChangesListener( mxChangesListener );
3172 }
3173 catch( Exception& )
3174 {
3175 TOOLS_WARN_EXCEPTION( "sd", "sd::MainSequence::create()" );
3176 return;
3177 }
3178
3179 DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" );
3180}
3181
3183{
3185
3186 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3187 interactiveSequence->reset();
3189
3190 try
3191 {
3192 Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY );
3193 if( xNotifier.is() )
3194 xNotifier->removeChangesListener( mxChangesListener );
3195 }
3196 catch( Exception& )
3197 {
3198
3199 }
3200}
3201
3202InteractiveSequencePtr MainSequence::createInteractiveSequence( const css::uno::Reference< css::drawing::XShape >& xShape )
3203{
3205
3206 // create a new interactive sequence container
3207 Reference< XTimeContainer > xISRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() );
3208
3209 uno::Sequence< css::beans::NamedValue > aUserData
3210 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE) } };
3211 xISRoot->setUserData( aUserData );
3212 xISRoot->setRestart( css::animations::AnimationRestart::WHEN_NOT_ACTIVE );
3213
3214 Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
3215 Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
3216 xParent->appendChild( xISRoot );
3217
3218 pIS = std::make_shared<InteractiveSequence>( xISRoot, this);
3219 pIS->setTriggerShape( xShape );
3220 pIS->addListener( this );
3221 maInteractiveSequenceVector.push_back( pIS );
3222 return pIS;
3223}
3224
3225CustomAnimationEffectPtr MainSequence::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const
3226{
3228
3229 if( !pEffect )
3230 {
3231 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3232 {
3233 pEffect = interactiveSequence->findEffect( xNode );
3234 if (pEffect)
3235 break;
3236 }
3237 }
3238 return pEffect;
3239}
3240
3242{
3243 sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect );
3244
3245 if( nOffset != -1 )
3246 return nOffset;
3247
3249
3250 for (auto const& interactiveSequence : maInteractiveSequenceVector)
3251 {
3252 sal_Int32 nTemp = interactiveSequence->getOffsetFromEffect( pEffect );
3253 if( nTemp != -1 )
3254 return nOffset + nTemp;
3255
3256 nOffset += interactiveSequence->getCount();
3257 }
3258
3259 return -1;
3260}
3261
3263{
3264 if( nOffset >= 0 )
3265 {
3266 if( nOffset < getCount() )
3268
3269 nOffset -= getCount();
3270
3271 auto aIter( maInteractiveSequenceVector.begin() );
3272
3273 while( (aIter != maInteractiveSequenceVector.end()) && (nOffset > (*aIter)->getCount()) )
3274 nOffset -= (*aIter++)->getCount();
3275
3276 if( (aIter != maInteractiveSequenceVector.end()) && (nOffset >= 0) )
3277 return (*aIter)->getEffectFromOffset( nOffset );
3278 }
3279
3281 return pEffect;
3282}
3283
3284bool MainSequence::disposeShape( const Reference< XShape >& xShape )
3285{
3286 bool bChanges = EffectSequenceHelper::disposeShape( xShape );
3287
3288 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3289 {
3290 bChanges |= iterativeSequence->disposeShape( xShape );
3291 }
3292
3293 if( bChanges )
3295
3296 return bChanges;
3297}
3298
3299bool MainSequence::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape )
3300{
3301 if( EffectSequenceHelper::hasEffect( xShape ) )
3302 return true;
3303
3304 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3305 {
3306 if( iterativeSequence->getTriggerShape() == xShape )
3307 return true;
3308
3309 if( iterativeSequence->hasEffect( xShape ) )
3310 return true;
3311 }
3312
3313 return false;
3314}
3315
3316void MainSequence::insertTextRange( const css::uno::Any& aTarget )
3317{
3319
3320 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3321 {
3322 iterativeSequence->insertTextRange( aTarget );
3323 }
3324}
3325
3326void MainSequence::disposeTextRange( const css::uno::Any& aTarget )
3327{
3329
3330 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3331 {
3332 iterativeSequence->disposeTextRange( aTarget );
3333 }
3334}
3335
3337void MainSequence::onTextChanged( const Reference< XShape >& xShape )
3338{
3340
3341 for (auto const& iterativeSequence : maInteractiveSequenceVector)
3342 {
3343 iterativeSequence->onTextChanged( xShape );
3344 }
3345}
3346
3347void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape )
3348{
3349 // get map [paragraph index] -> [NumberingLevel]
3350 // for following reusage inside all animation effects
3351 std::vector< sal_Int32 > paragraphNumberingLevel;
3352 std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr;
3353 if ( getParagraphNumberingLevels( xShape, paragraphNumberingLevel ) )
3354 paragraphNumberingLevelParam = &paragraphNumberingLevel;
3355
3356 // update internal flags for each animation effect
3357 const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false,
3358 [&xShape, &paragraphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) {
3359 bool bRes = bCheck;
3360 if (rxEffect->getTargetShape() == xShape)
3361 bRes |= rxEffect->checkForText( paragraphNumberingLevelParam );
3362 return bRes;
3363 });
3364
3365 if( bChanges )
3366 rebuild();
3367}
3368
3370{
3372}
3373
3375{
3377}
3378
3380{
3381 DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" );
3382 if( mnRebuildLockGuard )
3384
3386 {
3389 }
3390}
3391
3393{
3394 if( mnRebuildLockGuard )
3395 {
3397 return;
3398 }
3399
3400 mbRebuilding = true;
3401
3403
3404 auto aIter( maInteractiveSequenceVector.begin() );
3405 while( aIter != maInteractiveSequenceVector.end() )
3406 {
3407 InteractiveSequencePtr pIS( *aIter );
3408 if( pIS->maEffects.empty() )
3409 {
3410 // remove empty interactive sequences
3411 aIter = maInteractiveSequenceVector.erase( aIter );
3412
3413 Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW );
3414 Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW );
3415 Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW );
3416 xParent->removeChild( xISNode );
3417 }
3418 else
3419 {
3420 pIS->implRebuild();
3421 ++aIter;
3422 }
3423 }
3424
3426 mbRebuilding = false;
3427}
3428
3430{
3432}
3433
3434bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const css::uno::Reference< css::drawing::XShape >& xTriggerShape )
3435{
3436 EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence();
3437
3438 EffectSequenceHelper* pNewSequence = nullptr;
3439 if( xTriggerShape.is() )
3440 {
3442 {
3443 if( pIS->getTriggerShape() == xTriggerShape )
3444 {
3445 pNewSequence = pIS.get();
3446 break;
3447 }
3448 }
3449
3450 if( !pNewSequence )
3451 pNewSequence = createInteractiveSequence( xTriggerShape ).get();
3452 }
3453 else
3454 {
3455 pNewSequence = this;
3456 }
3457
3458 if( pOldSequence != pNewSequence )
3459 {
3460 if( pOldSequence )
3461 pOldSequence->maEffects.remove( pEffect );
3462 if( pNewSequence )
3463 pNewSequence->maEffects.push_back( pEffect );
3464 pEffect->setEffectSequence( pNewSequence );
3465 return true;
3466 }
3467 else
3468 {
3469 return false;
3470 }
3471
3472}
3473
3475{
3476 if( mbTimerMode )
3477 {
3478 implRebuild();
3479 }
3480 else
3481 {
3482 reset();
3483 createMainSequence();
3484 }
3485}
3486
3489{
3490 if( !mbRebuilding && (mbIgnoreChanges == 0) )
3491 {
3492 mbTimerMode = false;
3493 maTimer.Start();
3494 }
3495}
3496
3502{
3503 mbTimerMode = true;
3504 maTimer.Start();
3505}
3506
3507InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence )
3508: EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence )
3509{
3510 mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE;
3511
3512 try
3513 {
3514 if( mxSequenceRoot.is() )
3515 {
3516 Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW );
3517 Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW );
3518 while( !mxEventSource.is() && xEnumeration->hasMoreElements() )
3519 {
3520 Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW );
3521
3522 Event aEvent;
3523 if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) )
3524 aEvent.Source >>= mxEventSource;
3525 }
3526 }
3527 }
3528 catch( Exception& )
3529 {
3530 TOOLS_WARN_EXCEPTION( "sd", "sd::InteractiveSequence::InteractiveSequence()" );
3531 return;
3532 }
3533}
3534
3536{
3538}
3539
3541{
3543}
3544
3546: mpMainSequence(std::move( pMainSequence ))
3547{
3548 if( mpMainSequence )
3549 mpMainSequence->lockRebuilds();
3550}
3551
3553{
3554 if( mpMainSequence )
3555 mpMainSequence->unlockRebuilds();
3556}
3557
3558}
3559
3560/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double mfDuration
sal_Int16 mnFill
double mfAcceleration
double mfIterateInterval
sal_Int16 mnCommand
const sal_Int16 mnNodeType
bool mbAutoReverse
Any maTarget
sal_Int16 mnIterateType
double mfDecelerate
AnyEventRef aEvent
constexpr tools::Long Y() const
constexpr tools::Long X() const
static SdrObject * getSdrObjectFromXShape(const css::uno::Reference< css::uno::XInterface > &xInt)
virtual const tools::Rectangle & GetCurrentBoundRect() const
sdr::contact::ViewContact & GetViewContact() const
SdrPage * getSdrPageFromSdrObject() const
Size GetSize() const
void SetPathPoly(const basegfx::B2DPolyPolygon &rPathPoly)
const basegfx::B2DPolyPolygon & GetPathPoly() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
bool IsActive() const
void Stop()
void SetTimeout(sal_uInt64 nTimeoutMs)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual void Start(bool bStartTimer=true) override
void transform(const basegfx::B2DHomMatrix &rMatrix)
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
bool isEmpty() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
SAL_DLLPRIVATE css::uno::Any getColor(sal_Int32 nIndex)
SAL_DLLPRIVATE void replaceNode(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE css::uno::Reference< css::animations::XAnimationNode > createAfterEffectNode() const
SAL_DLLPRIVATE void setEnd(const css::uno::Any &rEnd)
static SAL_DLLPRIVATE sal_Int32 getNumberOfSubitems(const css::uno::Any &aTarget, sal_Int16 nIterateType)
SAL_DLLPRIVATE void setColor(sal_Int32 nIndex, const css::uno::Any &rColor)
SAL_DLLPRIVATE css::uno::Any getTransformationProperty(sal_Int32 nTransformType, EValue eValue)
SAL_DLLPRIVATE void setGroupId(sal_Int32 nGroupId)
SAL_DLLPRIVATE OUString getPath() const
SAL_DLLPRIVATE bool checkForText(const std::vector< sal_Int32 > *paragraphNumberingLevel=nullptr)
checks if the text for this effect has changed and updates internal flags.
SAL_DLLPRIVATE void setFill(sal_Int16 nFill)
static SAL_DLLPRIVATE sal_Int32 get_node_type(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE void setNode(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE rtl::Reference< SdrPathObj > createSdrPathObjFromPath(SdrModel &rTargetModel)
void setTargetSubItem(sal_Int16 nSubItem)
css::uno::Reference< css::animations::XAudio > mxAudio
SAL_DLLPRIVATE CustomAnimationEffectPtr clone() const
SAL_DLLPRIVATE bool getStopAudio() const
SAL_DLLPRIVATE bool setTransformationProperty(sal_Int32 nTransformType, EValue eValue, const css::uno::Any &rValue)
css::uno::Reference< css::animations::XAnimationNode > mxNode
SAL_DLLPRIVATE const OUString & getProperty() const
SAL_DLLPRIVATE const css::uno::Reference< css::animations::XAnimationNode > & getNode() const
void setTarget(const css::uno::Any &rTarget)
SAL_DLLPRIVATE css::uno::Reference< css::drawing::XShape > getTargetShape() const
void setDuration(double fDuration)
SAL_DLLPRIVATE bool setProperty(sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue, const css::uno::Any &rValue)
SAL_DLLPRIVATE css::uno::Any getRepeatCount() const
SAL_DLLPRIVATE void setAudio(const css::uno::Reference< css::animations::XAudio > &xAudio)
SAL_DLLPRIVATE void setAutoReverse(bool bAutoReverse)
void setIterateType(sal_Int16 nIterateType)
CustomAnimationEffect(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
void setNodeType(sal_Int16 nNodeType)
SAL_DLLPRIVATE css::uno::Any getEnd() const
EffectSequenceHelper * mpEffectSequence
SAL_DLLPRIVATE EffectSequenceHelper * getEffectSequence() const
SAL_DLLPRIVATE void setPath(const OUString &rPath)
SAL_DLLPRIVATE void setRepeatCount(const css::uno::Any &rRepeatCount)
SAL_DLLPRIVATE void setPresetClassAndId(sal_Int16 nPresetClass, const OUString &rPresetId)
SAL_DLLPRIVATE void setAcceleration(double fAcceleration)
SAL_DLLPRIVATE void updatePathFromSdrPathObj(const SdrPathObj &rPathObj)
SAL_DLLPRIVATE void updateSdrPathObjFromPath(SdrPathObj &rPathObj)
void createAudio(const css::uno::Any &rSource)
SAL_DLLPRIVATE bool calculateIterateDuration()
void setIterateInterval(double fIterateInterval)
SAL_DLLPRIVATE void setDecelerate(double fDecelerate)
CustomAnimationTextGroup(const css::uno::Reference< css::drawing::XShape > &rTarget, sal_Int32 nGroupId)
void addEffect(CustomAnimationEffectPtr const &pEffect)
SAL_DLLPRIVATE void setTextGroupingAuto(const CustomAnimationTextGroupPtr &pTextGroup, double fTextGroupingAuto)
virtual SAL_DLLPRIVATE CustomAnimationEffectPtr getEffectFromOffset(sal_Int32 nOffset) const
SAL_DLLPRIVATE void replace(const CustomAnimationEffectPtr &pEffect, const CustomAnimationPresetPtr &pDescriptor, double fDuration)
virtual SAL_DLLPRIVATE void insertTextRange(const css::uno::Any &aTarget)
virtual SAL_DLLPRIVATE bool disposeShape(const css::uno::Reference< css::drawing::XShape > &xShape)
virtual SAL_DLLPRIVATE bool hasEffect(const css::uno::Reference< css::drawing::XShape > &xShape)
SAL_DLLPRIVATE void setTextGrouping(const CustomAnimationTextGroupPtr &pTextGroup, sal_Int32 nTextGrouping)
SAL_DLLPRIVATE EffectSequence::iterator find(const CustomAnimationEffectPtr &pEffect)
SAL_DLLPRIVATE void createEffectsequence(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE bool getParagraphNumberingLevels(const css::uno::Reference< css::drawing::XShape > &xShape, std::vector< sal_Int32 > &rParagraphNumberingLevel)
virtual SAL_DLLPRIVATE void onTextChanged(const css::uno::Reference< css::drawing::XShape > &xShape)
CustomAnimationTextGroupMap maGroupMap
SAL_DLLPRIVATE void moveToBeforeEffect(const CustomAnimationEffectPtr &pEffect, const CustomAnimationEffectPtr &pInsertBefore)
CustomAnimationTextGroupPtr createTextGroup(const CustomAnimationEffectPtr &pEffect, sal_Int32 nTextGrouping, double fTextGroupingAuto, bool bAnimateForm, bool bTextReverse)
css::uno::Reference< css::animations::XTimeContainer > mxSequenceRoot
SAL_DLLPRIVATE sal_Int32 getCount() const
virtual SAL_DLLPRIVATE void disposeTextRange(const css::uno::Any &aTarget)
virtual SAL_DLLPRIVATE void implRebuild()
SAL_DLLPRIVATE void setTextReverse(const CustomAnimationTextGroupPtr &pTextGroup, bool bAnimateForm)
SAL_DLLPRIVATE void createTextGroupParagraphEffects(const CustomAnimationTextGroupPtr &pTextGroup, const CustomAnimationEffectPtr &pEffect, bool bUsed)
virtual SAL_DLLPRIVATE sal_Int32 getOffsetFromEffect(const CustomAnimationEffectPtr &xEffect) const
SAL_DLLPRIVATE void notify_listeners()
virtual SAL_DLLPRIVATE css::uno::Reference< css::animations::XAnimationNode > getRootNode()
SAL_DLLPRIVATE void createEffects(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE void removeListener(ISequenceListener *pListener)
SAL_DLLPRIVATE void processAfterEffect(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE CustomAnimationEffectPtr append(const CustomAnimationPresetPtr &pDescriptor, const css::uno::Any &rTarget, double fDuration)
virtual SAL_DLLPRIVATE ~EffectSequenceHelper()
SAL_DLLPRIVATE void create(const css::uno::Reference< css::animations::XAnimationNode > &xNode)
SAL_DLLPRIVATE CustomAnimationTextGroupPtr findGroup(sal_Int32 nGroupId)
css::uno::Reference< css::drawing::XShape > mxEventSource
SAL_DLLPRIVATE void setAnimateForm(const CustomAnimationTextGroupPtr &pTextGroup, bool bAnimateForm)
std::list< ISequenceListener * > maListeners
virtual SAL_DLLPRIVATE void rebuild()
this method rebuilds the animation nodes
SAL_DLLPRIVATE void remove(const CustomAnimationEffectPtr &pEffect)
SAL_DLLPRIVATE void updateTextGroups()
virtual SAL_DLLPRIVATE CustomAnimationEffectPtr findEffect(const css::uno::Reference< css::animations::XAnimationNode > &xNode) const
SAL_DLLPRIVATE void addListener(ISequenceListener *pListener)
virtual SAL_DLLPRIVATE void reset()
this listener is implemented by UI components to track changes in the animation core
InteractiveSequence(const css::uno::Reference< css::animations::XTimeContainer > &xSequenceRoot, MainSequence *pMainSequence)
virtual void implRebuild() override
virtual void rebuild() override
this method rebuilds the animation nodes
MainSequenceChangeGuard(EffectSequenceHelper *pSequence)
MainSequenceRebuildGuard(MainSequencePtr pMainSequence)
virtual ~MainSequence() override
bool setTrigger(const CustomAnimationEffectPtr &pEffect, const css::uno::Reference< css::drawing::XShape > &xTriggerShape)
void startRebuildTimer()
starts a timer that rebuilds the API core from the internal structure after 1 second
InteractiveSequencePtr createInteractiveSequence(const css::uno::Reference< css::drawing::XShape > &xShape)
virtual void rebuild() override
this method rebuilds the animation nodes
virtual bool hasEffect(const css::uno::Reference< css::drawing::XShape > &xShape) override
virtual void reset() override
virtual sal_Int32 getOffsetFromEffect(const CustomAnimationEffectPtr &xEffect) const override
::tools::Long mnRebuildLockGuard
virtual css::uno::Reference< css::animations::XAnimationNode > getRootNode() override
virtual bool disposeShape(const css::uno::Reference< css::drawing::XShape > &xShape) override
virtual void onTextChanged(const css::uno::Reference< css::drawing::XShape > &xShape) override
callback from the sd::View when an object just left text edit mode
virtual void disposeTextRange(const css::uno::Any &aTarget) override
virtual CustomAnimationEffectPtr getEffectFromOffset(sal_Int32 nOffset) const override
virtual void implRebuild() override
css::uno::Reference< css::animations::XTimeContainer > mxTimingRootNode
InteractiveSequenceVector maInteractiveSequenceVector
virtual void notify_change() override
void startRecreateTimer()
starts a timer that recreates the internal structure from the API core after 1 second
virtual CustomAnimationEffectPtr findEffect(const css::uno::Reference< css::animations::XAnimationNode > &xNode) const override
void lockRebuilds()
permits rebuilds until unlockRebuilds() is called.
css::uno::Reference< css::util::XChangesListener > mxChangesListener
virtual void insertTextRange(const css::uno::Any &aTarget) override
void getViewIndependentPrimitive2DContainer(drawinglayer::primitive2d::Primitive2DDecompositionVisitor &rVisitor) const
constexpr Point Center() const
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
float u
float x
Reference< XInterface > xTarget
FilterGroup & rTarget
sal_Int32 nIndex
uno_Any a
Environment aTo
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
def text(shape, orig_st)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
@ Exception
Reference< XComponentContext > getProcessComponentContext()
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
enumrange< T >::Iterator begin(enumrange< T >)
end
std::list< CustomAnimationEffectPtr > EffectSequence
IMPL_LINK_NOARG(MainSequence, onTimerHdl, Timer *, void)
static bool implFindNextContainer(Reference< XTimeContainer > const &xParent, Reference< XTimeContainer > const &xCurrent, Reference< XTimeContainer > &xNext)
static bool isParagraphTargetTextEmpty(ParagraphTarget aParaTarget)
std::shared_ptr< MainSequence > MainSequencePtr
std::shared_ptr< CustomAnimationTextGroup > CustomAnimationTextGroupPtr
std::shared_ptr< InteractiveSequence > InteractiveSequencePtr
std::shared_ptr< CustomAnimationPreset > CustomAnimationPresetPtr
void stl_process_after_effect_node_func(AfterEffectNode const &rNode)
inserts the animation node in the given AfterEffectNode at the correct position in the timing hierarc...
static bool implIsColorAttribute(std::u16string_view rAttributeName)
std::shared_ptr< CustomAnimationEffect > CustomAnimationEffectPtr
static Reference< XCommand > findCommandNode(const Reference< XAnimationNode > &xRootNode)
stores the link between an after effect node and its master for later insertion into the timing hiera...
Definition: animations.hxx:36
css::uno::Reference< css::animations::XAnimationNode > mxNode
Definition: animations.hxx:37
css::uno::Reference< css::animations::XAnimationNode > mxMaster
Definition: animations.hxx:38
stl_CustomAnimationEffect_search_node_predict(const css::uno::Reference< css::animations::XAnimationNode > &xSearchNode)
bool operator()(const CustomAnimationEffectPtr &pEffect) const
const css::uno::Reference< css::animations::XAnimationNode > & mxSearchNode
signed char sal_Int8
sal_Int32 nLength