Update ooo320-m1
[ooovba.git] / slideshow / source / engine / animationnodes / animationnodefactory.cxx
blob548e100de045a44d1a8b8250199202ae5792c469
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: animationnodefactory.cxx,v $
10 * $Revision: 1.14 $
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>
38 #include <com/sun/star/drawing/XShape.hpp>
39 #include <com/sun/star/animations/XAnimate.hpp>
40 #include <com/sun/star/animations/AnimationNodeType.hpp>
41 #include <com/sun/star/presentation/EffectNodeType.hpp>
42 #include <com/sun/star/presentation/TextAnimationType.hpp>
43 #include <com/sun/star/animations/XAnimateSet.hpp>
44 #include <com/sun/star/animations/XIterateContainer.hpp>
45 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
46 #include <com/sun/star/animations/XAnimateMotion.hpp>
47 #include <com/sun/star/animations/XAnimateColor.hpp>
48 #include <com/sun/star/animations/XAnimateTransform.hpp>
49 #include <com/sun/star/animations/AnimationTransformType.hpp>
50 #include <com/sun/star/animations/XTransitionFilter.hpp>
51 #include <com/sun/star/animations/XAudio.hpp>
52 #include <com/sun/star/presentation/ParagraphTarget.hpp>
53 #include <com/sun/star/beans/XPropertySet.hpp>
54 #include <animations/animationnodehelper.hxx>
55 #include <basegfx/numeric/ftools.hxx>
57 #include "animationnodefactory.hxx"
58 #include "paralleltimecontainer.hxx"
59 #include "sequentialtimecontainer.hxx"
60 #include "propertyanimationnode.hxx"
61 #include "animationsetnode.hxx"
62 #include "animationpathmotionnode.hxx"
63 #include "animationcolornode.hxx"
64 #include "animationtransformnode.hxx"
65 #include "animationtransitionfilternode.hxx"
66 #include "animationaudionode.hxx"
67 #include "animationcommandnode.hxx"
68 #include "nodetools.hxx"
69 #include "tools.hxx"
71 #include <boost/bind.hpp>
73 using namespace ::com::sun::star;
75 namespace slideshow {
76 namespace internal {
78 namespace {
80 // forward declaration needed by NodeCreator
81 BaseNodeSharedPtr implCreateAnimationNode(
82 const uno::Reference< animations::XAnimationNode >& xNode,
83 const BaseContainerNodeSharedPtr& rParent,
84 const NodeContext& rContext );
86 class NodeCreator
88 public:
89 NodeCreator( BaseContainerNodeSharedPtr& rParent,
90 const NodeContext& rContext )
91 : mrParent( rParent ), mrContext( rContext ) {}
93 void operator()(
94 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
96 createChild( xChildNode, mrContext );
99 protected:
100 void createChild(
101 const uno::Reference< animations::XAnimationNode >& xChildNode,
102 const NodeContext& rContext ) const
104 BaseNodeSharedPtr pChild( implCreateAnimationNode( xChildNode,
105 mrParent,
106 rContext ) );
108 OSL_ENSURE( pChild,
109 "NodeCreator::operator(): child creation failed" );
111 // TODO(Q1): This yields circular references, which, it seems, is
112 // unavoidable here
113 if( pChild )
114 mrParent->appendChildNode( pChild );
117 BaseContainerNodeSharedPtr& mrParent;
118 const NodeContext& mrContext;
121 /** Same as NodeCreator, only that NodeContext's
122 SubsetShape is cloned for every child node.
124 This is used for iterated animation node generation
126 class CloningNodeCreator : private NodeCreator
128 public:
129 CloningNodeCreator( BaseContainerNodeSharedPtr& rParent,
130 const NodeContext& rContext )
131 : NodeCreator( rParent, rContext ) {}
133 void operator()(
134 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
136 NodeContext aContext( mrContext );
138 // TODO(Q1): There's a catch here. If you clone a
139 // subset whose actual subsetting has already been
140 // realized (i.e. if enableSubsetShape() has been
141 // called already), and the original of your clone
142 // goes out of scope, then your subset will be
143 // gone (SubsettableShapeManager::revokeSubset() be
144 // called). As of now, this behaviour is not
145 // triggered here (we either clone, XOR we enable
146 // subset initially), but one might consider
147 // reworking DrawShape/ShapeSubset to avoid this.
149 // clone ShapeSubset, since each node needs their
150 // own version of the ShapeSubset (otherwise,
151 // e.g. activity counting does not work - subset
152 // would be removed after first animation node
153 // disables it).
155 // NOTE: this is only a problem for animation
156 // nodes that explicitely call
157 // disableSubsetShape(). Independent shape subsets
158 // (like those created for ParagraphTargets)
159 // solely rely on the ShapeSubset destructor to
160 // normalize things, which does the right thing
161 // here: the subset is only removed after _the
162 // last_ animation node releases the shared ptr.
163 aContext.mpMasterShapeSubset.reset(
164 new ShapeSubset( *aContext.mpMasterShapeSubset ) );
166 createChild( xChildNode, aContext );
170 /** Create animation nodes for text iterations
172 This method clones the animation nodes below xIterNode
173 for every iterated shape entity.
175 bool implCreateIteratedNodes(
176 const uno::Reference< animations::XIterateContainer >& xIterNode,
177 BaseContainerNodeSharedPtr& rParent,
178 const NodeContext& rContext )
180 ENSURE_OR_THROW( xIterNode.is(),
181 "implCreateIteratedNodes(): Invalid node" );
183 const double nIntervalTimeout( xIterNode->getIterateInterval() );
185 // valid iterate interval? We're ruling out monstrous
186 // values here, to avoid pseudo 'hangs' in the
187 // presentation
188 if( nIntervalTimeout < 0.0 ||
189 nIntervalTimeout > 1000.0 )
191 return false; // not an active iteration
194 if( ::basegfx::fTools::equalZero( nIntervalTimeout ) )
195 OSL_TRACE( "implCreateIteratedNodes(): "
196 "iterate interval close to zero, there's "
197 "no point in defining such an effect "
198 "(visually equivalent to whole-shape effect)" );
200 // Determine target shape (or subset)
201 // ==================================
203 // TODO(E1): I'm not too sure what to expect here...
204 ENSURE_OR_RETURN(
205 xIterNode->getTarget().hasValue(),
206 "implCreateIteratedNodes(): no target on ITERATE node" );
208 uno::Reference< drawing::XShape > xTargetShape( xIterNode->getTarget(),
209 uno::UNO_QUERY );
211 presentation::ParagraphTarget aTarget;
212 sal_Int16 nSubItem( xIterNode->getSubItem() );
213 bool bParagraphTarget( false );
215 if( !xTargetShape.is() )
217 // no shape provided. Maybe a ParagraphTarget?
218 if( !(xIterNode->getTarget() >>= aTarget) )
219 ENSURE_OR_RETURN(
220 false,
221 "implCreateIteratedNodes(): could not extract any "
222 "target information" );
224 xTargetShape = aTarget.Shape;
226 ENSURE_OR_RETURN(
227 xTargetShape.is(),
228 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
230 // we've a paragraph target to iterate over, thus,
231 // the whole animation container refers only to
232 // the text
233 nSubItem = presentation::ShapeAnimationSubType::ONLY_TEXT;
235 bParagraphTarget = true;
238 // Lookup shape, and fill NodeContext
239 // ==================================
241 AttributableShapeSharedPtr pTargetShape(
242 lookupAttributableShape( rContext.maContext.mpSubsettableShapeManager,
243 xTargetShape ) );
245 const DocTreeNodeSupplier& rTreeNodeSupplier(
246 pTargetShape->getTreeNodeSupplier() );
248 ShapeSubsetSharedPtr pTargetSubset;
250 NodeContext aContext( rContext );
252 // paragraph targets already need a subset as the
253 // master shape (they're representing only a single
254 // paragraph)
255 if( bParagraphTarget )
257 ENSURE_OR_RETURN(
258 aTarget.Paragraph >= 0 &&
259 rTreeNodeSupplier.getNumberOfTreeNodes(
260 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) > aTarget.Paragraph,
261 "implCreateIteratedNodes(): paragraph index out of range" );
263 pTargetSubset.reset(
264 new ShapeSubset(
265 pTargetShape,
266 // retrieve index aTarget.Paragraph of
267 // type PARAGRAPH from this shape
268 rTreeNodeSupplier.getTreeNode(
269 aTarget.Paragraph,
270 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ),
271 rContext.maContext.mpSubsettableShapeManager ) );
273 // iterate target is not the whole shape, but only
274 // the selected paragraph - subset _must_ be
275 // independent, to be able to affect visibility
276 // independent of master shape
277 aContext.mbIsIndependentSubset = true;
279 // already enable parent subset right here, to
280 // make potentially generated subsets subtract
281 // their content from the parent subset (and not
282 // the original shape). Otherwise, already
283 // subsetted parents (e.g. paragraphs) would not
284 // have their characters removed, when the child
285 // iterations start.
286 // Furthermore, the setup of initial shape
287 // attributes of course needs the subset shape
288 // generated, to apply e.g. visibility changes.
289 pTargetSubset->enableSubsetShape();
291 else
293 pTargetSubset.reset(
294 new ShapeSubset( pTargetShape,
295 rContext.maContext.mpSubsettableShapeManager ));
298 aContext.mpMasterShapeSubset = pTargetSubset;
299 uno::Reference< animations::XAnimationNode > xNode( xIterNode,
300 uno::UNO_QUERY_THROW );
302 // Generate subsets
303 // ================
305 if( bParagraphTarget ||
306 nSubItem != presentation::ShapeAnimationSubType::ONLY_TEXT )
308 // prepend with animations for
309 // full Shape (will be subtracted
310 // from the subset parts within
311 // the Shape::createSubset()
312 // method). For ONLY_TEXT effects,
313 // we skip this part, to animate
314 // only the text.
316 // OR
318 // prepend with subset animation for full
319 // _paragraph_, from which the individual
320 // paragraph subsets are subtracted. Note that the
321 // subitem is superfluous here, we always assume
322 // ONLY_TEXT, if a paragraph is referenced as the
323 // master of an iteration effect.
324 NodeCreator aCreator( rParent, aContext );
325 if( !::anim::for_each_childNode( xNode,
326 aCreator ) )
328 ENSURE_OR_RETURN(
329 false,
330 "implCreateIteratedNodes(): iterated child node creation failed" );
334 // TODO(F2): This does not do the correct
335 // thing. Having nSubItem be set to ONLY_BACKGROUND
336 // should result in the text staying unanimated in the
337 // foreground, while the shape moves in the background
338 // (this behaviour is perfectly possible with the
339 // slideshow engine, only that the text won't be
340 // currently visible, because animations are always in
341 // the foreground)
342 if( nSubItem != presentation::ShapeAnimationSubType::ONLY_BACKGROUND )
344 // determine type of subitem iteration (logical
345 // text unit to animate)
346 DocTreeNode::NodeType eIterateNodeType(
347 DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL );
349 switch( xIterNode->getIterateType() )
351 case presentation::TextAnimationType::BY_PARAGRAPH:
352 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH;
353 break;
355 case presentation::TextAnimationType::BY_WORD:
356 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_WORD;
357 break;
359 case presentation::TextAnimationType::BY_LETTER:
360 eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL;
361 break;
363 default:
364 ENSURE_OR_THROW(
365 false, "implCreateIteratedNodes(): "
366 "Unexpected IterateType on XIterateContainer");
367 break;
370 if( bParagraphTarget &&
371 eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_WORD &&
372 eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL )
374 // will not animate the whole paragraph, when
375 // only the paragraph is animated at all.
376 OSL_ENSURE( false,
377 "implCreateIteratedNodes(): Ignoring paragraph iteration for paragraph master" );
379 else
381 // setup iteration parameters
382 // --------------------------
384 // iterate target is the whole shape (or the
385 // whole parent subshape), thus, can save
386 // loads of subset shapes by generating them
387 // only when the effects become active -
388 // before and after the effect active
389 // duration, all attributes are shared by
390 // master shape and subset (since the iterated
391 // effects are all the same).
392 aContext.mbIsIndependentSubset = false;
394 // determine number of nodes for given subitem
395 // type
396 sal_Int32 nTreeNodes( 0 );
397 if( bParagraphTarget )
399 // create the iterated subset _relative_ to
400 // the given paragraph index (i.e. animate the
401 // given subset type, but only when it's part
402 // of the given paragraph)
403 nTreeNodes = rTreeNodeSupplier.getNumberOfSubsetTreeNodes(
404 pTargetSubset->getSubset(),
405 eIterateNodeType );
407 else
409 // generate normal subset
410 nTreeNodes = rTreeNodeSupplier.getNumberOfTreeNodes(
411 eIterateNodeType );
415 // iterate node, generate copies of the children for each subset
416 // -------------------------------------------------------------
418 // NodeContext::mnStartDelay contains additional node delay.
419 // This will make the duplicated nodes for each iteration start
420 // increasingly later.
421 aContext.mnStartDelay = nIntervalTimeout;
423 for( sal_Int32 i=0; i<nTreeNodes; ++i )
425 // create subset with the corresponding tree nodes
426 if( bParagraphTarget )
428 // create subsets relative to paragraph subset
429 aContext.mpMasterShapeSubset.reset(
430 new ShapeSubset(
431 pTargetSubset,
432 rTreeNodeSupplier.getSubsetTreeNode(
433 pTargetSubset->getSubset(),
435 eIterateNodeType ) ) );
437 else
439 // create subsets from main shape
440 aContext.mpMasterShapeSubset.reset(
441 new ShapeSubset( pTargetSubset,
442 rTreeNodeSupplier.getTreeNode(
444 eIterateNodeType ) ) );
447 CloningNodeCreator aCreator( rParent, aContext );
448 if( !::anim::for_each_childNode( xNode,
449 aCreator ) )
451 ENSURE_OR_RETURN(
452 false, "implCreateIteratedNodes(): "
453 "iterated child node creation failed" );
456 aContext.mnStartDelay += nIntervalTimeout;
461 // done with iterate child generation
462 return true;
465 BaseNodeSharedPtr implCreateAnimationNode(
466 const uno::Reference< animations::XAnimationNode >& xNode,
467 const BaseContainerNodeSharedPtr& rParent,
468 const NodeContext& rContext )
470 ENSURE_OR_THROW( xNode.is(),
471 "implCreateAnimationNode(): invalid XAnimationNode" );
473 BaseNodeSharedPtr pCreatedNode;
474 BaseContainerNodeSharedPtr pCreatedContainer;
476 // create the internal node, corresponding to xNode
477 switch( xNode->getType() )
479 case animations::AnimationNodeType::CUSTOM:
480 OSL_ENSURE( false, "implCreateAnimationNode(): "
481 "CUSTOM not yet implemented" );
482 return pCreatedNode;
484 case animations::AnimationNodeType::PAR:
485 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
486 new ParallelTimeContainer( xNode, rParent, rContext ) );
487 break;
489 case animations::AnimationNodeType::ITERATE:
490 // map iterate container to ParallelTimeContainer.
491 // the iterating functionality is to be found
492 // below, (see method implCreateIteratedNodes)
493 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
494 new ParallelTimeContainer( xNode, rParent, rContext ) );
495 break;
497 case animations::AnimationNodeType::SEQ:
498 pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
499 new SequentialTimeContainer( xNode, rParent, rContext ) );
500 break;
502 case animations::AnimationNodeType::ANIMATE:
503 pCreatedNode.reset( new PropertyAnimationNode(
504 xNode, rParent, rContext ) );
505 break;
507 case animations::AnimationNodeType::SET:
508 pCreatedNode.reset( new AnimationSetNode(
509 xNode, rParent, rContext ) );
510 break;
512 case animations::AnimationNodeType::ANIMATEMOTION:
513 pCreatedNode.reset( new AnimationPathMotionNode(
514 xNode, rParent, rContext ) );
515 break;
517 case animations::AnimationNodeType::ANIMATECOLOR:
518 pCreatedNode.reset( new AnimationColorNode(
519 xNode, rParent, rContext ) );
520 break;
522 case animations::AnimationNodeType::ANIMATETRANSFORM:
523 pCreatedNode.reset( new AnimationTransformNode(
524 xNode, rParent, rContext ) );
525 break;
527 case animations::AnimationNodeType::TRANSITIONFILTER:
528 pCreatedNode.reset( new AnimationTransitionFilterNode(
529 xNode, rParent, rContext ) );
530 break;
532 case animations::AnimationNodeType::AUDIO:
533 pCreatedNode.reset( new AnimationAudioNode(
534 xNode, rParent, rContext ) );
535 break;
537 case animations::AnimationNodeType::COMMAND:
538 pCreatedNode.reset( new AnimationCommandNode(
539 xNode, rParent, rContext ) );
540 break;
542 default:
543 OSL_ENSURE( false, "implCreateAnimationNode(): "
544 "invalid AnimationNodeType" );
545 return pCreatedNode;
548 // TODO(Q1): This yields circular references, which, it seems, is
549 // unavoidable here
551 // HACK: node objects need shared_ptr to themselves,
552 // which we pass them here.
553 pCreatedNode->setSelf( pCreatedNode );
555 // if we've got a container node object, recursively add
556 // its children
557 if( pCreatedContainer )
559 uno::Reference< animations::XIterateContainer > xIterNode(
560 xNode, uno::UNO_QUERY );
562 // when this node is an XIterateContainer with
563 // active iterations, this method will generate
564 // the appropriate children
565 if( xIterNode.is() )
567 // note that implCreateIteratedNodes() might
568 // choose not to generate any child nodes
569 // (e.g. when the iterate timeout is outside
570 // sensible limits). Then, no child nodes are
571 // generated at all, since typically, child
572 // node attribute are incomplete for iteration
573 // children.
574 implCreateIteratedNodes( xIterNode,
575 pCreatedContainer,
576 rContext );
578 else
580 // no iterate subset node, just plain child generation now
581 NodeCreator aCreator( pCreatedContainer, rContext );
582 if( !::anim::for_each_childNode( xNode, aCreator ) )
584 OSL_ENSURE( false, "implCreateAnimationNode(): "
585 "child node creation failed" );
586 return BaseNodeSharedPtr();
591 return pCreatedNode;
594 } // anon namespace
596 AnimationNodeSharedPtr AnimationNodeFactory::createAnimationNode(
597 const uno::Reference< animations::XAnimationNode >& xNode,
598 const ::basegfx::B2DVector& rSlideSize,
599 const SlideShowContext& rContext )
601 ENSURE_OR_THROW(
602 xNode.is(),
603 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
605 return BaseNodeSharedPtr( implCreateAnimationNode(
606 xNode,
607 BaseContainerNodeSharedPtr(), // no parent
608 NodeContext( rContext,
609 rSlideSize )));
612 #if defined(VERBOSE) && defined(DBG_UTIL)
613 void AnimationNodeFactory::showTree( AnimationNodeSharedPtr& pRootNode )
615 if( pRootNode )
616 DEBUG_NODES_SHOWTREE( boost::dynamic_pointer_cast<BaseContainerNode>(
617 pRootNode).get() );
619 #endif
621 } // namespace internal
622 } // namespace slideshow