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