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 .
21 #include <com/sun/star/drawing/XShape.hpp>
22 #include <com/sun/star/animations/AnimationNodeType.hpp>
23 #include <com/sun/star/presentation/TextAnimationType.hpp>
24 #include <com/sun/star/animations/XIterateContainer.hpp>
25 #include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
26 #include <com/sun/star/presentation/ParagraphTarget.hpp>
27 #include <basegfx/numeric/ftools.hxx>
28 #include <sal/log.hxx>
30 #include <animationnodefactory.hxx>
31 #include "paralleltimecontainer.hxx"
32 #include "sequentialtimecontainer.hxx"
33 #include "propertyanimationnode.hxx"
34 #include "animationsetnode.hxx"
35 #include "animationpathmotionnode.hxx"
36 #include "animationphysicsnode.hxx"
37 #include "animationcolornode.hxx"
38 #include "animationtransformnode.hxx"
39 #include "animationtransitionfilternode.hxx"
40 #include "animationaudionode.hxx"
41 #include "animationcommandnode.hxx"
42 #include "nodetools.hxx"
47 using namespace ::com::sun::star
;
49 namespace slideshow::internal
{
53 // forward declaration needed by NodeCreator
54 BaseNodeSharedPtr
implCreateAnimationNode(
55 const uno::Reference
< animations::XAnimationNode
>& xNode
,
56 const BaseContainerNodeSharedPtr
& rParent
,
57 const NodeContext
& rContext
);
62 NodeCreator( BaseContainerNodeSharedPtr
& rParent
,
63 const NodeContext
& rContext
)
64 : mrParent( rParent
), mrContext( rContext
) {}
67 const uno::Reference
< animations::XAnimationNode
>& xChildNode
) const
69 createChild( xChildNode
, mrContext
);
74 const uno::Reference
< animations::XAnimationNode
>& xChildNode
,
75 const NodeContext
& rContext
) const
77 BaseNodeSharedPtr
pChild( implCreateAnimationNode( xChildNode
,
82 "NodeCreator::operator(): child creation failed" );
84 // TODO(Q1): This yields circular references, which, it seems, is
87 mrParent
->appendChildNode( pChild
);
90 BaseContainerNodeSharedPtr
& mrParent
;
91 const NodeContext
& mrContext
;
94 /** Same as NodeCreator, only that NodeContext's
95 SubsetShape is cloned for every child node.
97 This is used for iterated animation node generation
99 class CloningNodeCreator
: private NodeCreator
102 CloningNodeCreator( BaseContainerNodeSharedPtr
& rParent
,
103 const NodeContext
& rContext
)
104 : NodeCreator( rParent
, rContext
) {}
107 const uno::Reference
< animations::XAnimationNode
>& xChildNode
) const
109 NodeContext
aContext( mrContext
);
111 // TODO(Q1): There's a catch here. If you clone a
112 // subset whose actual subsetting has already been
113 // realized (i.e. if enableSubsetShape() has been
114 // called already), and the original of your clone
115 // goes out of scope, then your subset will be
116 // gone (SubsettableShapeManager::revokeSubset() be
117 // called). As of now, this behaviour is not
118 // triggered here (we either clone, XOR we enable
119 // subset initially), but one might consider
120 // reworking DrawShape/ShapeSubset to avoid this.
122 // clone ShapeSubset, since each node needs their
123 // own version of the ShapeSubset (otherwise,
124 // e.g. activity counting does not work - subset
125 // would be removed after first animation node
128 // NOTE: this is only a problem for animation
129 // nodes that explicitly call
130 // disableSubsetShape(). Independent shape subsets
131 // (like those created for ParagraphTargets)
132 // solely rely on the ShapeSubset destructor to
133 // normalize things, which does the right thing
134 // here: the subset is only removed after _the
135 // last_ animation node releases the shared ptr.
136 aContext
.mpMasterShapeSubset
=
137 std::make_shared
<ShapeSubset
>( *aContext
.mpMasterShapeSubset
);
139 createChild( xChildNode
, aContext
);
143 /** Create animation nodes for text iterations
145 This method clones the animation nodes below xIterNode
146 for every iterated shape entity.
148 bool implCreateIteratedNodes(
149 const uno::Reference
< animations::XIterateContainer
>& xIterNode
,
150 BaseContainerNodeSharedPtr
& rParent
,
151 const NodeContext
& rContext
)
153 ENSURE_OR_THROW( xIterNode
.is(),
154 "implCreateIteratedNodes(): Invalid node" );
156 const double nIntervalTimeout( xIterNode
->getIterateInterval() );
158 // valid iterate interval? We're ruling out monstrous
159 // values here, to avoid pseudo 'hangs' in the
161 if( nIntervalTimeout
< 0.0 ||
162 nIntervalTimeout
> 1000.0 )
164 return false; // not an active iteration
167 if( ::basegfx::fTools::equalZero( nIntervalTimeout
) )
168 SAL_INFO("slideshow", "implCreateIteratedNodes(): "
169 "iterate interval close to zero, there's "
170 "no point in defining such an effect "
171 "(visually equivalent to whole-shape effect)" );
173 // Determine target shape (or subset)
174 // ==================================
176 // TODO(E1): I'm not too sure what to expect here...
177 ENSURE_OR_RETURN_FALSE(
178 xIterNode
->getTarget().hasValue(),
179 "implCreateIteratedNodes(): no target on ITERATE node" );
181 uno::Reference
< drawing::XShape
> xTargetShape( xIterNode
->getTarget(),
184 presentation::ParagraphTarget aTarget
;
185 sal_Int16
nSubItem( xIterNode
->getSubItem() );
186 bool bParagraphTarget( false );
188 if( !xTargetShape
.is() )
190 // no shape provided. Maybe a ParagraphTarget?
191 if( !(xIterNode
->getTarget() >>= aTarget
) )
192 ENSURE_OR_RETURN_FALSE(
194 "implCreateIteratedNodes(): could not extract any "
195 "target information" );
197 xTargetShape
= aTarget
.Shape
;
199 ENSURE_OR_RETURN_FALSE(
201 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
203 // we've a paragraph target to iterate over, thus,
204 // the whole animation container refers only to
206 nSubItem
= presentation::ShapeAnimationSubType::ONLY_TEXT
;
208 bParagraphTarget
= true;
211 // Lookup shape, and fill NodeContext
212 // ==================================
214 AttributableShapeSharedPtr
pTargetShape(
215 lookupAttributableShape( rContext
.maContext
.mpSubsettableShapeManager
,
218 const DocTreeNodeSupplier
& rTreeNodeSupplier(
219 pTargetShape
->getTreeNodeSupplier() );
221 ShapeSubsetSharedPtr pTargetSubset
;
223 NodeContext
aContext( rContext
);
225 // paragraph targets already need a subset as the
226 // master shape (they're representing only a single
228 if( bParagraphTarget
)
230 ENSURE_OR_RETURN_FALSE(
231 aTarget
.Paragraph
>= 0 &&
232 rTreeNodeSupplier
.getNumberOfTreeNodes(
233 DocTreeNode::NodeType::LogicalParagraph
) > aTarget
.Paragraph
,
234 "implCreateIteratedNodes(): paragraph index out of range" );
237 std::make_shared
<ShapeSubset
>(
239 // retrieve index aTarget.Paragraph of
240 // type PARAGRAPH from this shape
241 rTreeNodeSupplier
.getTreeNode(
243 DocTreeNode::NodeType::LogicalParagraph
),
244 rContext
.maContext
.mpSubsettableShapeManager
);
246 // iterate target is not the whole shape, but only
247 // the selected paragraph - subset _must_ be
248 // independent, to be able to affect visibility
249 // independent of master shape
250 aContext
.mbIsIndependentSubset
= true;
252 // already enable parent subset right here, to
253 // make potentially generated subsets subtract
254 // their content from the parent subset (and not
255 // the original shape). Otherwise, already
256 // subsetted parents (e.g. paragraphs) would not
257 // have their characters removed, when the child
259 // Furthermore, the setup of initial shape
260 // attributes of course needs the subset shape
261 // generated, to apply e.g. visibility changes.
262 pTargetSubset
->enableSubsetShape();
267 std::make_shared
<ShapeSubset
>( pTargetShape
,
268 rContext
.maContext
.mpSubsettableShapeManager
);
271 aContext
.mpMasterShapeSubset
= pTargetSubset
;
272 uno::Reference
< animations::XAnimationNode
> xNode( xIterNode
,
273 uno::UNO_QUERY_THROW
);
278 if( bParagraphTarget
||
279 nSubItem
!= presentation::ShapeAnimationSubType::ONLY_TEXT
)
281 // prepend with animations for
282 // full Shape (will be subtracted
283 // from the subset parts within
284 // the Shape::createSubset()
285 // method). For ONLY_TEXT effects,
286 // we skip this part, to animate
291 // prepend with subset animation for full
292 // _paragraph_, from which the individual
293 // paragraph subsets are subtracted. Note that the
294 // subitem is superfluous here, we always assume
295 // ONLY_TEXT, if a paragraph is referenced as the
296 // master of an iteration effect.
297 NodeCreator
aCreator( rParent
, aContext
);
298 if( !for_each_childNode( xNode
, aCreator
) )
300 ENSURE_OR_RETURN_FALSE(
302 "implCreateIteratedNodes(): iterated child node creation failed" );
306 // TODO(F2): This does not do the correct
307 // thing. Having nSubItem be set to ONLY_BACKGROUND
308 // should result in the text staying unanimated in the
309 // foreground, while the shape moves in the background
310 // (this behaviour is perfectly possible with the
311 // slideshow engine, only that the text won't be
312 // currently visible, because animations are always in
314 if( nSubItem
!= presentation::ShapeAnimationSubType::ONLY_BACKGROUND
)
316 // determine type of subitem iteration (logical
317 // text unit to animate)
318 DocTreeNode::NodeType
eIterateNodeType(
319 DocTreeNode::NodeType::LogicalCharacterCell
);
321 switch( xIterNode
->getIterateType() )
323 case presentation::TextAnimationType::BY_PARAGRAPH
:
324 eIterateNodeType
= DocTreeNode::NodeType::LogicalParagraph
;
327 case presentation::TextAnimationType::BY_WORD
:
328 eIterateNodeType
= DocTreeNode::NodeType::LogicalWord
;
331 case presentation::TextAnimationType::BY_LETTER
:
332 eIterateNodeType
= DocTreeNode::NodeType::LogicalCharacterCell
;
337 false, "implCreateIteratedNodes(): "
338 "Unexpected IterateType on XIterateContainer");
342 if( bParagraphTarget
&&
343 eIterateNodeType
!= DocTreeNode::NodeType::LogicalWord
&&
344 eIterateNodeType
!= DocTreeNode::NodeType::LogicalCharacterCell
)
346 // will not animate the whole paragraph, when
347 // only the paragraph is animated at all.
348 OSL_FAIL( "implCreateIteratedNodes(): Ignoring paragraph iteration for paragraph master" );
352 // setup iteration parameters
355 // iterate target is the whole shape (or the
356 // whole parent subshape), thus, can save
357 // loads of subset shapes by generating them
358 // only when the effects become active -
359 // before and after the effect active
360 // duration, all attributes are shared by
361 // master shape and subset (since the iterated
362 // effects are all the same).
363 aContext
.mbIsIndependentSubset
= false;
365 // determine number of nodes for given subitem
367 sal_Int32
nTreeNodes( 0 );
368 if( bParagraphTarget
)
370 // create the iterated subset _relative_ to
371 // the given paragraph index (i.e. animate the
372 // given subset type, but only when it's part
373 // of the given paragraph)
374 nTreeNodes
= rTreeNodeSupplier
.getNumberOfSubsetTreeNodes(
375 pTargetSubset
->getSubset(),
380 // generate normal subset
381 nTreeNodes
= rTreeNodeSupplier
.getNumberOfTreeNodes(
386 // iterate node, generate copies of the children for each subset
389 // NodeContext::mnStartDelay contains additional node delay.
390 // This will make the duplicated nodes for each iteration start
391 // increasingly later.
392 aContext
.mnStartDelay
= nIntervalTimeout
;
394 for( sal_Int32 i
=0; i
<nTreeNodes
; ++i
)
396 // create subset with the corresponding tree nodes
397 if( bParagraphTarget
)
399 // create subsets relative to paragraph subset
400 aContext
.mpMasterShapeSubset
=
401 std::make_shared
<ShapeSubset
>(
403 rTreeNodeSupplier
.getSubsetTreeNode(
404 pTargetSubset
->getSubset(),
406 eIterateNodeType
) );
410 // create subsets from main shape
411 aContext
.mpMasterShapeSubset
=
412 std::make_shared
<ShapeSubset
>( pTargetSubset
,
413 rTreeNodeSupplier
.getTreeNode(
415 eIterateNodeType
) );
418 CloningNodeCreator
aCreator( rParent
, aContext
);
419 if( !for_each_childNode( xNode
, aCreator
) )
421 ENSURE_OR_RETURN_FALSE(
422 false, "implCreateIteratedNodes(): "
423 "iterated child node creation failed" );
426 aContext
.mnStartDelay
+= nIntervalTimeout
;
431 // done with iterate child generation
435 BaseNodeSharedPtr
implCreateAnimationNode(
436 const uno::Reference
< animations::XAnimationNode
>& xNode
,
437 const BaseContainerNodeSharedPtr
& rParent
,
438 const NodeContext
& rContext
)
440 ENSURE_OR_THROW( xNode
.is(),
441 "implCreateAnimationNode(): invalid XAnimationNode" );
443 BaseNodeSharedPtr pCreatedNode
;
444 BaseContainerNodeSharedPtr pCreatedContainer
;
446 // create the internal node, corresponding to xNode
447 switch( xNode
->getType() )
449 case animations::AnimationNodeType::CUSTOM
:
450 OSL_FAIL( "implCreateAnimationNode(): "
451 "CUSTOM not yet implemented" );
454 case animations::AnimationNodeType::PAR
:
455 pCreatedNode
= pCreatedContainer
=
456 std::make_shared
<ParallelTimeContainer
>( xNode
, rParent
, rContext
);
459 case animations::AnimationNodeType::ITERATE
:
460 // map iterate container to ParallelTimeContainer.
461 // the iterating functionality is to be found
462 // below, (see method implCreateIteratedNodes)
463 pCreatedNode
= pCreatedContainer
=
464 std::make_shared
<ParallelTimeContainer
>( xNode
, rParent
, rContext
);
467 case animations::AnimationNodeType::SEQ
:
468 pCreatedNode
= pCreatedContainer
=
469 std::make_shared
<SequentialTimeContainer
>( xNode
, rParent
, rContext
);
472 case animations::AnimationNodeType::ANIMATE
:
473 pCreatedNode
= std::make_shared
<PropertyAnimationNode
>(
474 xNode
, rParent
, rContext
);
477 case animations::AnimationNodeType::SET
:
478 pCreatedNode
= std::make_shared
<AnimationSetNode
>(
479 xNode
, rParent
, rContext
);
482 case animations::AnimationNodeType::ANIMATEMOTION
:
483 pCreatedNode
= std::make_shared
<AnimationPathMotionNode
>(
484 xNode
, rParent
, rContext
);
487 case animations::AnimationNodeType::ANIMATECOLOR
:
488 pCreatedNode
= std::make_shared
<AnimationColorNode
>(
489 xNode
, rParent
, rContext
);
492 case animations::AnimationNodeType::ANIMATETRANSFORM
:
493 pCreatedNode
= std::make_shared
<AnimationTransformNode
>(
494 xNode
, rParent
, rContext
);
497 case animations::AnimationNodeType::ANIMATEPHYSICS
:
498 pCreatedNode
= std::make_shared
<AnimationPhysicsNode
>(
499 xNode
, rParent
, rContext
);
502 case animations::AnimationNodeType::TRANSITIONFILTER
:
503 pCreatedNode
= std::make_shared
<AnimationTransitionFilterNode
>(
504 xNode
, rParent
, rContext
);
507 case animations::AnimationNodeType::AUDIO
:
508 pCreatedNode
= std::make_shared
<AnimationAudioNode
>(
509 xNode
, rParent
, rContext
);
512 case animations::AnimationNodeType::COMMAND
:
513 pCreatedNode
= std::make_shared
<AnimationCommandNode
>(
514 xNode
, rParent
, rContext
);
518 OSL_FAIL( "implCreateAnimationNode(): "
519 "invalid AnimationNodeType" );
523 // TODO(Q1): This yields circular references, which, it seems, is
526 // HACK: node objects need shared_ptr to themselves,
527 // which we pass them here.
528 pCreatedNode
->setSelf( pCreatedNode
);
530 // if we've got a container node object, recursively add
532 if( pCreatedContainer
)
534 uno::Reference
< animations::XIterateContainer
> xIterNode(
535 xNode
, uno::UNO_QUERY
);
537 // when this node is an XIterateContainer with
538 // active iterations, this method will generate
539 // the appropriate children
542 // note that implCreateIteratedNodes() might
543 // choose not to generate any child nodes
544 // (e.g. when the iterate timeout is outside
545 // sensible limits). Then, no child nodes are
546 // generated at all, since typically, child
547 // node attribute are incomplete for iteration
549 implCreateIteratedNodes( xIterNode
,
555 // no iterate subset node, just plain child generation now
556 NodeCreator
aCreator( pCreatedContainer
, rContext
);
557 if( !for_each_childNode( xNode
, aCreator
) )
559 OSL_FAIL( "implCreateAnimationNode(): "
560 "child node creation failed" );
561 return BaseNodeSharedPtr();
571 AnimationNodeSharedPtr
AnimationNodeFactory::createAnimationNode(
572 const uno::Reference
< animations::XAnimationNode
>& xNode
,
573 const ::basegfx::B2DVector
& rSlideSize
,
574 const SlideShowContext
& rContext
)
578 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
580 return implCreateAnimationNode(
582 BaseContainerNodeSharedPtr(), // no parent
583 NodeContext( rContext
,
587 #if defined(DBG_UTIL)
588 void AnimationNodeFactory::showTree( AnimationNodeSharedPtr
const & pRootNode
)
591 DEBUG_NODES_SHOWTREE( std::dynamic_pointer_cast
<BaseContainerNode
>(
596 } // namespace slideshow
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */