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