1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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"
57 #include <boost/shared_ptr.hpp>
59 using namespace ::com::sun::star
;
66 // forward declaration needed by NodeCreator
67 BaseNodeSharedPtr
implCreateAnimationNode(
68 const uno::Reference
< animations::XAnimationNode
>& xNode
,
69 const BaseContainerNodeSharedPtr
& rParent
,
70 const NodeContext
& rContext
);
75 NodeCreator( BaseContainerNodeSharedPtr
& rParent
,
76 const NodeContext
& rContext
)
77 : mrParent( rParent
), mrContext( rContext
) {}
80 const uno::Reference
< animations::XAnimationNode
>& xChildNode
) const
82 createChild( xChildNode
, mrContext
);
87 const uno::Reference
< animations::XAnimationNode
>& xChildNode
,
88 const NodeContext
& rContext
) const
90 BaseNodeSharedPtr
pChild( implCreateAnimationNode( xChildNode
,
95 "NodeCreator::operator(): child creation failed" );
97 // TODO(Q1): This yields circular references, which, it seems, is
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
115 CloningNodeCreator( BaseContainerNodeSharedPtr
& rParent
,
116 const NodeContext
& rContext
)
117 : NodeCreator( rParent
, rContext
) {}
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
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
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(),
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(
207 "implCreateIteratedNodes(): could not extract any "
208 "target information" );
210 xTargetShape
= aTarget
.Shape
;
212 ENSURE_OR_RETURN_FALSE(
214 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
216 // we've a paragraph target to iterate over, thus,
217 // the whole animation container refers only to
219 nSubItem
= presentation::ShapeAnimationSubType::ONLY_TEXT
;
221 bParagraphTarget
= true;
224 // Lookup shape, and fill NodeContext
225 // ==================================
227 AttributableShapeSharedPtr
pTargetShape(
228 lookupAttributableShape( rContext
.maContext
.mpSubsettableShapeManager
,
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
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" );
252 // retrieve index aTarget.Paragraph of
253 // type PARAGRAPH from this shape
254 rTreeNodeSupplier
.getTreeNode(
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
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();
280 new ShapeSubset( pTargetShape
,
281 rContext
.maContext
.mpSubsettableShapeManager
));
284 aContext
.mpMasterShapeSubset
= pTargetSubset
;
285 uno::Reference
< animations::XAnimationNode
> xNode( xIterNode
,
286 uno::UNO_QUERY_THROW
);
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
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(
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
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
;
340 case presentation::TextAnimationType::BY_WORD
:
341 eIterateNodeType
= DocTreeNode::NODETYPE_LOGICAL_WORD
;
344 case presentation::TextAnimationType::BY_LETTER
:
345 eIterateNodeType
= DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL
;
350 false, "implCreateIteratedNodes(): "
351 "Unexpected IterateType on XIterateContainer");
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" );
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
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(),
393 // generate normal subset
394 nTreeNodes
= rTreeNodeSupplier
.getNumberOfTreeNodes(
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(
416 rTreeNodeSupplier
.getSubsetTreeNode(
417 pTargetSubset
->getSubset(),
419 eIterateNodeType
) ) );
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
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" );
467 case animations::AnimationNodeType::PAR
:
468 pCreatedNode
= pCreatedContainer
= BaseContainerNodeSharedPtr(
469 new ParallelTimeContainer( xNode
, rParent
, rContext
) );
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
) );
480 case animations::AnimationNodeType::SEQ
:
481 pCreatedNode
= pCreatedContainer
= BaseContainerNodeSharedPtr(
482 new SequentialTimeContainer( xNode
, rParent
, rContext
) );
485 case animations::AnimationNodeType::ANIMATE
:
486 pCreatedNode
.reset( new PropertyAnimationNode(
487 xNode
, rParent
, rContext
) );
490 case animations::AnimationNodeType::SET
:
491 pCreatedNode
.reset( new AnimationSetNode(
492 xNode
, rParent
, rContext
) );
495 case animations::AnimationNodeType::ANIMATEMOTION
:
496 pCreatedNode
.reset( new AnimationPathMotionNode(
497 xNode
, rParent
, rContext
) );
500 case animations::AnimationNodeType::ANIMATECOLOR
:
501 pCreatedNode
.reset( new AnimationColorNode(
502 xNode
, rParent
, rContext
) );
505 case animations::AnimationNodeType::ANIMATETRANSFORM
:
506 pCreatedNode
.reset( new AnimationTransformNode(
507 xNode
, rParent
, rContext
) );
510 case animations::AnimationNodeType::TRANSITIONFILTER
:
511 pCreatedNode
.reset( new AnimationTransitionFilterNode(
512 xNode
, rParent
, rContext
) );
515 case animations::AnimationNodeType::AUDIO
:
516 pCreatedNode
.reset( new AnimationAudioNode(
517 xNode
, rParent
, rContext
) );
520 case animations::AnimationNodeType::COMMAND
:
521 pCreatedNode
.reset( new AnimationCommandNode(
522 xNode
, rParent
, rContext
) );
526 OSL_FAIL( "implCreateAnimationNode(): "
527 "invalid AnimationNodeType" );
531 // TODO(Q1): This yields circular references, which, it seems, is
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
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
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
557 implCreateIteratedNodes( xIterNode
,
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();
579 AnimationNodeSharedPtr
AnimationNodeFactory::createAnimationNode(
580 const uno::Reference
< animations::XAnimationNode
>& xNode
,
581 const ::basegfx::B2DVector
& rSlideSize
,
582 const SlideShowContext
& rContext
)
586 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
588 return BaseNodeSharedPtr( implCreateAnimationNode(
590 BaseContainerNodeSharedPtr(), // no parent
591 NodeContext( rContext
,
595 #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
596 void AnimationNodeFactory::showTree( AnimationNodeSharedPtr
& pRootNode
)
599 DEBUG_NODES_SHOWTREE( boost::dynamic_pointer_cast
<BaseContainerNode
>(
604 } // namespace internal
605 } // namespace slideshow
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */