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_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 #include <tools/diagnose_ex.h>
34 #include <canvas/verbosetrace.hxx>
35 
36 #include <com/sun/star/animations/AnimationCalcMode.hpp>
37 #include <comphelper/sequence.hxx>
38 
39 #include "activitiesfactory.hxx"
40 #include "smilfunctionparser.hxx"
41 #include "accumulation.hxx"
42 #include "activityparameters.hxx"
43 #include "interpolation.hxx"
44 #include "tools.hxx"
45 #include "simplecontinuousactivitybase.hxx"
46 #include "discreteactivitybase.hxx"
47 #include "continuousactivitybase.hxx"
48 #include "continuouskeytimeactivitybase.hxx"
49 
50 #include <boost/bind.hpp>
51 #include <boost/optional.hpp>
52 
53 #include <cmath> // for modf
54 #include <vector>
55 #include <algorithm>
56 
57 using namespace com::sun::star;
58 
59 namespace slideshow {
60 namespace internal {
61 
62 namespace {
63 
64 /** Traits template, to take formula application only for ValueType = double
65  */
66 template<typename ValueType> struct FormulaTraits
67 {
68     static ValueType getPresentationValue(
69         const ValueType& rVal, const ExpressionNodeSharedPtr& )
70     {
71         return rVal;
72     }
73 };
74 
75 /// Specialization for ValueType = double
76 template<> struct FormulaTraits<double>
77 {
78     static double getPresentationValue(
79         double const& rVal, ExpressionNodeSharedPtr const& rFormula )
80     {
81         return rFormula ? (*rFormula)(rVal) : rVal;
82     }
83 };
84 
85 // Various ActivityBase specializations for different animator types
86 // =================================================================
87 
88 /** FromToBy handler
89 
90     Provides the Activity specializations for FromToBy
91     animations (e.g. those without a values list).
92 
93     This template makes heavy use of SFINAE, only one of
94     the perform*() methods will compile for each of the
95     base classes.
96 
97     Note that we omit the virtual keyword on the perform()
98     overrides on purpose; those that actually do override
99     baseclass virtual methods inherit the property, and
100     the others won't increase our vtable. What's more,
101     having all perform() method in the vtable actually
102     creates POIs for them, which breaks the whole SFINAE
103     concept (IOW, this template won't compile any longer).
104 
105     @tpl BaseType
106     Base class to use for this activity. Only
107     ContinuousActivityBase and DiscreteActivityBase are
108     supported here.
109 
110     @tpl AnimationType
111     Type of the Animation to call.
112 */
113 template<class BaseType, typename AnimationType>
114 class FromToByActivity : public BaseType
115 {
116 public:
117     typedef typename AnimationType::ValueType           ValueType;
118     typedef boost::optional<ValueType>                  OptionalValueType;
119 
120 private:
121     // some compilers don't inline whose definition they haven't
122     // seen before the call site...
123     ValueType getPresentationValue( const ValueType& rVal ) const
124     {
125         return FormulaTraits<ValueType>::getPresentationValue( rVal, mpFormula);
126     }
127 
128 public:
129     /** Create FromToByActivity.
130 
131         @param rFrom
132         From this value, the animation starts
133 
134         @param rTo
135         With this value, the animation ends
136 
137         @param rBy
138         With this value, the animation increments the start value
139 
140         @param rParms
141         Standard Activity parameter struct
142 
143         @param rAnim
144         Shared ptr to AnimationType
145 
146         @param rInterpolator
147         Interpolator object to be used for lerping between
148         start and end value (need to be passed, since it
149         might contain state, e.g. interpolation direction
150         for HSL color space).
151 
152         @param bCumulative
153         Whether repeated animations should cumulate the
154         value, or start fresh each time.
155     */
156     FromToByActivity(
157         const OptionalValueType&                      rFrom,
158         const OptionalValueType&                      rTo,
159         const OptionalValueType&                      rBy,
160         const ActivityParameters&                     rParms,
161         const ::boost::shared_ptr< AnimationType >&   rAnim,
162         const Interpolator< ValueType >&              rInterpolator,
163         bool                                          bCumulative )
164         : BaseType( rParms ),
165           maFrom( rFrom ),
166           maTo( rTo ),
167           maBy( rBy ),
168           mpFormula( rParms.mpFormula ),
169           maStartValue(),
170           maEndValue(),
171           mpAnim( rAnim ),
172           maInterpolator( rInterpolator ),
173           mbDynamicStartValue( false ),
174           mbCumulative( bCumulative )
175     {
176         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
177 
178         ENSURE_OR_THROW(
179             rTo || rBy,
180             "From and one of To or By, or To or By alone must be valid" );
181     }
182 
183     virtual void startAnimation()
184     {
185         if (this->isDisposed() || !mpAnim)
186             return;
187         BaseType::startAnimation();
188 
189         // start animation
190         mpAnim->start( BaseType::getShape(),
191                        BaseType::getShapeAttributeLayer() );
192 
193         // setup start and end value. Determine animation
194         // start value only when animation actually
195         // started up (this order is part of the Animation
196         // interface contract)
197         const ValueType aAnimationStartValue( mpAnim->getUnderlyingValue() );
198 
199         // first of all, determine general type of
200         // animation, by inspecting which of the FromToBy values
201         // are actually valid.
202         // See http://www.w3.org/TR/smil20/animation.html#AnimationNS-FromToBy
203         // for a definition
204         if( maFrom )
205         {
206             // From-to or From-by animation. According to
207             // SMIL spec, the To value takes precedence
208             // over the By value, if both are specified
209             if( maTo )
210             {
211                 // From-To animation
212                 maStartValue = *maFrom;
213                 maEndValue = *maTo;
214             }
215             else if( maBy )
216             {
217                 // From-By animation
218                 maStartValue = *maFrom;
219                 maEndValue = maStartValue + *maBy;
220             }
221         }
222         else
223         {
224             // By or To animation. According to SMIL spec,
225             // the To value takes precedence over the By
226             // value, if both are specified
227             if( maTo )
228             {
229                 // To animation
230 
231                 // According to the SMIL spec
232                 // (http://www.w3.org/TR/smil20/animation.html#animationNS-ToAnimation),
233                 // the to animation interpolates between
234                 // the _running_ underlying value and the to value (as the end value)
235                 mbDynamicStartValue = true;
236                 maEndValue = *maTo;
237             }
238             else if( maBy )
239             {
240                 // By animation
241                 maStartValue = aAnimationStartValue;
242                 maEndValue = maStartValue + *maBy;
243             }
244         }
245     }
246 
247     virtual void endAnimation()
248     {
249         // end animation
250         if (mpAnim)
251             mpAnim->end();
252     }
253 
254     /// perform override for ContinuousActivityBase
255     void perform( double nModifiedTime, sal_uInt32 nRepeatCount ) const
256     {
257         if (this->isDisposed() || !mpAnim)
258             return;
259         (*mpAnim)(
260             getPresentationValue(
261                 accumulate( maEndValue,
262                             mbCumulative * nRepeatCount, // means: mbCumulative ? nRepeatCount : 0,
263                             maInterpolator( (mbDynamicStartValue
264                                              ? mpAnim->getUnderlyingValue()
265                                              : maStartValue),
266                                             maEndValue,
267                                             nModifiedTime ) ) ) );
268     }
269 
270     using BaseType::perform;
271 
272     /// perform override for DiscreteActivityBase base
273     void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
274     {
275         if (this->isDisposed() || !mpAnim)
276             return;
277         (*mpAnim)(
278             getPresentationValue(
279                 accumulate( maEndValue, mbCumulative ? nRepeatCount : 0,
280                             lerp( maInterpolator,
281                                   (mbDynamicStartValue
282                                    ? mpAnim->getUnderlyingValue()
283                                    : maStartValue),
284                                   maEndValue,
285                                   nFrame,
286                                   BaseType::getNumberOfKeyTimes() ) ) ) );
287     }
288 
289     using BaseType::isAutoReverse;
290 
291     virtual void performEnd()
292     {
293         // xxx todo: good guess
294         if (mpAnim)
295         {
296             if (isAutoReverse())
297                 (*mpAnim)( getPresentationValue( maStartValue ) );
298             else
299                 (*mpAnim)( getPresentationValue( maEndValue ) );
300         }
301     }
302 
303     /// Disposable:
304     virtual void dispose()
305     {
306         mpAnim.reset();
307         BaseType::dispose();
308     }
309 
310 private:
311     const OptionalValueType                 maFrom;
312     const OptionalValueType                 maTo;
313     const OptionalValueType                 maBy;
314 
315     ExpressionNodeSharedPtr                 mpFormula;
316 
317     ValueType                               maStartValue;
318     ValueType                               maEndValue;
319 
320     ::boost::shared_ptr< AnimationType >    mpAnim;
321     Interpolator< ValueType >               maInterpolator;
322     bool                                    mbDynamicStartValue;
323     bool                                    mbCumulative;
324 };
325 
326 
327 /** Generate Activity corresponding to given FromToBy values
328 
329     @tpl BaseType
330     BaseType to use for deriving the Activity from
331 
332     @tpl AnimationType
333     Subtype of the Animation object (e.g. NumberAnimation)
334 */
335 template<class BaseType, typename AnimationType>
336 AnimationActivitySharedPtr createFromToByActivity(
337     const uno::Any&                                          rFromAny,
338     const uno::Any&                                          rToAny,
339     const uno::Any&                                          rByAny,
340     const ActivityParameters&                                rParms,
341     const ::boost::shared_ptr< AnimationType >&              rAnim,
342     const Interpolator< typename AnimationType::ValueType >& rInterpolator,
343     bool                                                     bCumulative,
344     const ShapeSharedPtr&                                    rShape,
345     const ::basegfx::B2DVector&                              rSlideBounds )
346 {
347     typedef typename AnimationType::ValueType           ValueType;
348     typedef boost::optional<ValueType>                  OptionalValueType;
349 
350     OptionalValueType aFrom;
351     OptionalValueType aTo;
352     OptionalValueType aBy;
353 
354     ValueType aTmpValue;
355 
356     if( rFromAny.hasValue() )
357     {
358         ENSURE_OR_THROW(
359             extractValue( aTmpValue, rFromAny, rShape, rSlideBounds ),
360             "createFromToByActivity(): Could not extract from value" );
361         aFrom.reset(aTmpValue);
362     }
363     if( rToAny.hasValue() )
364     {
365         ENSURE_OR_THROW(
366             extractValue( aTmpValue, rToAny, rShape, rSlideBounds ),
367             "createFromToByActivity(): Could not extract to value" );
368         aTo.reset(aTmpValue);
369     }
370     if( rByAny.hasValue() )
371     {
372         ENSURE_OR_THROW(
373             extractValue( aTmpValue, rByAny, rShape, rSlideBounds ),
374             "createFromToByActivity(): Could not extract by value" );
375         aBy.reset(aTmpValue);
376     }
377 
378     return AnimationActivitySharedPtr(
379         new FromToByActivity<BaseType, AnimationType>(
380             aFrom,
381             aTo,
382             aBy,
383             rParms,
384             rAnim,
385             rInterpolator,
386             bCumulative ) );
387 }
388 
389 /* The following table shows which animator combines with
390    which Activity type:
391 
392    NumberAnimator:  all
393    PairAnimation:   all
394    ColorAnimation:  all
395    StringAnimation: DiscreteActivityBase
396    BoolAnimation:   DiscreteActivityBase
397 */
398 
399 /** Values handler
400 
401     Provides the Activity specializations for value lists
402     animations.
403 
404     This template makes heavy use of SFINAE, only one of
405     the perform*() methods will compile for each of the
406     base classes.
407 
408     Note that we omit the virtual keyword on the perform()
409     overrides on purpose; those that actually do override
410     baseclass virtual methods inherit the property, and
411     the others won't increase our vtable. What's more,
412     having all perform() method in the vtable actually
413     creates POIs for them, which breaks the whole SFINAE
414     concept (IOW, this template won't compile any longer).
415 
416     @tpl BaseType
417     Base class to use for this activity. Only
418     ContinuousKeyTimeActivityBase and DiscreteActivityBase
419     are supported here. For values animation without key
420     times, the client must emulate key times by providing
421     a vector of equally spaced values between 0 and 1,
422     with the same number of entries as the values vector.
423 
424     @tpl AnimationType
425     Type of the Animation to call.
426 */
427 template<class BaseType, typename AnimationType>
428 class ValuesActivity : public BaseType
429 {
430 public:
431     typedef typename AnimationType::ValueType   ValueType;
432     typedef std::vector<ValueType>              ValueVectorType;
433 
434 private:
435     // some compilers don't inline methods whose definition they haven't
436     // seen before the call site...
437     ValueType getPresentationValue( const ValueType& rVal ) const
438     {
439         return FormulaTraits<ValueType>::getPresentationValue(
440             rVal, mpFormula );
441     }
442 
443 public:
444     /** Create ValuesActivity.
445 
446         @param rValues
447         Value vector to cycle animation through
448 
449         @param rParms
450         Standard Activity parameter struct
451 
452         @param rAnim
453         Shared ptr to AnimationType
454 
455         @param rInterpolator
456         Interpolator object to be used for lerping between
457         start and end value (need to be passed, since it
458         might contain state, e.g. interpolation direction
459         for HSL color space).
460 
461         @param bCumulative
462         Whether repeated animations should cumulate the
463         value, or start afresh each time.
464     */
465     ValuesActivity(
466         const ValueVectorType&                      rValues,
467         const ActivityParameters&                   rParms,
468         const boost::shared_ptr<AnimationType>&     rAnim,
469         const Interpolator< ValueType >&            rInterpolator,
470         bool                                        bCumulative )
471         : BaseType( rParms ),
472           maValues( rValues ),
473           mpFormula( rParms.mpFormula ),
474           mpAnim( rAnim ),
475           maInterpolator( rInterpolator ),
476           mbCumulative( bCumulative )
477     {
478         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
479         ENSURE_OR_THROW( !rValues.empty(), "Empty value vector" );
480     }
481 
482     virtual void startAnimation()
483     {
484         if (this->isDisposed() || !mpAnim)
485             return;
486         BaseType::startAnimation();
487 
488         // start animation
489         mpAnim->start( BaseType::getShape(),
490                        BaseType::getShapeAttributeLayer() );
491     }
492 
493     virtual void endAnimation()
494     {
495         // end animation
496         if (mpAnim)
497             mpAnim->end();
498     }
499 
500     /// perform override for ContinuousKeyTimeActivityBase base
501     void perform( sal_uInt32    nIndex,
502                   double        nFractionalIndex,
503                   sal_uInt32    nRepeatCount ) const
504     {
505         if (this->isDisposed() || !mpAnim)
506             return;
507         ENSURE_OR_THROW( nIndex+1 < maValues.size(),
508                           "ValuesActivity::perform(): index out of range" );
509 
510         // interpolate between nIndex and nIndex+1 values
511         (*mpAnim)(
512             getPresentationValue(
513                 accumulate( maValues.back(),
514                             mbCumulative ? nRepeatCount : 0,
515                             maInterpolator( maValues[ nIndex ],
516                                             maValues[ nIndex+1 ],
517                                             nFractionalIndex ) ) ) );
518     }
519 
520     using BaseType::perform;
521 
522     /// perform override for DiscreteActivityBase base
523     void perform( sal_uInt32 nFrame, sal_uInt32 nRepeatCount ) const
524     {
525         if (this->isDisposed() || !mpAnim)
526             return;
527         ENSURE_OR_THROW( nFrame < maValues.size(),
528                           "ValuesActivity::perform(): index out of range" );
529 
530         // this is discrete, thus no lerp here.
531         (*mpAnim)(
532             getPresentationValue(
533                 accumulate( maValues.back(),
534                             mbCumulative ? nRepeatCount : 0,
535                             maValues[ nFrame ] ) ) );
536     }
537 
538     virtual void performEnd()
539     {
540         // xxx todo: good guess
541         if (mpAnim)
542             (*mpAnim)( getPresentationValue( maValues.back() ) );
543     }
544 
545     /// Disposable:
546     virtual void dispose()
547     {
548         mpAnim.reset();
549         BaseType::dispose();
550     }
551 
552 private:
553     ValueVectorType                         maValues;
554 
555     ExpressionNodeSharedPtr                 mpFormula;
556 
557     boost::shared_ptr<AnimationType>        mpAnim;
558     Interpolator< ValueType >               maInterpolator;
559     bool                                    mbCumulative;
560 };
561 
562 /** Generate Activity corresponding to given Value vector
563 
564     @tpl BaseType
565     BaseType to use for deriving the Activity from
566 
567     @tpl AnimationType
568     Subtype of the Animation object (e.g. NumberAnimation)
569 */
570 template<class BaseType, typename AnimationType>
571 AnimationActivitySharedPtr createValueListActivity(
572     const uno::Sequence<uno::Any>&                            rValues,
573     const ActivityParameters&                                 rParms,
574     const boost::shared_ptr<AnimationType>&                   rAnim,
575     const Interpolator<typename AnimationType::ValueType>&    rInterpolator,
576     bool                                                      bCumulative,
577     const ShapeSharedPtr&                                     rShape,
578     const ::basegfx::B2DVector&                               rSlideBounds )
579 {
580     typedef typename AnimationType::ValueType   ValueType;
581     typedef std::vector<ValueType>              ValueVectorType;
582 
583     ValueVectorType aValueVector;
584     aValueVector.reserve( rValues.getLength() );
585 
586     for( ::std::size_t i=0, nLen=rValues.getLength(); i<nLen; ++i )
587     {
588         ValueType aValue;
589         ENSURE_OR_THROW(
590             extractValue( aValue, rValues[i], rShape, rSlideBounds ),
591             "createValueListActivity(): Could not extract values" );
592         aValueVector.push_back( aValue );
593     }
594 
595     return AnimationActivitySharedPtr(
596         new ValuesActivity<BaseType, AnimationType>(
597             aValueVector,
598             rParms,
599             rAnim,
600             rInterpolator,
601             bCumulative ) );
602 }
603 
604 /** Generate Activity for given XAnimate, corresponding to given Value vector
605 
606     @tpl AnimationType
607     Subtype of the Animation object (e.g. NumberAnimation)
608 
609     @param rParms
610     Common activity parameters
611 
612     @param xNode
613     XAnimate node, to retrieve animation values from
614 
615     @param rAnim
616     Actual animation to operate with (gets called with the
617     time-dependent values)
618 
619     @param rInterpolator
620     Interpolator object to be used for lerping between
621     start and end values (need to be passed, since it
622     might contain state, e.g. interpolation direction
623     for HSL color space).
624 */
625 template<typename AnimationType>
626 AnimationActivitySharedPtr createActivity(
627     const ActivitiesFactory::CommonParameters&               rParms,
628     const uno::Reference< animations::XAnimate >&            xNode,
629     const ::boost::shared_ptr< AnimationType >&              rAnim,
630     const Interpolator< typename AnimationType::ValueType >& rInterpolator
631     = Interpolator< typename AnimationType::ValueType >() )
632 {
633     // setup common parameters
634     // =======================
635 
636     ActivityParameters aActivityParms( rParms.mpEndEvent,
637                                        rParms.mrEventQueue,
638                                        rParms.mrActivitiesQueue,
639                                        rParms.mnMinDuration,
640                                        rParms.maRepeats,
641                                        rParms.mnAcceleration,
642                                        rParms.mnDeceleration,
643                                        rParms.mnMinNumberOfFrames,
644                                        rParms.mbAutoReverse );
645 
646     // is a formula given?
647     const ::rtl::OUString& rFormulaString( xNode->getFormula() );
648     if( rFormulaString.getLength() )
649     {
650         // yep, parse and pass to ActivityParameters
651         try
652         {
653             aActivityParms.mpFormula =
654                 SmilFunctionParser::parseSmilFunction(
655                     rFormulaString,
656                     calcRelativeShapeBounds(
657                         rParms.maSlideBounds,
658                         rParms.mpShape->getBounds() ) );
659         }
660         catch( ParseError& )
661         {
662             // parse error, thus no formula
663             OSL_ENSURE( false,
664                         "createActivity(): Error parsing formula string" );
665         }
666     }
667 
668     // are key times given?
669     const uno::Sequence< double >& aKeyTimes( xNode->getKeyTimes() );
670     if( aKeyTimes.hasElements() )
671     {
672         // yes, convert them from Sequence< double >
673         aActivityParms.maDiscreteTimes.resize( aKeyTimes.getLength() );
674         comphelper::sequenceToArray(
675             &aActivityParms.maDiscreteTimes[0],
676             aKeyTimes ); // saves us some temporary vectors
677     }
678 
679     // values sequence given?
680     const sal_Int32 nValueLen( xNode->getValues().getLength() );
681     if( nValueLen )
682     {
683         // Value list activity
684         // ===================
685 
686         // fake keytimes, if necessary
687         if( !aKeyTimes.hasElements() )
688         {
689             // create a dummy vector of key times,
690             // with aValues.getLength equally spaced entries.
691             for( sal_Int32 i=0; i<nValueLen; ++i )
692                 aActivityParms.maDiscreteTimes.push_back( double(i)/nValueLen );
693         }
694 
695         // determine type of animation needed here:
696         // Value list activities are possible with
697         // ContinuousKeyTimeActivityBase and DiscreteActivityBase
698         // specializations
699         const sal_Int16 nCalcMode( xNode->getCalcMode() );
700 
701         switch( nCalcMode )
702         {
703             case animations::AnimationCalcMode::DISCRETE:
704             {
705                 // since DiscreteActivityBase suspends itself
706                 // between the frames, create a WakeupEvent for it.
707                 aActivityParms.mpWakeupEvent.reset(
708                     new WakeupEvent(
709                         rParms.mrEventQueue.getTimer(),
710                         rParms.mrActivitiesQueue ) );
711 
712                 AnimationActivitySharedPtr pActivity(
713                     createValueListActivity< DiscreteActivityBase >(
714                         xNode->getValues(),
715                         aActivityParms,
716                         rAnim,
717                         rInterpolator,
718                         xNode->getAccumulate(),
719                         rParms.mpShape,
720                         rParms.maSlideBounds ) );
721 
722                 // WakeupEvent and DiscreteActivityBase need circular
723                 // references to the corresponding other object.
724                 aActivityParms.mpWakeupEvent->setActivity( pActivity );
725 
726                 return pActivity;
727             }
728 
729             default:
730                 OSL_ENSURE( false, "createActivity(): unexpected case" );
731                 // FALLTHROUGH intended
732             case animations::AnimationCalcMode::PACED:
733                 // FALLTHROUGH intended
734             case animations::AnimationCalcMode::SPLINE:
735                 // FALLTHROUGH intended
736             case animations::AnimationCalcMode::LINEAR:
737                 return createValueListActivity< ContinuousKeyTimeActivityBase >(
738                     xNode->getValues(),
739                     aActivityParms,
740                     rAnim,
741                     rInterpolator,
742                     xNode->getAccumulate(),
743                     rParms.mpShape,
744                     rParms.maSlideBounds );
745         }
746     }
747     else
748     {
749         // FromToBy activity
750         // =================
751 
752         // determine type of animation needed here:
753         // FromToBy activities are possible with
754         // ContinuousActivityBase and DiscreteActivityBase
755         // specializations
756         const sal_Int16 nCalcMode( xNode->getCalcMode() );
757 
758         switch( nCalcMode )
759         {
760             case animations::AnimationCalcMode::DISCRETE:
761             {
762                 // fake keytimes, if necessary
763                 if( !aKeyTimes.hasElements() )
764                 {
765                     // create a dummy vector of 2 key times
766                     const ::std::size_t nLen( 2 );
767                     for( ::std::size_t i=0; i<nLen; ++i )
768                         aActivityParms.maDiscreteTimes.push_back( double(i)/nLen );
769                 }
770 
771                 // since DiscreteActivityBase suspends itself
772                 // between the frames, create a WakeupEvent for it.
773                 aActivityParms.mpWakeupEvent.reset(
774                     new WakeupEvent(
775                         rParms.mrEventQueue.getTimer(),
776                         rParms.mrActivitiesQueue ) );
777 
778                 AnimationActivitySharedPtr pActivity(
779                     createFromToByActivity< DiscreteActivityBase >(
780                         xNode->getFrom(),
781                         xNode->getTo(),
782                         xNode->getBy(),
783                         aActivityParms,
784                         rAnim,
785                         rInterpolator,
786                         xNode->getAccumulate(),
787                         rParms.mpShape,
788                         rParms.maSlideBounds ) );
789 
790                 // WakeupEvent and DiscreteActivityBase need circular
791                 // references to the corresponding other object.
792                 aActivityParms.mpWakeupEvent->setActivity( pActivity );
793 
794                 return pActivity;
795             }
796 
797             default:
798                 OSL_ENSURE( false, "createActivity(): unexpected case" );
799                 // FALLTHROUGH intended
800             case animations::AnimationCalcMode::PACED:
801                 // FALLTHROUGH intended
802             case animations::AnimationCalcMode::SPLINE:
803                 // FALLTHROUGH intended
804             case animations::AnimationCalcMode::LINEAR:
805                 return createFromToByActivity< ContinuousActivityBase >(
806                     xNode->getFrom(),
807                     xNode->getTo(),
808                     xNode->getBy(),
809                     aActivityParms,
810                     rAnim,
811                     rInterpolator,
812                     xNode->getAccumulate(),
813                     rParms.mpShape,
814                     rParms.maSlideBounds );
815         }
816     }
817 }
818 
819 /** Simple activity for ActivitiesFactory::createSimpleActivity
820 
821     @tpl Direction
822     Determines direction of value generator. A 1 yields a
823     forward direction, starting with 0.0 and ending with
824     1.0. A 0 yields a backward direction, starting with
825     1.0 and ending with 0.0
826 */
827 template<int Direction>
828 class SimpleActivity : public ContinuousActivityBase
829 {
830 public:
831     /** Create SimpleActivity.
832 
833         @param rParms
834         Standard Activity parameter struct
835     */
836     SimpleActivity( const ActivityParameters&       rParms,
837                     const NumberAnimationSharedPtr& rAnim ) :
838         ContinuousActivityBase( rParms ),
839         mpAnim( rAnim )
840     {
841         ENSURE_OR_THROW( mpAnim, "Invalid animation object" );
842     }
843 
844     virtual void startAnimation()
845     {
846         if (this->isDisposed() || !mpAnim)
847             return;
848         ContinuousActivityBase::startAnimation();
849 
850         // start animation
851         mpAnim->start( getShape(),
852                        getShapeAttributeLayer() );
853     }
854 
855     virtual void endAnimation()
856     {
857         // end animation
858         if (mpAnim)
859             mpAnim->end();
860     }
861 
862     using SimpleContinuousActivityBase::perform;
863 
864     /// perform override for ContinuousActivityBase
865     virtual void perform( double nModifiedTime, sal_uInt32 ) const
866     {
867         if (this->isDisposed() || !mpAnim)
868             return;
869         // no cumulation, simple [0,1] range
870         (*mpAnim)( 1.0 - Direction + nModifiedTime*(2.0*Direction - 1.0) );
871     }
872 
873     virtual void performEnd()
874     {
875         // xxx todo: review
876         if (mpAnim)
877             (*mpAnim)( 1.0*Direction );
878     }
879 
880     /// Disposable:
881     virtual void dispose()
882     {
883         mpAnim.reset();
884         ContinuousActivityBase::dispose();
885     }
886 
887 private:
888     NumberAnimationSharedPtr    mpAnim;
889 };
890 
891 } // anon namespace
892 
893 
894 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
895     const CommonParameters&                        rParms,
896     const NumberAnimationSharedPtr&                rAnim,
897     const uno::Reference< animations::XAnimate >&  xNode )
898 {
899     // forward to appropriate template instantiation
900     return createActivity( rParms, xNode, rAnim );
901 }
902 
903 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
904     const CommonParameters&                        rParms,
905     const EnumAnimationSharedPtr&                  rAnim,
906     const uno::Reference< animations::XAnimate >&  xNode )
907 {
908     // forward to appropriate template instantiation
909     return createActivity( rParms, xNode, rAnim );
910 }
911 
912 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
913     const CommonParameters&                        rParms,
914     const ColorAnimationSharedPtr&                 rAnim,
915     const uno::Reference< animations::XAnimate >&  xNode )
916 {
917     // forward to appropriate template instantiation
918     return createActivity( rParms, xNode, rAnim );
919 }
920 
921 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
922     const CommonParameters&                            rParms,
923     const HSLColorAnimationSharedPtr&                  rAnim,
924     const uno::Reference< animations::XAnimateColor >& xNode )
925 {
926     // forward to appropriate template instantiation
927     return createActivity( rParms,
928                            uno::Reference< animations::XAnimate >(
929                                xNode, uno::UNO_QUERY_THROW ),
930                            rAnim,
931                            // Direction==true means clockwise in SMIL API
932                            Interpolator< HSLColor >( !xNode->getDirection() ) );
933 }
934 
935 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
936     const CommonParameters&                        rParms,
937     const PairAnimationSharedPtr&                  rAnim,
938     const uno::Reference< animations::XAnimate >&  xNode )
939 {
940     // forward to appropriate template instantiation
941     return createActivity( rParms, xNode, rAnim );
942 }
943 
944 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
945     const CommonParameters&                        rParms,
946     const StringAnimationSharedPtr&                rAnim,
947     const uno::Reference< animations::XAnimate >&  xNode )
948 {
949     // forward to appropriate template instantiation
950     return createActivity( rParms, xNode, rAnim );
951 }
952 
953 AnimationActivitySharedPtr ActivitiesFactory::createAnimateActivity(
954     const CommonParameters&                        rParms,
955     const BoolAnimationSharedPtr&                  rAnim,
956     const uno::Reference< animations::XAnimate >&  xNode )
957 {
958     // forward to appropriate template instantiation
959     return createActivity( rParms, xNode, rAnim );
960 }
961 
962 AnimationActivitySharedPtr ActivitiesFactory::createSimpleActivity(
963     const CommonParameters&         rParms,
964     const NumberAnimationSharedPtr& rAnim,
965     bool                            bDirectionForward )
966 {
967     ActivityParameters aActivityParms( rParms.mpEndEvent,
968                                        rParms.mrEventQueue,
969                                        rParms.mrActivitiesQueue,
970                                        rParms.mnMinDuration,
971                                        rParms.maRepeats,
972                                        rParms.mnAcceleration,
973                                        rParms.mnDeceleration,
974                                        rParms.mnMinNumberOfFrames,
975                                        rParms.mbAutoReverse );
976 
977     if( bDirectionForward )
978         return AnimationActivitySharedPtr(
979             new SimpleActivity<1>( aActivityParms, rAnim ) );
980     else
981         return AnimationActivitySharedPtr(
982             new SimpleActivity<0>( aActivityParms, rAnim ) );
983 }
984 
985 } // namespace internal
986 } // namespace presentation
987 
988