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