1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: animationnodefactory.cxx,v $
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"
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"
71 #include <boost/bind.hpp>
73 using namespace ::com::sun::star
;
80 // forward declaration needed by NodeCreator
81 BaseNodeSharedPtr
implCreateAnimationNode(
82 const uno::Reference
< animations::XAnimationNode
>& xNode
,
83 const BaseContainerNodeSharedPtr
& rParent
,
84 const NodeContext
& rContext
);
89 NodeCreator( BaseContainerNodeSharedPtr
& rParent
,
90 const NodeContext
& rContext
)
91 : mrParent( rParent
), mrContext( rContext
) {}
94 const uno::Reference
< animations::XAnimationNode
>& xChildNode
) const
96 createChild( xChildNode
, mrContext
);
101 const uno::Reference
< animations::XAnimationNode
>& xChildNode
,
102 const NodeContext
& rContext
) const
104 BaseNodeSharedPtr
pChild( implCreateAnimationNode( xChildNode
,
109 "NodeCreator::operator(): child creation failed" );
111 // TODO(Q1): This yields circular references, which, it seems, is
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
129 CloningNodeCreator( BaseContainerNodeSharedPtr
& rParent
,
130 const NodeContext
& rContext
)
131 : NodeCreator( rParent
, rContext
) {}
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
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
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...
205 xIterNode
->getTarget().hasValue(),
206 "implCreateIteratedNodes(): no target on ITERATE node" );
208 uno::Reference
< drawing::XShape
> xTargetShape( xIterNode
->getTarget(),
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
) )
221 "implCreateIteratedNodes(): could not extract any "
222 "target information" );
224 xTargetShape
= aTarget
.Shape
;
228 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
230 // we've a paragraph target to iterate over, thus,
231 // the whole animation container refers only to
233 nSubItem
= presentation::ShapeAnimationSubType::ONLY_TEXT
;
235 bParagraphTarget
= true;
238 // Lookup shape, and fill NodeContext
239 // ==================================
241 AttributableShapeSharedPtr
pTargetShape(
242 lookupAttributableShape( rContext
.maContext
.mpSubsettableShapeManager
,
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
255 if( bParagraphTarget
)
258 aTarget
.Paragraph
>= 0 &&
259 rTreeNodeSupplier
.getNumberOfTreeNodes(
260 DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH
) > aTarget
.Paragraph
,
261 "implCreateIteratedNodes(): paragraph index out of range" );
266 // retrieve index aTarget.Paragraph of
267 // type PARAGRAPH from this shape
268 rTreeNodeSupplier
.getTreeNode(
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
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();
294 new ShapeSubset( pTargetShape
,
295 rContext
.maContext
.mpSubsettableShapeManager
));
298 aContext
.mpMasterShapeSubset
= pTargetSubset
;
299 uno::Reference
< animations::XAnimationNode
> xNode( xIterNode
,
300 uno::UNO_QUERY_THROW
);
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
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
,
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
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
;
355 case presentation::TextAnimationType::BY_WORD
:
356 eIterateNodeType
= DocTreeNode::NODETYPE_LOGICAL_WORD
;
359 case presentation::TextAnimationType::BY_LETTER
:
360 eIterateNodeType
= DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL
;
365 false, "implCreateIteratedNodes(): "
366 "Unexpected IterateType on XIterateContainer");
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.
377 "implCreateIteratedNodes(): Ignoring paragraph iteration for paragraph master" );
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
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(),
409 // generate normal subset
410 nTreeNodes
= rTreeNodeSupplier
.getNumberOfTreeNodes(
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(
432 rTreeNodeSupplier
.getSubsetTreeNode(
433 pTargetSubset
->getSubset(),
435 eIterateNodeType
) ) );
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
,
452 false, "implCreateIteratedNodes(): "
453 "iterated child node creation failed" );
456 aContext
.mnStartDelay
+= nIntervalTimeout
;
461 // done with iterate child generation
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" );
484 case animations::AnimationNodeType::PAR
:
485 pCreatedNode
= pCreatedContainer
= BaseContainerNodeSharedPtr(
486 new ParallelTimeContainer( xNode
, rParent
, rContext
) );
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
) );
497 case animations::AnimationNodeType::SEQ
:
498 pCreatedNode
= pCreatedContainer
= BaseContainerNodeSharedPtr(
499 new SequentialTimeContainer( xNode
, rParent
, rContext
) );
502 case animations::AnimationNodeType::ANIMATE
:
503 pCreatedNode
.reset( new PropertyAnimationNode(
504 xNode
, rParent
, rContext
) );
507 case animations::AnimationNodeType::SET
:
508 pCreatedNode
.reset( new AnimationSetNode(
509 xNode
, rParent
, rContext
) );
512 case animations::AnimationNodeType::ANIMATEMOTION
:
513 pCreatedNode
.reset( new AnimationPathMotionNode(
514 xNode
, rParent
, rContext
) );
517 case animations::AnimationNodeType::ANIMATECOLOR
:
518 pCreatedNode
.reset( new AnimationColorNode(
519 xNode
, rParent
, rContext
) );
522 case animations::AnimationNodeType::ANIMATETRANSFORM
:
523 pCreatedNode
.reset( new AnimationTransformNode(
524 xNode
, rParent
, rContext
) );
527 case animations::AnimationNodeType::TRANSITIONFILTER
:
528 pCreatedNode
.reset( new AnimationTransitionFilterNode(
529 xNode
, rParent
, rContext
) );
532 case animations::AnimationNodeType::AUDIO
:
533 pCreatedNode
.reset( new AnimationAudioNode(
534 xNode
, rParent
, rContext
) );
537 case animations::AnimationNodeType::COMMAND
:
538 pCreatedNode
.reset( new AnimationCommandNode(
539 xNode
, rParent
, rContext
) );
543 OSL_ENSURE( false, "implCreateAnimationNode(): "
544 "invalid AnimationNodeType" );
548 // TODO(Q1): This yields circular references, which, it seems, is
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
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
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
574 implCreateIteratedNodes( xIterNode
,
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();
596 AnimationNodeSharedPtr
AnimationNodeFactory::createAnimationNode(
597 const uno::Reference
< animations::XAnimationNode
>& xNode
,
598 const ::basegfx::B2DVector
& rSlideSize
,
599 const SlideShowContext
& rContext
)
603 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
605 return BaseNodeSharedPtr( implCreateAnimationNode(
607 BaseContainerNodeSharedPtr(), // no parent
608 NodeContext( rContext
,
612 #if defined(VERBOSE) && defined(DBG_UTIL)
613 void AnimationNodeFactory::showTree( AnimationNodeSharedPtr
& pRootNode
)
616 DEBUG_NODES_SHOWTREE( boost::dynamic_pointer_cast
<BaseContainerNode
>(
621 } // namespace internal
622 } // namespace slideshow