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