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