1*70f497fbSAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*70f497fbSAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*70f497fbSAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*70f497fbSAndrew Rist  * distributed with this work for additional information
6*70f497fbSAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*70f497fbSAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*70f497fbSAndrew Rist  * "License"); you may not use this file except in compliance
9*70f497fbSAndrew Rist  * with the License.  You may obtain a copy of the License at
10*70f497fbSAndrew Rist  *
11*70f497fbSAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12*70f497fbSAndrew Rist  *
13*70f497fbSAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*70f497fbSAndrew Rist  * software distributed under the License is distributed on an
15*70f497fbSAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*70f497fbSAndrew Rist  * KIND, either express or implied.  See the License for the
17*70f497fbSAndrew Rist  * specific language governing permissions and limitations
18*70f497fbSAndrew Rist  * under the License.
19*70f497fbSAndrew Rist  *
20*70f497fbSAndrew Rist  *************************************************************/
21*70f497fbSAndrew Rist 
22*70f497fbSAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_slideshow.hxx"
26cdf0e10cSrcweir 
27cdf0e10cSrcweir // must be first
28cdf0e10cSrcweir #include <canvas/debug.hxx>
29cdf0e10cSrcweir #include <canvas/verbosetrace.hxx>
30cdf0e10cSrcweir #include <cppuhelper/exc_hlp.hxx>
31cdf0e10cSrcweir #include <comphelper/anytostring.hxx>
32cdf0e10cSrcweir #include <com/sun/star/presentation/ParagraphTarget.hpp>
33cdf0e10cSrcweir #include <com/sun/star/animations/Timing.hpp>
34cdf0e10cSrcweir #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
35cdf0e10cSrcweir #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
36cdf0e10cSrcweir 
37cdf0e10cSrcweir #include "nodetools.hxx"
38cdf0e10cSrcweir #include "doctreenode.hxx"
39cdf0e10cSrcweir #include "animationbasenode.hxx"
40cdf0e10cSrcweir #include "delayevent.hxx"
41cdf0e10cSrcweir #include "framerate.hxx"
42cdf0e10cSrcweir 
43cdf0e10cSrcweir #include <boost/bind.hpp>
44cdf0e10cSrcweir #include <boost/optional.hpp>
45cdf0e10cSrcweir #include <algorithm>
46cdf0e10cSrcweir 
47cdf0e10cSrcweir using namespace com::sun::star;
48cdf0e10cSrcweir 
49cdf0e10cSrcweir namespace slideshow {
50cdf0e10cSrcweir namespace internal {
51cdf0e10cSrcweir 
52cdf0e10cSrcweir AnimationBaseNode::AnimationBaseNode(
53cdf0e10cSrcweir     const uno::Reference< animations::XAnimationNode >&   xNode,
54cdf0e10cSrcweir     const BaseContainerNodeSharedPtr&                     rParent,
55cdf0e10cSrcweir     const NodeContext&                                    rContext )
56cdf0e10cSrcweir     : BaseNode( xNode, rParent, rContext ),
57cdf0e10cSrcweir       mxAnimateNode( xNode, uno::UNO_QUERY_THROW ),
58cdf0e10cSrcweir       maAttributeLayerHolder(),
59cdf0e10cSrcweir       maSlideSize( rContext.maSlideSize ),
60cdf0e10cSrcweir       mpActivity(),
61cdf0e10cSrcweir       mpShape(),
62cdf0e10cSrcweir       mpShapeSubset(),
63cdf0e10cSrcweir       mpSubsetManager(rContext.maContext.mpSubsettableShapeManager),
64cdf0e10cSrcweir       mbIsIndependentSubset( rContext.mbIsIndependentSubset )
65cdf0e10cSrcweir {
66cdf0e10cSrcweir     // extract native node targets
67cdf0e10cSrcweir     // ===========================
68cdf0e10cSrcweir 
69cdf0e10cSrcweir     // plain shape target
70cdf0e10cSrcweir     uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(),
71cdf0e10cSrcweir                                               uno::UNO_QUERY );
72cdf0e10cSrcweir 
73cdf0e10cSrcweir     // distinguish 5 cases:
74cdf0e10cSrcweir     //
75cdf0e10cSrcweir     //  - plain shape target
76cdf0e10cSrcweir     //  (NodeContext.mpMasterShapeSubset full set)
77cdf0e10cSrcweir     //
78cdf0e10cSrcweir     //  - parent-generated subset (generate an
79cdf0e10cSrcweir     //  independent subset)
80cdf0e10cSrcweir     //
81cdf0e10cSrcweir     //  - parent-generated subset from iteration
82cdf0e10cSrcweir     //  (generate a dependent subset)
83cdf0e10cSrcweir     //
84cdf0e10cSrcweir     //  - XShape target at the XAnimatioNode (generate
85cdf0e10cSrcweir     //  a plain shape target)
86cdf0e10cSrcweir     //
87cdf0e10cSrcweir     //  - ParagraphTarget target at the XAnimationNode
88cdf0e10cSrcweir     //  (generate an independent shape subset)
89cdf0e10cSrcweir     if( rContext.mpMasterShapeSubset )
90cdf0e10cSrcweir     {
91cdf0e10cSrcweir         if( rContext.mpMasterShapeSubset->isFullSet() )
92cdf0e10cSrcweir         {
93cdf0e10cSrcweir             // case 1: plain shape target from parent
94cdf0e10cSrcweir             mpShape = rContext.mpMasterShapeSubset->getSubsetShape();
95cdf0e10cSrcweir         }
96cdf0e10cSrcweir         else
97cdf0e10cSrcweir         {
98cdf0e10cSrcweir             // cases 2 & 3: subset shape
99cdf0e10cSrcweir             mpShapeSubset = rContext.mpMasterShapeSubset;
100cdf0e10cSrcweir         }
101cdf0e10cSrcweir     }
102cdf0e10cSrcweir     else
103cdf0e10cSrcweir     {
104cdf0e10cSrcweir         // no parent-provided shape, try to extract
105cdf0e10cSrcweir         // from XAnimationNode - cases 4 and 5
106cdf0e10cSrcweir 
107cdf0e10cSrcweir         if( xShape.is() )
108cdf0e10cSrcweir         {
109cdf0e10cSrcweir             mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager,
110cdf0e10cSrcweir                                                xShape );
111cdf0e10cSrcweir         }
112cdf0e10cSrcweir         else
113cdf0e10cSrcweir         {
114cdf0e10cSrcweir             // no shape provided. Maybe a ParagraphTarget?
115cdf0e10cSrcweir             presentation::ParagraphTarget aTarget;
116cdf0e10cSrcweir 
117cdf0e10cSrcweir             if( !(mxAnimateNode->getTarget() >>= aTarget) )
118cdf0e10cSrcweir                 ENSURE_OR_THROW(
119cdf0e10cSrcweir                     false, "could not extract any target information" );
120cdf0e10cSrcweir 
121cdf0e10cSrcweir             xShape = aTarget.Shape;
122cdf0e10cSrcweir 
123cdf0e10cSrcweir             ENSURE_OR_THROW( xShape.is(), "invalid shape in ParagraphTarget" );
124cdf0e10cSrcweir 
125cdf0e10cSrcweir             mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager,
126cdf0e10cSrcweir                                                xShape );
127cdf0e10cSrcweir 
128cdf0e10cSrcweir             // NOTE: For shapes with ParagraphTarget, we ignore
129cdf0e10cSrcweir             // the SubItem property. We implicitely assume that it
130cdf0e10cSrcweir             // is set to ONLY_TEXT.
131cdf0e10cSrcweir             OSL_ENSURE(
132cdf0e10cSrcweir                 mxAnimateNode->getSubItem() ==
133cdf0e10cSrcweir                 presentation::ShapeAnimationSubType::ONLY_TEXT ||
134cdf0e10cSrcweir                 mxAnimateNode->getSubItem() ==
135cdf0e10cSrcweir                 presentation::ShapeAnimationSubType::AS_WHOLE,
136cdf0e10cSrcweir                 "ParagraphTarget given, but subitem not AS_TEXT or AS_WHOLE? "
137cdf0e10cSrcweir                 "Make up your mind, I'll ignore the subitem." );
138cdf0e10cSrcweir 
139cdf0e10cSrcweir             // okay, found a ParagraphTarget with a valid XShape. Does the shape
140cdf0e10cSrcweir             // provide the given paragraph?
141cdf0e10cSrcweir             const DocTreeNode& rTreeNode(
142cdf0e10cSrcweir                 mpShape->getTreeNodeSupplier().getTreeNode(
143cdf0e10cSrcweir                     aTarget.Paragraph,
144cdf0e10cSrcweir                     DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) );
145cdf0e10cSrcweir 
146cdf0e10cSrcweir             // CAUTION: the creation of the subset shape
147cdf0e10cSrcweir             // _must_ stay in the node constructor, since
148cdf0e10cSrcweir             // Slide::prefetchShow() initializes shape
149cdf0e10cSrcweir             // attributes right after animation import (or
150cdf0e10cSrcweir             // the Slide class must be changed).
151cdf0e10cSrcweir             mpShapeSubset.reset(
152cdf0e10cSrcweir                 new ShapeSubset( mpShape,
153cdf0e10cSrcweir                                  rTreeNode,
154cdf0e10cSrcweir                                  mpSubsetManager ));
155cdf0e10cSrcweir 
156cdf0e10cSrcweir             // Override NodeContext, and flag this node as
157cdf0e10cSrcweir             // a special independent subset one. This is
158cdf0e10cSrcweir             // important when applying initial attributes:
159cdf0e10cSrcweir             // independent shape subsets must be setup
160cdf0e10cSrcweir             // when the slide starts, since they, as their
161cdf0e10cSrcweir             // name suggest, can have state independent to
162cdf0e10cSrcweir             // the master shape. The following example
163cdf0e10cSrcweir             // might illustrate that: a master shape has
164cdf0e10cSrcweir             // no effect, one of the text paragraphs
165cdf0e10cSrcweir             // within it has an appear effect. Now, the
166cdf0e10cSrcweir             // respective paragraph must be invisible when
167cdf0e10cSrcweir             // the slide is initially shown, and become
168cdf0e10cSrcweir             // visible only when the effect starts.
169cdf0e10cSrcweir             mbIsIndependentSubset = true;
170cdf0e10cSrcweir 
171cdf0e10cSrcweir             // already enable subset right here, the
172cdf0e10cSrcweir             // setup of initial shape attributes of
173cdf0e10cSrcweir             // course needs the subset shape
174cdf0e10cSrcweir             // generated, to apply e.g. visibility
175cdf0e10cSrcweir             // changes.
176cdf0e10cSrcweir             mpShapeSubset->enableSubsetShape();
177cdf0e10cSrcweir         }
178cdf0e10cSrcweir     }
179cdf0e10cSrcweir }
180cdf0e10cSrcweir 
181cdf0e10cSrcweir void AnimationBaseNode::dispose()
182cdf0e10cSrcweir {
183cdf0e10cSrcweir     if (mpActivity) {
184cdf0e10cSrcweir         mpActivity->dispose();
185cdf0e10cSrcweir         mpActivity.reset();
186cdf0e10cSrcweir     }
187cdf0e10cSrcweir 
188cdf0e10cSrcweir     maAttributeLayerHolder.reset();
189cdf0e10cSrcweir     mxAnimateNode.clear();
190cdf0e10cSrcweir     mpShape.reset();
191cdf0e10cSrcweir     mpShapeSubset.reset();
192cdf0e10cSrcweir 
193cdf0e10cSrcweir     BaseNode::dispose();
194cdf0e10cSrcweir }
195cdf0e10cSrcweir 
196cdf0e10cSrcweir bool AnimationBaseNode::init_st()
197cdf0e10cSrcweir {
198cdf0e10cSrcweir     // if we've still got an old activity lying around, dispose it:
199cdf0e10cSrcweir     if (mpActivity) {
200cdf0e10cSrcweir         mpActivity->dispose();
201cdf0e10cSrcweir         mpActivity.reset();
202cdf0e10cSrcweir     }
203cdf0e10cSrcweir 
204cdf0e10cSrcweir     // note: actually disposing the activity too early might cause problems,
205cdf0e10cSrcweir     // because on dequeued() it calls endAnimation(pAnim->end()), thus ending
206cdf0e10cSrcweir     // animation _after_ last screen update.
207cdf0e10cSrcweir     // review that end() is properly called (which calls endAnimation(), too).
208cdf0e10cSrcweir 
209cdf0e10cSrcweir     try {
210cdf0e10cSrcweir         // TODO(F2): For restart functionality, we must regenerate activities,
211cdf0e10cSrcweir         // since they are not able to reset their state (or implement _that_)
212cdf0e10cSrcweir         mpActivity = createActivity();
213cdf0e10cSrcweir     }
214cdf0e10cSrcweir     catch (uno::Exception const&) {
215cdf0e10cSrcweir         OSL_ENSURE( false, rtl::OUStringToOString(
216cdf0e10cSrcweir                         comphelper::anyToString(cppu::getCaughtException()),
217cdf0e10cSrcweir                         RTL_TEXTENCODING_UTF8 ) );
218cdf0e10cSrcweir         // catch and ignore. We later handle empty activities, but for
219cdf0e10cSrcweir         // other nodes to function properly, the core functionality of
220cdf0e10cSrcweir         // this node must remain up and running.
221cdf0e10cSrcweir     }
222cdf0e10cSrcweir     return true;
223cdf0e10cSrcweir }
224cdf0e10cSrcweir 
225cdf0e10cSrcweir bool AnimationBaseNode::resolve_st()
226cdf0e10cSrcweir {
227cdf0e10cSrcweir     // enable shape subset for automatically generated
228cdf0e10cSrcweir     // subsets. Independent subsets are already setup
229cdf0e10cSrcweir     // during construction time. Doing it only here
230cdf0e10cSrcweir     // saves us a lot of sprites and shapes lying
231cdf0e10cSrcweir     // around. This is especially important for
232cdf0e10cSrcweir     // character-wise iterations, since the shape
233cdf0e10cSrcweir     // content (e.g. thousands of characters) would
234cdf0e10cSrcweir     // otherwise be painted character-by-character.
235cdf0e10cSrcweir     if (isDependentSubsettedShape() && mpShapeSubset) {
236cdf0e10cSrcweir         mpShapeSubset->enableSubsetShape();
237cdf0e10cSrcweir     }
238cdf0e10cSrcweir     return true;
239cdf0e10cSrcweir }
240cdf0e10cSrcweir 
241cdf0e10cSrcweir void AnimationBaseNode::activate_st()
242cdf0e10cSrcweir {
243cdf0e10cSrcweir     // create new attribute layer
244cdf0e10cSrcweir     maAttributeLayerHolder.createAttributeLayer( getShape() );
245cdf0e10cSrcweir 
246cdf0e10cSrcweir     ENSURE_OR_THROW( maAttributeLayerHolder.get(),
247cdf0e10cSrcweir                       "Could not generate shape attribute layer" );
248cdf0e10cSrcweir 
249cdf0e10cSrcweir     // TODO(Q2): This affects the way mpActivity
250cdf0e10cSrcweir     // works, but is performed here because of
251cdf0e10cSrcweir     // locality (we're fiddling with the additive mode
252cdf0e10cSrcweir     // here, anyway, and it's the only place where we
253cdf0e10cSrcweir     // do). OTOH, maybe the complete additive mode
254cdf0e10cSrcweir     // setup should be moved to the activities.
255cdf0e10cSrcweir 
256cdf0e10cSrcweir     // for simple by-animations, the SMIL spec
257cdf0e10cSrcweir     // requires us to emulate "0,by-value" value list
258cdf0e10cSrcweir     // behaviour, with additive mode forced to "sum",
259cdf0e10cSrcweir     // no matter what the input is
260cdf0e10cSrcweir     // (http://www.w3.org/TR/smil20/animation.html#adef-by).
261cdf0e10cSrcweir     if( mxAnimateNode->getBy().hasValue() &&
262cdf0e10cSrcweir         !mxAnimateNode->getTo().hasValue() &&
263cdf0e10cSrcweir         !mxAnimateNode->getFrom().hasValue() )
264cdf0e10cSrcweir     {
265cdf0e10cSrcweir         // force attribute mode to REPLACE (note the
266cdf0e10cSrcweir         // subtle discrepancy to the paragraph above,
267cdf0e10cSrcweir         // where SMIL requires SUM. This is internally
268cdf0e10cSrcweir         // handled by the FromToByActivity, and is
269cdf0e10cSrcweir         // because otherwise DOM values would not be
270cdf0e10cSrcweir         // handled correctly: the activity cannot
271cdf0e10cSrcweir         // determine whether an
272cdf0e10cSrcweir         // Activity::getUnderlyingValue() yields the
273cdf0e10cSrcweir         // DOM value, or already a summed-up conglomerate)
274cdf0e10cSrcweir         //
275cdf0e10cSrcweir         // Note that this poses problems with our
276cdf0e10cSrcweir         // hybrid activity duration (time or min number of frames),
277cdf0e10cSrcweir         // since if activities
278cdf0e10cSrcweir         // exceed their duration, wrong 'by' start
279cdf0e10cSrcweir         // values might arise ('Laser effect')
280cdf0e10cSrcweir         maAttributeLayerHolder.get()->setAdditiveMode(
281cdf0e10cSrcweir             animations::AnimationAdditiveMode::REPLACE );
282cdf0e10cSrcweir     }
283cdf0e10cSrcweir     else
284cdf0e10cSrcweir     {
285cdf0e10cSrcweir         // apply additive mode to newly created Attribute layer
286cdf0e10cSrcweir         maAttributeLayerHolder.get()->setAdditiveMode(
287cdf0e10cSrcweir             mxAnimateNode->getAdditive() );
288cdf0e10cSrcweir     }
289cdf0e10cSrcweir 
290cdf0e10cSrcweir     // fake normal animation behaviour, even if we
291cdf0e10cSrcweir     // show nothing.  This is the appropriate way to
292cdf0e10cSrcweir     // handle errors on Activity generation, because
293cdf0e10cSrcweir     // maybe all other effects on the slide are
294cdf0e10cSrcweir     // correctly initialized (but won't run, if we
295cdf0e10cSrcweir     // signal an error here)
296cdf0e10cSrcweir     if (mpActivity) {
297cdf0e10cSrcweir         // supply Activity (and the underlying Animation) with
298cdf0e10cSrcweir         // it's AttributeLayer, to perform the animation on
299cdf0e10cSrcweir         mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() );
300cdf0e10cSrcweir 
301cdf0e10cSrcweir         // add to activities queue
302cdf0e10cSrcweir         getContext().mrActivitiesQueue.addActivity( mpActivity );
303cdf0e10cSrcweir     }
304cdf0e10cSrcweir     else {
305cdf0e10cSrcweir         // Actually, DO generate the event for empty activity,
306cdf0e10cSrcweir         // to keep the chain of animations running
307cdf0e10cSrcweir         BaseNode::scheduleDeactivationEvent();
308cdf0e10cSrcweir     }
309cdf0e10cSrcweir }
310cdf0e10cSrcweir 
311cdf0e10cSrcweir void AnimationBaseNode::deactivate_st( NodeState eDestState )
312cdf0e10cSrcweir {
313cdf0e10cSrcweir     if (eDestState == FROZEN) {
314cdf0e10cSrcweir         if (mpActivity)
315cdf0e10cSrcweir             mpActivity->end();
316cdf0e10cSrcweir     }
317cdf0e10cSrcweir 
318cdf0e10cSrcweir     if (isDependentSubsettedShape()) {
319cdf0e10cSrcweir         // for dependent subsets, remove subset shape
320cdf0e10cSrcweir         // from layer, re-integrate subsetted part
321cdf0e10cSrcweir         // back into original shape. For independent
322cdf0e10cSrcweir         // subsets, we cannot make any assumptions
323cdf0e10cSrcweir         // about subset attribute state relative to
324cdf0e10cSrcweir         // master shape, thus, have to keep it. This
325cdf0e10cSrcweir         // will effectively re-integrate the subsetted
326cdf0e10cSrcweir         // part into the original shape (whose
327cdf0e10cSrcweir         // animation will hopefully have ended, too)
328cdf0e10cSrcweir 
329cdf0e10cSrcweir         // this statement will save a whole lot of
330cdf0e10cSrcweir         // sprites for iterated text effects, since
331cdf0e10cSrcweir         // those sprites will only exist during the
332cdf0e10cSrcweir         // actual lifetime of the effects
333cdf0e10cSrcweir         if (mpShapeSubset) {
334cdf0e10cSrcweir             mpShapeSubset->disableSubsetShape();
335cdf0e10cSrcweir         }
336cdf0e10cSrcweir     }
337cdf0e10cSrcweir 
338cdf0e10cSrcweir     if (eDestState == ENDED) {
339cdf0e10cSrcweir 
340cdf0e10cSrcweir         // no shape anymore, no layer needed:
341cdf0e10cSrcweir         maAttributeLayerHolder.reset();
342cdf0e10cSrcweir 
343cdf0e10cSrcweir         if (! isDependentSubsettedShape()) {
344cdf0e10cSrcweir 
345cdf0e10cSrcweir             // for all other shapes, removing the
346cdf0e10cSrcweir             // attribute layer quite possibly changes
347cdf0e10cSrcweir             // shape display. Thus, force update
348cdf0e10cSrcweir             AttributableShapeSharedPtr const pShape( getShape() );
349cdf0e10cSrcweir 
350cdf0e10cSrcweir             // don't anybody dare to check against
351cdf0e10cSrcweir             // pShape->isVisible() here, removing the
352cdf0e10cSrcweir             // attribute layer might actually make the
353cdf0e10cSrcweir             // shape invisible!
354cdf0e10cSrcweir             getContext().mpSubsettableShapeManager->notifyShapeUpdate( pShape );
355cdf0e10cSrcweir         }
356cdf0e10cSrcweir 
357cdf0e10cSrcweir         if (mpActivity) {
358cdf0e10cSrcweir             // kill activity, if still running
359cdf0e10cSrcweir             mpActivity->dispose();
360cdf0e10cSrcweir             mpActivity.reset();
361cdf0e10cSrcweir         }
362cdf0e10cSrcweir     }
363cdf0e10cSrcweir }
364cdf0e10cSrcweir 
365cdf0e10cSrcweir bool AnimationBaseNode::hasPendingAnimation() const
366cdf0e10cSrcweir {
367cdf0e10cSrcweir     // TODO(F1): This might not always be true. Are there 'inactive'
368cdf0e10cSrcweir     // animation nodes?
369cdf0e10cSrcweir     return true;
370cdf0e10cSrcweir }
371cdf0e10cSrcweir 
372cdf0e10cSrcweir #if defined(VERBOSE) && defined(DBG_UTIL)
373cdf0e10cSrcweir void AnimationBaseNode::showState() const
374cdf0e10cSrcweir {
375cdf0e10cSrcweir     BaseNode::showState();
376cdf0e10cSrcweir 
377cdf0e10cSrcweir     VERBOSE_TRACE( "AnimationBaseNode info: independent subset=%s",
378cdf0e10cSrcweir                    mbIsIndependentSubset ? "y" : "n" );
379cdf0e10cSrcweir }
380cdf0e10cSrcweir #endif
381cdf0e10cSrcweir 
382cdf0e10cSrcweir ActivitiesFactory::CommonParameters
383cdf0e10cSrcweir AnimationBaseNode::fillCommonParameters() const
384cdf0e10cSrcweir {
385cdf0e10cSrcweir     double nDuration = 0.0;
386cdf0e10cSrcweir 
387cdf0e10cSrcweir     // TODO(F3): Duration/End handling is barely there
388cdf0e10cSrcweir     if( !(mxAnimateNode->getDuration() >>= nDuration) ) {
389cdf0e10cSrcweir         mxAnimateNode->getEnd() >>= nDuration; // Wah.
390cdf0e10cSrcweir     }
391cdf0e10cSrcweir 
392cdf0e10cSrcweir     // minimal duration we fallback to (avoid 0 here!)
393cdf0e10cSrcweir     nDuration = ::std::max( 0.001, nDuration );
394cdf0e10cSrcweir 
395cdf0e10cSrcweir     const bool bAutoReverse( mxAnimateNode->getAutoReverse() );
396cdf0e10cSrcweir 
397cdf0e10cSrcweir     boost::optional<double> aRepeats;
398cdf0e10cSrcweir     double nRepeats = 0;
399cdf0e10cSrcweir     if( (mxAnimateNode->getRepeatCount() >>= nRepeats) ) {
400cdf0e10cSrcweir         aRepeats.reset( nRepeats );
401cdf0e10cSrcweir     }
402cdf0e10cSrcweir     else {
403cdf0e10cSrcweir         if( (mxAnimateNode->getRepeatDuration() >>= nRepeats) ) {
404cdf0e10cSrcweir             // when repeatDuration is given,
405cdf0e10cSrcweir             // autoreverse does _not_ modify the
406cdf0e10cSrcweir             // active duration. Thus, calc repeat
407cdf0e10cSrcweir             // count with already adapted simple
408cdf0e10cSrcweir             // duration (twice the specified duration)
409cdf0e10cSrcweir 
410cdf0e10cSrcweir             // convert duration back to repeat counts
411cdf0e10cSrcweir             if( bAutoReverse )
412cdf0e10cSrcweir                 aRepeats.reset( nRepeats / (2.0 * nDuration) );
413cdf0e10cSrcweir             else
414cdf0e10cSrcweir                 aRepeats.reset( nRepeats / nDuration );
415cdf0e10cSrcweir         }
416cdf0e10cSrcweir         else {
417cdf0e10cSrcweir             // no double value for both values - Timing::INDEFINITE?
418cdf0e10cSrcweir             animations::Timing eTiming;
419cdf0e10cSrcweir 
420cdf0e10cSrcweir             if( !(mxAnimateNode->getRepeatDuration() >>= eTiming) ||
421cdf0e10cSrcweir                 eTiming != animations::Timing_INDEFINITE )
422cdf0e10cSrcweir             {
423cdf0e10cSrcweir                 if( !(mxAnimateNode->getRepeatCount() >>= eTiming) ||
424cdf0e10cSrcweir                     eTiming != animations::Timing_INDEFINITE )
425cdf0e10cSrcweir                 {
426cdf0e10cSrcweir                     // no indefinite timing, no other values given -
427cdf0e10cSrcweir                     // use simple run, i.e. repeat of 1.0
428cdf0e10cSrcweir                     aRepeats.reset( 1.0 );
429cdf0e10cSrcweir                 }
430cdf0e10cSrcweir             }
431cdf0e10cSrcweir         }
432cdf0e10cSrcweir     }
433cdf0e10cSrcweir 
434cdf0e10cSrcweir     // calc accel/decel:
435cdf0e10cSrcweir     double nAcceleration = 0.0;
436cdf0e10cSrcweir     double nDeceleration = 0.0;
437cdf0e10cSrcweir     BaseNodeSharedPtr const pSelf( getSelf() );
438cdf0e10cSrcweir     for ( boost::shared_ptr<BaseNode> pNode( pSelf );
439cdf0e10cSrcweir           pNode; pNode = pNode->getParentNode() )
440cdf0e10cSrcweir     {
441cdf0e10cSrcweir         uno::Reference<animations::XAnimationNode> const xAnimationNode(
442cdf0e10cSrcweir             pNode->getXAnimationNode() );
443cdf0e10cSrcweir         nAcceleration = std::max( nAcceleration,
444cdf0e10cSrcweir                                   xAnimationNode->getAcceleration() );
445cdf0e10cSrcweir         nDeceleration = std::max( nDeceleration,
446cdf0e10cSrcweir                                   xAnimationNode->getDecelerate() );
447cdf0e10cSrcweir     }
448cdf0e10cSrcweir 
449cdf0e10cSrcweir     EventSharedPtr pEndEvent;
450cdf0e10cSrcweir     if (pSelf) {
451cdf0e10cSrcweir         pEndEvent = makeEvent(
452cdf0e10cSrcweir             boost::bind( &AnimationNode::deactivate, pSelf ),
453cdf0e10cSrcweir             "AnimationBaseNode::deactivate");
454cdf0e10cSrcweir     }
455cdf0e10cSrcweir 
456cdf0e10cSrcweir     // Calculate the minimum frame count that depends on the duration and
457cdf0e10cSrcweir     // the minimum frame count.
458cdf0e10cSrcweir     const sal_Int32 nMinFrameCount (basegfx::clamp<sal_Int32>(
459cdf0e10cSrcweir         basegfx::fround(nDuration * FrameRate::MinimumFramesPerSecond), 1, 10));
460cdf0e10cSrcweir 
461cdf0e10cSrcweir     return ActivitiesFactory::CommonParameters(
462cdf0e10cSrcweir         pEndEvent,
463cdf0e10cSrcweir         getContext().mrEventQueue,
464cdf0e10cSrcweir         getContext().mrActivitiesQueue,
465cdf0e10cSrcweir         nDuration,
466cdf0e10cSrcweir         nMinFrameCount,
467cdf0e10cSrcweir         bAutoReverse,
468cdf0e10cSrcweir         aRepeats,
469cdf0e10cSrcweir         nAcceleration,
470cdf0e10cSrcweir         nDeceleration,
471cdf0e10cSrcweir         getShape(),
472cdf0e10cSrcweir         getSlideSize());
473cdf0e10cSrcweir }
474cdf0e10cSrcweir 
475cdf0e10cSrcweir AttributableShapeSharedPtr AnimationBaseNode::getShape() const
476cdf0e10cSrcweir {
477cdf0e10cSrcweir     // any subsetting at all?
478cdf0e10cSrcweir     if (mpShapeSubset)
479cdf0e10cSrcweir         return mpShapeSubset->getSubsetShape();
480cdf0e10cSrcweir     else
481cdf0e10cSrcweir         return mpShape; // nope, plain shape always
482cdf0e10cSrcweir }
483cdf0e10cSrcweir 
484cdf0e10cSrcweir } // namespace internal
485cdf0e10cSrcweir } // namespace slideshow
486cdf0e10cSrcweir 
487