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 <basegfx/numeric/ftools.hxx>
42 :
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"
55 : #include "tools.hxx"
56 :
57 : #include <boost/shared_ptr.hpp>
58 :
59 : using namespace ::com::sun::star;
60 :
61 : namespace slideshow {
62 : namespace internal {
63 :
64 : namespace {
65 :
66 : // forward declaration needed by NodeCreator
67 : BaseNodeSharedPtr implCreateAnimationNode(
68 : const uno::Reference< animations::XAnimationNode >& xNode,
69 : const BaseContainerNodeSharedPtr& rParent,
70 : const NodeContext& rContext );
71 :
72 : class NodeCreator
73 : {
74 : public:
75 0 : NodeCreator( BaseContainerNodeSharedPtr& rParent,
76 : const NodeContext& rContext )
77 0 : : mrParent( rParent ), mrContext( rContext ) {}
78 :
79 0 : void operator()(
80 : const uno::Reference< animations::XAnimationNode >& xChildNode ) const
81 : {
82 0 : createChild( xChildNode, mrContext );
83 0 : }
84 :
85 : protected:
86 0 : void createChild(
87 : const uno::Reference< animations::XAnimationNode >& xChildNode,
88 : const NodeContext& rContext ) const
89 : {
90 : BaseNodeSharedPtr pChild( implCreateAnimationNode( xChildNode,
91 : mrParent,
92 0 : rContext ) );
93 :
94 : OSL_ENSURE( pChild,
95 : "NodeCreator::operator(): child creation failed" );
96 :
97 : // TODO(Q1): This yields circular references, which, it seems, is
98 : // unavoidable here
99 0 : if( pChild )
100 0 : mrParent->appendChildNode( pChild );
101 0 : }
102 :
103 : BaseContainerNodeSharedPtr& mrParent;
104 : const NodeContext& mrContext;
105 : };
106 :
107 : /** Same as NodeCreator, only that NodeContext's
108 : SubsetShape is cloned for every child node.
109 :
110 : This is used for iterated animation node generation
111 : */
112 : class CloningNodeCreator : private NodeCreator
113 : {
114 : public:
115 0 : CloningNodeCreator( BaseContainerNodeSharedPtr& rParent,
116 : const NodeContext& rContext )
117 0 : : NodeCreator( rParent, rContext ) {}
118 :
119 0 : void operator()(
120 : const uno::Reference< animations::XAnimationNode >& xChildNode ) const
121 : {
122 0 : NodeContext aContext( mrContext );
123 :
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.
134 :
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
139 : // disables it).
140 :
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 0 : new ShapeSubset( *aContext.mpMasterShapeSubset ) );
151 :
152 0 : createChild( xChildNode, aContext );
153 0 : }
154 : };
155 :
156 : /** Create animation nodes for text iterations
157 :
158 : This method clones the animation nodes below xIterNode
159 : for every iterated shape entity.
160 : */
161 0 : bool implCreateIteratedNodes(
162 : const uno::Reference< animations::XIterateContainer >& xIterNode,
163 : BaseContainerNodeSharedPtr& rParent,
164 : const NodeContext& rContext )
165 : {
166 0 : ENSURE_OR_THROW( xIterNode.is(),
167 : "implCreateIteratedNodes(): Invalid node" );
168 :
169 0 : const double nIntervalTimeout( xIterNode->getIterateInterval() );
170 :
171 : // valid iterate interval? We're ruling out monstrous
172 : // values here, to avoid pseudo 'hangs' in the
173 : // presentation
174 0 : if( nIntervalTimeout < 0.0 ||
175 0 : nIntervalTimeout > 1000.0 )
176 : {
177 0 : return false; // not an active iteration
178 : }
179 :
180 0 : 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)" );
185 :
186 : // Determine target shape (or subset)
187 : // ==================================
188 :
189 : // TODO(E1): I'm not too sure what to expect here...
190 0 : ENSURE_OR_RETURN_FALSE(
191 : xIterNode->getTarget().hasValue(),
192 : "implCreateIteratedNodes(): no target on ITERATE node" );
193 :
194 0 : uno::Reference< drawing::XShape > xTargetShape( xIterNode->getTarget(),
195 0 : uno::UNO_QUERY );
196 :
197 0 : presentation::ParagraphTarget aTarget;
198 0 : sal_Int16 nSubItem( xIterNode->getSubItem() );
199 0 : bool bParagraphTarget( false );
200 :
201 0 : if( !xTargetShape.is() )
202 : {
203 : // no shape provided. Maybe a ParagraphTarget?
204 0 : if( !(xIterNode->getTarget() >>= aTarget) )
205 0 : ENSURE_OR_RETURN_FALSE(
206 : false,
207 : "implCreateIteratedNodes(): could not extract any "
208 : "target information" );
209 :
210 0 : xTargetShape = aTarget.Shape;
211 :
212 0 : ENSURE_OR_RETURN_FALSE(
213 : xTargetShape.is(),
214 : "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
215 :
216 : // we've a paragraph target to iterate over, thus,
217 : // the whole animation container refers only to
218 : // the text
219 0 : nSubItem = presentation::ShapeAnimationSubType::ONLY_TEXT;
220 :
221 0 : bParagraphTarget = true;
222 : }
223 :
224 : // Lookup shape, and fill NodeContext
225 : // ==================================
226 :
227 : AttributableShapeSharedPtr pTargetShape(
228 : lookupAttributableShape( rContext.maContext.mpSubsettableShapeManager,
229 0 : xTargetShape ) );
230 :
231 : const DocTreeNodeSupplier& rTreeNodeSupplier(
232 0 : pTargetShape->getTreeNodeSupplier() );
233 :
234 0 : ShapeSubsetSharedPtr pTargetSubset;
235 :
236 0 : NodeContext aContext( rContext );
237 :
238 : // paragraph targets already need a subset as the
239 : // master shape (they're representing only a single
240 : // paragraph)
241 0 : if( bParagraphTarget )
242 : {
243 0 : 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" );
248 :
249 : pTargetSubset.reset(
250 : new ShapeSubset(
251 : pTargetShape,
252 : // retrieve index aTarget.Paragraph of
253 : // type PARAGRAPH from this shape
254 : rTreeNodeSupplier.getTreeNode(
255 : aTarget.Paragraph,
256 0 : DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ),
257 0 : rContext.maContext.mpSubsettableShapeManager ) );
258 :
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 0 : aContext.mbIsIndependentSubset = true;
264 :
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
271 : // iterations start.
272 : // Furthermore, the setup of initial shape
273 : // attributes of course needs the subset shape
274 : // generated, to apply e.g. visibility changes.
275 0 : pTargetSubset->enableSubsetShape();
276 : }
277 : else
278 : {
279 : pTargetSubset.reset(
280 : new ShapeSubset( pTargetShape,
281 0 : rContext.maContext.mpSubsettableShapeManager ));
282 : }
283 :
284 0 : aContext.mpMasterShapeSubset = pTargetSubset;
285 : uno::Reference< animations::XAnimationNode > xNode( xIterNode,
286 0 : uno::UNO_QUERY_THROW );
287 :
288 : // Generate subsets
289 : // ================
290 :
291 0 : if( bParagraphTarget ||
292 : nSubItem != presentation::ShapeAnimationSubType::ONLY_TEXT )
293 : {
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
300 : // only the text.
301 :
302 : // OR
303 :
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 0 : NodeCreator aCreator( rParent, aContext );
311 0 : if( !for_each_childNode( xNode, aCreator ) )
312 : {
313 0 : ENSURE_OR_RETURN_FALSE(
314 : false,
315 : "implCreateIteratedNodes(): iterated child node creation failed" );
316 : }
317 : }
318 :
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
326 : // the foreground)
327 0 : if( nSubItem != presentation::ShapeAnimationSubType::ONLY_BACKGROUND )
328 : {
329 : // determine type of subitem iteration (logical
330 : // text unit to animate)
331 : DocTreeNode::NodeType eIterateNodeType(
332 0 : DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL );
333 :
334 0 : switch( xIterNode->getIterateType() )
335 : {
336 : case presentation::TextAnimationType::BY_PARAGRAPH:
337 0 : eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH;
338 0 : break;
339 :
340 : case presentation::TextAnimationType::BY_WORD:
341 0 : eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_WORD;
342 0 : break;
343 :
344 : case presentation::TextAnimationType::BY_LETTER:
345 0 : eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL;
346 0 : break;
347 :
348 : default:
349 0 : ENSURE_OR_THROW(
350 : false, "implCreateIteratedNodes(): "
351 : "Unexpected IterateType on XIterateContainer");
352 : break;
353 : }
354 :
355 0 : if( bParagraphTarget &&
356 0 : eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_WORD &&
357 : eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL )
358 : {
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" );
362 : }
363 : else
364 : {
365 : // setup iteration parameters
366 :
367 :
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 0 : aContext.mbIsIndependentSubset = false;
377 :
378 : // determine number of nodes for given subitem
379 : // type
380 0 : sal_Int32 nTreeNodes( 0 );
381 0 : if( bParagraphTarget )
382 : {
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 0 : pTargetSubset->getSubset(),
389 0 : eIterateNodeType );
390 : }
391 : else
392 : {
393 : // generate normal subset
394 : nTreeNodes = rTreeNodeSupplier.getNumberOfTreeNodes(
395 0 : eIterateNodeType );
396 : }
397 :
398 :
399 : // iterate node, generate copies of the children for each subset
400 :
401 :
402 : // NodeContext::mnStartDelay contains additional node delay.
403 : // This will make the duplicated nodes for each iteration start
404 : // increasingly later.
405 0 : aContext.mnStartDelay = nIntervalTimeout;
406 :
407 0 : for( sal_Int32 i=0; i<nTreeNodes; ++i )
408 : {
409 : // create subset with the corresponding tree nodes
410 0 : if( bParagraphTarget )
411 : {
412 : // create subsets relative to paragraph subset
413 : aContext.mpMasterShapeSubset.reset(
414 : new ShapeSubset(
415 : pTargetSubset,
416 : rTreeNodeSupplier.getSubsetTreeNode(
417 0 : pTargetSubset->getSubset(),
418 : i,
419 0 : eIterateNodeType ) ) );
420 : }
421 : else
422 : {
423 : // create subsets from main shape
424 : aContext.mpMasterShapeSubset.reset(
425 : new ShapeSubset( pTargetSubset,
426 : rTreeNodeSupplier.getTreeNode(
427 : i,
428 0 : eIterateNodeType ) ) );
429 : }
430 :
431 0 : CloningNodeCreator aCreator( rParent, aContext );
432 0 : if( !for_each_childNode( xNode, aCreator ) )
433 : {
434 0 : ENSURE_OR_RETURN_FALSE(
435 : false, "implCreateIteratedNodes(): "
436 : "iterated child node creation failed" );
437 : }
438 :
439 0 : aContext.mnStartDelay += nIntervalTimeout;
440 : }
441 : }
442 : }
443 :
444 : // done with iterate child generation
445 0 : return true;
446 : }
447 :
448 0 : BaseNodeSharedPtr implCreateAnimationNode(
449 : const uno::Reference< animations::XAnimationNode >& xNode,
450 : const BaseContainerNodeSharedPtr& rParent,
451 : const NodeContext& rContext )
452 : {
453 0 : ENSURE_OR_THROW( xNode.is(),
454 : "implCreateAnimationNode(): invalid XAnimationNode" );
455 :
456 0 : BaseNodeSharedPtr pCreatedNode;
457 0 : BaseContainerNodeSharedPtr pCreatedContainer;
458 :
459 : // create the internal node, corresponding to xNode
460 0 : switch( xNode->getType() )
461 : {
462 : case animations::AnimationNodeType::CUSTOM:
463 : OSL_FAIL( "implCreateAnimationNode(): "
464 : "CUSTOM not yet implemented" );
465 0 : return pCreatedNode;
466 :
467 : case animations::AnimationNodeType::PAR:
468 0 : pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
469 0 : new ParallelTimeContainer( xNode, rParent, rContext ) );
470 0 : break;
471 :
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 0 : pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
477 0 : new ParallelTimeContainer( xNode, rParent, rContext ) );
478 0 : break;
479 :
480 : case animations::AnimationNodeType::SEQ:
481 0 : pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr(
482 0 : new SequentialTimeContainer( xNode, rParent, rContext ) );
483 0 : break;
484 :
485 : case animations::AnimationNodeType::ANIMATE:
486 : pCreatedNode.reset( new PropertyAnimationNode(
487 0 : xNode, rParent, rContext ) );
488 0 : break;
489 :
490 : case animations::AnimationNodeType::SET:
491 : pCreatedNode.reset( new AnimationSetNode(
492 0 : xNode, rParent, rContext ) );
493 0 : break;
494 :
495 : case animations::AnimationNodeType::ANIMATEMOTION:
496 : pCreatedNode.reset( new AnimationPathMotionNode(
497 0 : xNode, rParent, rContext ) );
498 0 : break;
499 :
500 : case animations::AnimationNodeType::ANIMATECOLOR:
501 : pCreatedNode.reset( new AnimationColorNode(
502 0 : xNode, rParent, rContext ) );
503 0 : break;
504 :
505 : case animations::AnimationNodeType::ANIMATETRANSFORM:
506 : pCreatedNode.reset( new AnimationTransformNode(
507 0 : xNode, rParent, rContext ) );
508 0 : break;
509 :
510 : case animations::AnimationNodeType::TRANSITIONFILTER:
511 : pCreatedNode.reset( new AnimationTransitionFilterNode(
512 0 : xNode, rParent, rContext ) );
513 0 : break;
514 :
515 : case animations::AnimationNodeType::AUDIO:
516 : pCreatedNode.reset( new AnimationAudioNode(
517 0 : xNode, rParent, rContext ) );
518 0 : break;
519 :
520 : case animations::AnimationNodeType::COMMAND:
521 : pCreatedNode.reset( new AnimationCommandNode(
522 0 : xNode, rParent, rContext ) );
523 0 : break;
524 :
525 : default:
526 : OSL_FAIL( "implCreateAnimationNode(): "
527 : "invalid AnimationNodeType" );
528 0 : return pCreatedNode;
529 : }
530 :
531 : // TODO(Q1): This yields circular references, which, it seems, is
532 : // unavoidable here
533 :
534 : // HACK: node objects need shared_ptr to themselves,
535 : // which we pass them here.
536 0 : pCreatedNode->setSelf( pCreatedNode );
537 :
538 : // if we've got a container node object, recursively add
539 : // its children
540 0 : if( pCreatedContainer )
541 : {
542 : uno::Reference< animations::XIterateContainer > xIterNode(
543 0 : xNode, uno::UNO_QUERY );
544 :
545 : // when this node is an XIterateContainer with
546 : // active iterations, this method will generate
547 : // the appropriate children
548 0 : if( xIterNode.is() )
549 : {
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
556 : // children.
557 : implCreateIteratedNodes( xIterNode,
558 : pCreatedContainer,
559 0 : rContext );
560 : }
561 : else
562 : {
563 : // no iterate subset node, just plain child generation now
564 0 : NodeCreator aCreator( pCreatedContainer, rContext );
565 0 : if( !for_each_childNode( xNode, aCreator ) )
566 : {
567 : OSL_FAIL( "implCreateAnimationNode(): "
568 : "child node creation failed" );
569 0 : return BaseNodeSharedPtr();
570 : }
571 0 : }
572 : }
573 :
574 0 : return pCreatedNode;
575 : }
576 :
577 : } // anon namespace
578 :
579 0 : AnimationNodeSharedPtr AnimationNodeFactory::createAnimationNode(
580 : const uno::Reference< animations::XAnimationNode >& xNode,
581 : const ::basegfx::B2DVector& rSlideSize,
582 : const SlideShowContext& rContext )
583 : {
584 0 : ENSURE_OR_THROW(
585 : xNode.is(),
586 : "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
587 :
588 : return BaseNodeSharedPtr( implCreateAnimationNode(
589 : xNode,
590 : BaseContainerNodeSharedPtr(), // no parent
591 : NodeContext( rContext,
592 0 : rSlideSize )));
593 : }
594 :
595 : #if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
596 : void AnimationNodeFactory::showTree( AnimationNodeSharedPtr& pRootNode )
597 : {
598 : if( pRootNode )
599 : DEBUG_NODES_SHOWTREE( boost::dynamic_pointer_cast<BaseContainerNode>(
600 : pRootNode).get() );
601 : }
602 : #endif
603 :
604 : } // namespace internal
605 : } // namespace slideshow
606 :
607 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|