bump product version to 5.0.4.1
[LibreOffice.git] / slideshow / source / engine / animationnodes / animationnodefactory.cxx
blob15f9e079edbfe0ac75ee9c50359a677af053c6b6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 // must be first
22 #include <canvas/debug.hxx>
23 #include <canvas/verbosetrace.hxx>
25 #include <com/sun/star/drawing/XShape.hpp>
26 #include <com/sun/star/animations/XAnimate.hpp>
27 #include <com/sun/star/animations/AnimationNodeType.hpp>
28 #include <com/sun/star/presentation/EffectNodeType.hpp>
29 #include <com/sun/star/presentation/TextAnimationType.hpp>
30 #include <com/sun/star/animations/XAnimateSet.hpp>
31 #include <com/sun/star/animations/XIterateContainer.hpp>
32 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
33 #include <com/sun/star/animations/XAnimateMotion.hpp>
34 #include <com/sun/star/animations/XAnimateColor.hpp>
35 #include <com/sun/star/animations/XAnimateTransform.hpp>
36 #include <com/sun/star/animations/AnimationTransformType.hpp>
37 #include <com/sun/star/animations/XTransitionFilter.hpp>
38 #include <com/sun/star/animations/XAudio.hpp>
39 #include <com/sun/star/presentation/ParagraphTarget.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <basegfx/numeric/ftools.hxx>
43 #include "animationnodefactory.hxx"
44 #include "paralleltimecontainer.hxx"
45 #include "sequentialtimecontainer.hxx"
46 #include "propertyanimationnode.hxx"
47 #include "animationsetnode.hxx"
48 #include "animationpathmotionnode.hxx"
49 #include "animationcolornode.hxx"
50 #include "animationtransformnode.hxx"
51 #include "animationtransitionfilternode.hxx"
52 #include "animationaudionode.hxx"
53 #include "animationcommandnode.hxx"
54 #include "nodetools.hxx"
55 #include "tools.hxx"
57 #include <boost/shared_ptr.hpp>
59 using namespace ::com::sun::star;
61 namespace slideshow {
62 namespace internal {
64 namespace {
66 // forward declaration needed by NodeCreator
67 BaseNodeSharedPtr implCreateAnimationNode(
68 const uno::Reference< animations::XAnimationNode >& xNode,
69 const BaseContainerNodeSharedPtr& rParent,
70 const NodeContext& rContext );
72 class NodeCreator
74 public:
75 NodeCreator( BaseContainerNodeSharedPtr& rParent,
76 const NodeContext& rContext )
77 : mrParent( rParent ), mrContext( rContext ) {}
79 void operator()(
80 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
82 createChild( xChildNode, mrContext );
85 protected:
86 void createChild(
87 const uno::Reference< animations::XAnimationNode >& xChildNode,
88 const NodeContext& rContext ) const
90 BaseNodeSharedPtr pChild( implCreateAnimationNode( xChildNode,
91 mrParent,
92 rContext ) );
94 OSL_ENSURE( pChild,
95 "NodeCreator::operator(): child creation failed" );
97 // TODO(Q1): This yields circular references, which, it seems, is
98 // unavoidable here
99 if( pChild )
100 mrParent->appendChildNode( pChild );
103 BaseContainerNodeSharedPtr& mrParent;
104 const NodeContext& mrContext;
107 /** Same as NodeCreator, only that NodeContext's
108 SubsetShape is cloned for every child node.
110 This is used for iterated animation node generation
112 class CloningNodeCreator : private NodeCreator
114 public:
115 CloningNodeCreator( BaseContainerNodeSharedPtr& rParent,
116 const NodeContext& rContext )
117 : NodeCreator( rParent, rContext ) {}
119 void operator()(
120 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
122 NodeContext aContext( mrContext );
124 // TODO(Q1): There's a catch here. If you clone a
125 // subset whose actual subsetting has already been
126 // realized (i.e. if enableSubsetShape() has been
127 // called already), and the original of your clone
128 // goes out of scope, then your subset will be
129 // gone (SubsettableShapeManager::revokeSubset() be
130 // called). As of now, this behaviour is not
131 // triggered here (we either clone, XOR we enable
132 // subset initially), but one might consider
133 // reworking DrawShape/ShapeSubset to avoid this.
135 // clone ShapeSubset, since each node needs their
136 // own version of the ShapeSubset (otherwise,
137 // e.g. activity counting does not work - subset
138 // would be removed after first animation node
139 // disables it).
141 // NOTE: this is only a problem for animation
142 // nodes that explicitly call
143 // disableSubsetShape(). Independent shape subsets
144 // (like those created for ParagraphTargets)
145 // solely rely on the ShapeSubset destructor to
146 // normalize things, which does the right thing
147 // here: the subset is only removed after _the
148 // last_ animation node releases the shared ptr.
149 aContext.mpMasterShapeSubset.reset(
150 new ShapeSubset( *aContext.mpMasterShapeSubset ) );
152 createChild( xChildNode, aContext );
156 /** Create animation nodes for text iterations
158 This method clones the animation nodes below xIterNode
159 for every iterated shape entity.
161 bool implCreateIteratedNodes(
162 const uno::Reference< animations::XIterateContainer >& xIterNode,
163 BaseContainerNodeSharedPtr& rParent,
164 const NodeContext& rContext )
166 ENSURE_OR_THROW( xIterNode.is(),
167 "implCreateIteratedNodes(): Invalid node" );
169 const double nIntervalTimeout( xIterNode->getIterateInterval() );
171 // valid iterate interval? We're ruling out monstrous
172 // values here, to avoid pseudo 'hangs' in the
173 // presentation
174 if( nIntervalTimeout < 0.0 ||
175 nIntervalTimeout > 1000.0 )
177 return false; // not an active iteration
180 if( ::basegfx::fTools::equalZero( nIntervalTimeout ) )
181 OSL_TRACE( "implCreateIteratedNodes(): "
182 "iterate interval close to zero, there's "
183 "no point in defining such an effect "
184 "(visually equivalent to whole-shape effect)" );
186 // Determine target shape (or subset)
187 // ==================================
189 // TODO(E1): I'm not too sure what to expect here...
190 ENSURE_OR_RETURN_FALSE(
191 xIterNode->getTarget().hasValue(),
192 "implCreateIteratedNodes(): no target on ITERATE node" );
194 uno::Reference< drawing::XShape > xTargetShape( xIterNode->getTarget(),
195 uno::UNO_QUERY );
197 presentation::ParagraphTarget aTarget;
198 sal_Int16 nSubItem( xIterNode->getSubItem() );
199 bool bParagraphTarget( false );
201 if( !xTargetShape.is() )
203 // no shape provided. Maybe a ParagraphTarget?
204 if( !(xIterNode->getTarget() >>= aTarget) )
205 ENSURE_OR_RETURN_FALSE(
206 false,
207 "implCreateIteratedNodes(): could not extract any "
208 "target information" );
210 xTargetShape = aTarget.Shape;
212 ENSURE_OR_RETURN_FALSE(
213 xTargetShape.is(),
214 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
216 // we've a paragraph target to iterate over, thus,
217 // the whole animation container refers only to
218 // the text
219 nSubItem = presentation::ShapeAnimationSubType::ONLY_TEXT;
221 bParagraphTarget = true;
224 // Lookup shape, and fill NodeContext
225 // ==================================
227 AttributableShapeSharedPtr pTargetShape(
228 lookupAttributableShape( rContext.maContext.mpSubsettableShapeManager,
229 xTargetShape ) );
231 const DocTreeNodeSupplier& rTreeNodeSupplier(
232 pTargetShape->getTreeNodeSupplier() );
234 ShapeSubsetSharedPtr pTargetSubset;
236 NodeContext aContext( rContext );
238 // paragraph targets already need a subset as the
239 // master shape (they're representing only a single
240 // paragraph)
241 if( bParagraphTarget )
243 ENSURE_OR_RETURN_FALSE(
244 aTarget.Paragraph >= 0 &&
245 rTreeNodeSupplier.getNumberOfTreeNodes(
246 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) > aTarget.Paragraph,
247 "implCreateIteratedNodes(): paragraph index out of range" );
249 pTargetSubset.reset(
250 new ShapeSubset(
251 pTargetShape,
252 // retrieve index aTarget.Paragraph of
253 // type PARAGRAPH from this shape
254 rTreeNodeSupplier.getTreeNode(
255 aTarget.Paragraph,
256 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ),
257 rContext.maContext.mpSubsettableShapeManager ) );
259 // iterate target is not the whole shape, but only
260 // the selected paragraph - subset _must_ be
261 // independent, to be able to affect visibility
262 // independent of master shape
263 aContext.mbIsIndependentSubset = true;
265 // already enable parent subset right here, to
266 // make potentially generated subsets subtract
267 // their content from the parent subset (and not
268 // the original shape). Otherwise, already
269 // subsetted parents (e.g. paragraphs) would not
270 // have their characters removed, when the child
271 // iterations start.
272 // Furthermore, the setup of initial shape
273 // attributes of course needs the subset shape
274 // generated, to apply e.g. visibility changes.
275 pTargetSubset->enableSubsetShape();
277 else
279 pTargetSubset.reset(
280 new ShapeSubset( pTargetShape,
281 rContext.maContext.mpSubsettableShapeManager ));
284 aContext.mpMasterShapeSubset = pTargetSubset;
285 uno::Reference< animations::XAnimationNode > xNode( xIterNode,
286 uno::UNO_QUERY_THROW );
288 // Generate subsets
289 // ================
291 if( bParagraphTarget ||
292 nSubItem != presentation::ShapeAnimationSubType::ONLY_TEXT )
294 // prepend with animations for
295 // full Shape (will be subtracted
296 // from the subset parts within
297 // the Shape::createSubset()
298 // method). For ONLY_TEXT effects,
299 // we skip this part, to animate
300 // only the text.
302 // OR
304 // prepend with subset animation for full
305 // _paragraph_, from which the individual
306 // paragraph subsets are subtracted. Note that the
307 // subitem is superfluous here, we always assume
308 // ONLY_TEXT, if a paragraph is referenced as the
309 // master of an iteration effect.
310 NodeCreator aCreator( rParent, aContext );
311 if( !for_each_childNode( xNode, aCreator ) )
313 ENSURE_OR_RETURN_FALSE(
314 false,
315 "implCreateIteratedNodes(): iterated child node creation failed" );
319 // TODO(F2): This does not do the correct
320 // thing. Having nSubItem be set to ONLY_BACKGROUND
321 // should result in the text staying unanimated in the
322 // foreground, while the shape moves in the background
323 // (this behaviour is perfectly possible with the
324 // slideshow engine, only that the text won't be
325 // currently visible, because animations are always in
326 // the foreground)
327 if( nSubItem != presentation::ShapeAnimationSubType::ONLY_BACKGROUND )
329 // determine type of subitem iteration (logical
330 // text unit to animate)
331 DocTreeNode::NodeType eIterateNodeType(
332 DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL );
334 switch( xIterNode->getIterateType() )
336 case presentation::TextAnimationType::BY_PARAGRAPH:
337 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH;
338 break;
340 case presentation::TextAnimationType::BY_WORD:
341 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_WORD;
342 break;
344 case presentation::TextAnimationType::BY_LETTER:
345 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL;
346 break;
348 default:
349 ENSURE_OR_THROW(
350 false, "implCreateIteratedNodes(): "
351 "Unexpected IterateType on XIterateContainer");
352 break;
355 if( bParagraphTarget &&
356 eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_WORD &&
357 eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL )
359 // will not animate the whole paragraph, when
360 // only the paragraph is animated at all.
361 OSL_FAIL( "implCreateIteratedNodes(): Ignoring paragraph iteration for paragraph master" );
363 else
365 // setup iteration parameters
368 // iterate target is the whole shape (or the
369 // whole parent subshape), thus, can save
370 // loads of subset shapes by generating them
371 // only when the effects become active -
372 // before and after the effect active
373 // duration, all attributes are shared by
374 // master shape and subset (since the iterated
375 // effects are all the same).
376 aContext.mbIsIndependentSubset = false;
378 // determine number of nodes for given subitem
379 // type
380 sal_Int32 nTreeNodes( 0 );
381 if( bParagraphTarget )
383 // create the iterated subset _relative_ to
384 // the given paragraph index (i.e. animate the
385 // given subset type, but only when it's part
386 // of the given paragraph)
387 nTreeNodes = rTreeNodeSupplier.getNumberOfSubsetTreeNodes(
388 pTargetSubset->getSubset(),
389 eIterateNodeType );
391 else
393 // generate normal subset
394 nTreeNodes = rTreeNodeSupplier.getNumberOfTreeNodes(
395 eIterateNodeType );
399 // iterate node, generate copies of the children for each subset
402 // NodeContext::mnStartDelay contains additional node delay.
403 // This will make the duplicated nodes for each iteration start
404 // increasingly later.
405 aContext.mnStartDelay = nIntervalTimeout;
407 for( sal_Int32 i=0; i<nTreeNodes; ++i )
409 // create subset with the corresponding tree nodes
410 if( bParagraphTarget )
412 // create subsets relative to paragraph subset
413 aContext.mpMasterShapeSubset.reset(
414 new ShapeSubset(
415 pTargetSubset,
416 rTreeNodeSupplier.getSubsetTreeNode(
417 pTargetSubset->getSubset(),
419 eIterateNodeType ) ) );
421 else
423 // create subsets from main shape
424 aContext.mpMasterShapeSubset.reset(
425 new ShapeSubset( pTargetSubset,
426 rTreeNodeSupplier.getTreeNode(
428 eIterateNodeType ) ) );
431 CloningNodeCreator aCreator( rParent, aContext );
432 if( !for_each_childNode( xNode, aCreator ) )
434 ENSURE_OR_RETURN_FALSE(
435 false, "implCreateIteratedNodes(): "
436 "iterated child node creation failed" );
439 aContext.mnStartDelay += nIntervalTimeout;
444 // done with iterate child generation
445 return true;
448 BaseNodeSharedPtr implCreateAnimationNode(
449 const uno::Reference< animations::XAnimationNode >& xNode,
450 const BaseContainerNodeSharedPtr& rParent,
451 const NodeContext& rContext )
453 ENSURE_OR_THROW( xNode.is(),
454 "implCreateAnimationNode(): invalid XAnimationNode" );
456 BaseNodeSharedPtr pCreatedNode;
457 BaseContainerNodeSharedPtr pCreatedContainer;
459 // create the internal node, corresponding to xNode
460 switch( xNode->getType() )
462 case animations::AnimationNodeType::CUSTOM:
463 OSL_FAIL( "implCreateAnimationNode(): "
464 "CUSTOM not yet implemented" );
465 return pCreatedNode;
467 case animations::AnimationNodeType::PAR:
468 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
469 new ParallelTimeContainer( xNode, rParent, rContext ) );
470 break;
472 case animations::AnimationNodeType::ITERATE:
473 // map iterate container to ParallelTimeContainer.
474 // the iterating functionality is to be found
475 // below, (see method implCreateIteratedNodes)
476 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
477 new ParallelTimeContainer( xNode, rParent, rContext ) );
478 break;
480 case animations::AnimationNodeType::SEQ:
481 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
482 new SequentialTimeContainer( xNode, rParent, rContext ) );
483 break;
485 case animations::AnimationNodeType::ANIMATE:
486 pCreatedNode.reset( new PropertyAnimationNode(
487 xNode, rParent, rContext ) );
488 break;
490 case animations::AnimationNodeType::SET:
491 pCreatedNode.reset( new AnimationSetNode(
492 xNode, rParent, rContext ) );
493 break;
495 case animations::AnimationNodeType::ANIMATEMOTION:
496 pCreatedNode.reset( new AnimationPathMotionNode(
497 xNode, rParent, rContext ) );
498 break;
500 case animations::AnimationNodeType::ANIMATECOLOR:
501 pCreatedNode.reset( new AnimationColorNode(
502 xNode, rParent, rContext ) );
503 break;
505 case animations::AnimationNodeType::ANIMATETRANSFORM:
506 pCreatedNode.reset( new AnimationTransformNode(
507 xNode, rParent, rContext ) );
508 break;
510 case animations::AnimationNodeType::TRANSITIONFILTER:
511 pCreatedNode.reset( new AnimationTransitionFilterNode(
512 xNode, rParent, rContext ) );
513 break;
515 case animations::AnimationNodeType::AUDIO:
516 pCreatedNode.reset( new AnimationAudioNode(
517 xNode, rParent, rContext ) );
518 break;
520 case animations::AnimationNodeType::COMMAND:
521 pCreatedNode.reset( new AnimationCommandNode(
522 xNode, rParent, rContext ) );
523 break;
525 default:
526 OSL_FAIL( "implCreateAnimationNode(): "
527 "invalid AnimationNodeType" );
528 return pCreatedNode;
531 // TODO(Q1): This yields circular references, which, it seems, is
532 // unavoidable here
534 // HACK: node objects need shared_ptr to themselves,
535 // which we pass them here.
536 pCreatedNode->setSelf( pCreatedNode );
538 // if we've got a container node object, recursively add
539 // its children
540 if( pCreatedContainer )
542 uno::Reference< animations::XIterateContainer > xIterNode(
543 xNode, uno::UNO_QUERY );
545 // when this node is an XIterateContainer with
546 // active iterations, this method will generate
547 // the appropriate children
548 if( xIterNode.is() )
550 // note that implCreateIteratedNodes() might
551 // choose not to generate any child nodes
552 // (e.g. when the iterate timeout is outside
553 // sensible limits). Then, no child nodes are
554 // generated at all, since typically, child
555 // node attribute are incomplete for iteration
556 // children.
557 implCreateIteratedNodes( xIterNode,
558 pCreatedContainer,
559 rContext );
561 else
563 // no iterate subset node, just plain child generation now
564 NodeCreator aCreator( pCreatedContainer, rContext );
565 if( !for_each_childNode( xNode, aCreator ) )
567 OSL_FAIL( "implCreateAnimationNode(): "
568 "child node creation failed" );
569 return BaseNodeSharedPtr();
574 return pCreatedNode;
577 } // anon namespace
579 AnimationNodeSharedPtr AnimationNodeFactory::createAnimationNode(
580 const uno::Reference< animations::XAnimationNode >& xNode,
581 const ::basegfx::B2DVector& rSlideSize,
582 const SlideShowContext& rContext )
584 ENSURE_OR_THROW(
585 xNode.is(),
586 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
588 return BaseNodeSharedPtr( implCreateAnimationNode(
589 xNode,
590 BaseContainerNodeSharedPtr(), // no parent
591 NodeContext( rContext,
592 rSlideSize )));
595 #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
596 void AnimationNodeFactory::showTree( AnimationNodeSharedPtr& pRootNode )
598 if( pRootNode )
599 DEBUG_NODES_SHOWTREE( boost::dynamic_pointer_cast<BaseContainerNode>(
600 pRootNode).get() );
602 #endif
604 } // namespace internal
605 } // namespace slideshow
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */