LibreOffice Module slideshow (master) 1
animationnodefactory.cxx
Go to the documentation of this file.
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#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>
28#include <sal/log.hxx>
29
34#include "animationsetnode.hxx"
42#include "nodetools.hxx"
43#include <tools.hxx>
44
45#include <memory>
46
47using namespace ::com::sun::star;
48
49namespace slideshow::internal {
50
51namespace {
52
53// forward declaration needed by NodeCreator
54BaseNodeSharedPtr implCreateAnimationNode(
55 const uno::Reference< animations::XAnimationNode >& xNode,
56 const BaseContainerNodeSharedPtr& rParent,
57 const NodeContext& rContext );
58
59class NodeCreator
60{
61public:
62 NodeCreator( BaseContainerNodeSharedPtr& rParent,
63 const NodeContext& rContext )
64 : mrParent( rParent ), mrContext( rContext ) {}
65
66 void operator()(
67 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
68 {
69 createChild( xChildNode, mrContext );
70 }
71
72protected:
73 void createChild(
74 const uno::Reference< animations::XAnimationNode >& xChildNode,
75 const NodeContext& rContext ) const
76 {
77 BaseNodeSharedPtr pChild( implCreateAnimationNode( xChildNode,
79 rContext ) );
80
81 OSL_ENSURE( pChild,
82 "NodeCreator::operator(): child creation failed" );
83
84 // TODO(Q1): This yields circular references, which, it seems, is
85 // unavoidable here
86 if( pChild )
87 mrParent->appendChildNode( pChild );
88 }
89
91 const NodeContext& mrContext;
92};
93
99class CloningNodeCreator : private NodeCreator
100{
101public:
102 CloningNodeCreator( BaseContainerNodeSharedPtr& rParent,
103 const NodeContext& rContext )
104 : NodeCreator( rParent, rContext ) {}
105
106 void operator()(
107 const uno::Reference< animations::XAnimationNode >& xChildNode ) const
108 {
109 NodeContext aContext( mrContext );
110
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.
121
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
126 // disables it).
127
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 );
138
139 createChild( xChildNode, aContext );
140 }
141};
142
148bool implCreateIteratedNodes(
149 const uno::Reference< animations::XIterateContainer >& xIterNode,
151 const NodeContext& rContext )
152{
153 ENSURE_OR_THROW( xIterNode.is(),
154 "implCreateIteratedNodes(): Invalid node" );
155
156 const double nIntervalTimeout( xIterNode->getIterateInterval() );
157
158 // valid iterate interval? We're ruling out monstrous
159 // values here, to avoid pseudo 'hangs' in the
160 // presentation
161 if( nIntervalTimeout < 0.0 ||
162 nIntervalTimeout > 1000.0 )
163 {
164 return false; // not an active iteration
165 }
166
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)" );
172
173 // Determine target shape (or subset)
174 // ==================================
175
176 // TODO(E1): I'm not too sure what to expect here...
178 xIterNode->getTarget().hasValue(),
179 "implCreateIteratedNodes(): no target on ITERATE node" );
180
181 uno::Reference< drawing::XShape > xTargetShape( xIterNode->getTarget(),
182 uno::UNO_QUERY );
183
184 presentation::ParagraphTarget aTarget;
185 sal_Int16 nSubItem( xIterNode->getSubItem() );
186 bool bParagraphTarget( false );
187
188 if( !xTargetShape.is() )
189 {
190 // no shape provided. Maybe a ParagraphTarget?
191 if( !(xIterNode->getTarget() >>= aTarget) )
193 false,
194 "implCreateIteratedNodes(): could not extract any "
195 "target information" );
196
197 xTargetShape = aTarget.Shape;
198
200 xTargetShape.is(),
201 "implCreateIteratedNodes(): invalid shape in ParagraphTarget" );
202
203 // we've a paragraph target to iterate over, thus,
204 // the whole animation container refers only to
205 // the text
206 nSubItem = presentation::ShapeAnimationSubType::ONLY_TEXT;
207
208 bParagraphTarget = true;
209 }
210
211 // Lookup shape, and fill NodeContext
212 // ==================================
213
214 AttributableShapeSharedPtr pTargetShape(
215 lookupAttributableShape( rContext.maContext.mpSubsettableShapeManager,
216 xTargetShape ) );
217
218 const DocTreeNodeSupplier& rTreeNodeSupplier(
219 pTargetShape->getTreeNodeSupplier() );
220
221 ShapeSubsetSharedPtr pTargetSubset;
222
223 NodeContext aContext( rContext );
224
225 // paragraph targets already need a subset as the
226 // master shape (they're representing only a single
227 // paragraph)
228 if( bParagraphTarget )
229 {
231 aTarget.Paragraph >= 0 &&
232 rTreeNodeSupplier.getNumberOfTreeNodes(
233 DocTreeNode::NodeType::LogicalParagraph ) > aTarget.Paragraph,
234 "implCreateIteratedNodes(): paragraph index out of range" );
235
236 pTargetSubset =
237 std::make_shared<ShapeSubset>(
238 pTargetShape,
239 // retrieve index aTarget.Paragraph of
240 // type PARAGRAPH from this shape
241 rTreeNodeSupplier.getTreeNode(
242 aTarget.Paragraph,
244 rContext.maContext.mpSubsettableShapeManager );
245
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;
251
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
258 // iterations start.
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();
263 }
264 else
265 {
266 pTargetSubset =
267 std::make_shared<ShapeSubset>( pTargetShape,
268 rContext.maContext.mpSubsettableShapeManager );
269 }
270
271 aContext.mpMasterShapeSubset = pTargetSubset;
272 uno::Reference< animations::XAnimationNode > xNode( xIterNode,
273 uno::UNO_QUERY_THROW );
274
275 // Generate subsets
276 // ================
277
278 if( bParagraphTarget ||
279 nSubItem != presentation::ShapeAnimationSubType::ONLY_TEXT )
280 {
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
287 // only the text.
288
289 // OR
290
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 ) )
299 {
301 false,
302 "implCreateIteratedNodes(): iterated child node creation failed" );
303 }
304 }
305
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
313 // the foreground)
314 if( nSubItem != presentation::ShapeAnimationSubType::ONLY_BACKGROUND )
315 {
316 // determine type of subitem iteration (logical
317 // text unit to animate)
318 DocTreeNode::NodeType eIterateNodeType(
320
321 switch( xIterNode->getIterateType() )
322 {
323 case presentation::TextAnimationType::BY_PARAGRAPH:
325 break;
326
327 case presentation::TextAnimationType::BY_WORD:
328 eIterateNodeType = DocTreeNode::NodeType::LogicalWord;
329 break;
330
331 case presentation::TextAnimationType::BY_LETTER:
333 break;
334
335 default:
337 false, "implCreateIteratedNodes(): "
338 "Unexpected IterateType on XIterateContainer");
339 break;
340 }
341
342 if( bParagraphTarget &&
343 eIterateNodeType != DocTreeNode::NodeType::LogicalWord &&
345 {
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" );
349 }
350 else
351 {
352 // setup iteration parameters
353
354
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;
364
365 // determine number of nodes for given subitem
366 // type
367 sal_Int32 nTreeNodes( 0 );
368 if( bParagraphTarget )
369 {
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(),
376 eIterateNodeType );
377 }
378 else
379 {
380 // generate normal subset
381 nTreeNodes = rTreeNodeSupplier.getNumberOfTreeNodes(
382 eIterateNodeType );
383 }
384
385
386 // iterate node, generate copies of the children for each subset
387
388
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;
393
394 for( sal_Int32 i=0; i<nTreeNodes; ++i )
395 {
396 // create subset with the corresponding tree nodes
397 if( bParagraphTarget )
398 {
399 // create subsets relative to paragraph subset
400 aContext.mpMasterShapeSubset =
401 std::make_shared<ShapeSubset>(
402 pTargetSubset,
403 rTreeNodeSupplier.getSubsetTreeNode(
404 pTargetSubset->getSubset(),
405 i,
406 eIterateNodeType ) );
407 }
408 else
409 {
410 // create subsets from main shape
411 aContext.mpMasterShapeSubset =
412 std::make_shared<ShapeSubset>( pTargetSubset,
413 rTreeNodeSupplier.getTreeNode(
414 i,
415 eIterateNodeType ) );
416 }
417
418 CloningNodeCreator aCreator( rParent, aContext );
419 if( !for_each_childNode( xNode, aCreator ) )
420 {
422 false, "implCreateIteratedNodes(): "
423 "iterated child node creation failed" );
424 }
425
426 aContext.mnStartDelay += nIntervalTimeout;
427 }
428 }
429 }
430
431 // done with iterate child generation
432 return true;
433}
434
435BaseNodeSharedPtr implCreateAnimationNode(
436 const uno::Reference< animations::XAnimationNode >& xNode,
437 const BaseContainerNodeSharedPtr& rParent,
438 const NodeContext& rContext )
439{
440 ENSURE_OR_THROW( xNode.is(),
441 "implCreateAnimationNode(): invalid XAnimationNode" );
442
443 BaseNodeSharedPtr pCreatedNode;
444 BaseContainerNodeSharedPtr pCreatedContainer;
445
446 // create the internal node, corresponding to xNode
447 switch( xNode->getType() )
448 {
449 case animations::AnimationNodeType::CUSTOM:
450 OSL_FAIL( "implCreateAnimationNode(): "
451 "CUSTOM not yet implemented" );
452 return pCreatedNode;
453
454 case animations::AnimationNodeType::PAR:
455 pCreatedNode = pCreatedContainer =
456 std::make_shared<ParallelTimeContainer>( xNode, rParent, rContext );
457 break;
458
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 );
465 break;
466
467 case animations::AnimationNodeType::SEQ:
468 pCreatedNode = pCreatedContainer =
469 std::make_shared<SequentialTimeContainer>( xNode, rParent, rContext );
470 break;
471
472 case animations::AnimationNodeType::ANIMATE:
473 pCreatedNode = std::make_shared<PropertyAnimationNode>(
474 xNode, rParent, rContext );
475 break;
476
477 case animations::AnimationNodeType::SET:
478 pCreatedNode = std::make_shared<AnimationSetNode>(
479 xNode, rParent, rContext );
480 break;
481
482 case animations::AnimationNodeType::ANIMATEMOTION:
483 pCreatedNode = std::make_shared<AnimationPathMotionNode>(
484 xNode, rParent, rContext );
485 break;
486
487 case animations::AnimationNodeType::ANIMATECOLOR:
488 pCreatedNode = std::make_shared<AnimationColorNode>(
489 xNode, rParent, rContext );
490 break;
491
492 case animations::AnimationNodeType::ANIMATETRANSFORM:
493 pCreatedNode = std::make_shared<AnimationTransformNode>(
494 xNode, rParent, rContext );
495 break;
496
497 case animations::AnimationNodeType::ANIMATEPHYSICS:
498 pCreatedNode = std::make_shared<AnimationPhysicsNode>(
499 xNode, rParent, rContext );
500 break;
501
502 case animations::AnimationNodeType::TRANSITIONFILTER:
503 pCreatedNode = std::make_shared<AnimationTransitionFilterNode>(
504 xNode, rParent, rContext );
505 break;
506
507 case animations::AnimationNodeType::AUDIO:
508 pCreatedNode = std::make_shared<AnimationAudioNode>(
509 xNode, rParent, rContext );
510 break;
511
512 case animations::AnimationNodeType::COMMAND:
513 pCreatedNode = std::make_shared<AnimationCommandNode>(
514 xNode, rParent, rContext );
515 break;
516
517 default:
518 OSL_FAIL( "implCreateAnimationNode(): "
519 "invalid AnimationNodeType" );
520 return pCreatedNode;
521 }
522
523 // TODO(Q1): This yields circular references, which, it seems, is
524 // unavoidable here
525
526 // HACK: node objects need shared_ptr to themselves,
527 // which we pass them here.
528 pCreatedNode->setSelf( pCreatedNode );
529
530 // if we've got a container node object, recursively add
531 // its children
532 if( pCreatedContainer )
533 {
534 uno::Reference< animations::XIterateContainer > xIterNode(
535 xNode, uno::UNO_QUERY );
536
537 // when this node is an XIterateContainer with
538 // active iterations, this method will generate
539 // the appropriate children
540 if( xIterNode.is() )
541 {
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
548 // children.
549 implCreateIteratedNodes( xIterNode,
550 pCreatedContainer,
551 rContext );
552 }
553 else
554 {
555 // no iterate subset node, just plain child generation now
556 NodeCreator aCreator( pCreatedContainer, rContext );
557 if( !for_each_childNode( xNode, aCreator ) )
558 {
559 OSL_FAIL( "implCreateAnimationNode(): "
560 "child node creation failed" );
561 return BaseNodeSharedPtr();
562 }
563 }
564 }
565
566 return pCreatedNode;
567}
568
569} // anon namespace
570
572 const uno::Reference< animations::XAnimationNode >& xNode,
573 const ::basegfx::B2DVector& rSlideSize,
574 const SlideShowContext& rContext )
575{
577 xNode.is(),
578 "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" );
579
580 return implCreateAnimationNode(
581 xNode,
582 BaseContainerNodeSharedPtr(), // no parent
583 NodeContext( rContext,
584 rSlideSize ));
585}
586
587#if defined(DBG_UTIL)
589{
590 if( pRootNode )
591 DEBUG_NODES_SHOWTREE( std::dynamic_pointer_cast<BaseContainerNode>(
592 pRootNode).get() );
593}
594#endif
595
596} // namespace slideshow
597
598/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BaseContainerNodeSharedPtr & mrParent
const NodeContext & mrContext
NodeType
Type of shape entity represented by this node.
Definition: doctreenode.hxx:49
@ LogicalCharacterCell
This node represents a character.
@ LogicalWord
This node represents a word.
@ LogicalParagraph
This node represents a paragraph.
#define ENSURE_OR_RETURN_FALSE(c, m)
#define ENSURE_OR_THROW(c, m)
#define SAL_INFO(area, stream)
int i
void showTree(AnimationNodeSharedPtr const &pRootNode)
AnimationNodeSharedPtr createAnimationNode(const css::uno::Reference< css::animations::XAnimationNode > &xNode, const ::basegfx::B2DVector &rSlideSize, const SlideShowContext &rContext)
Create an AnimationNode for the given XAnimationNode.
AttributableShapeSharedPtr lookupAttributableShape(const ShapeManagerSharedPtr &rShapeManager, const uno::Reference< drawing::XShape > &xShape)
Definition: nodetools.cxx:49
::std::shared_ptr< ShapeSubset > ShapeSubsetSharedPtr
Definition: shapesubset.hxx:30
::std::shared_ptr< BaseContainerNode > BaseContainerNodeSharedPtr
::std::shared_ptr< AnimationNode > AnimationNodeSharedPtr
::std::shared_ptr< AttributableShape > AttributableShapeSharedPtr
::std::shared_ptr< BaseNode > BaseNodeSharedPtr
Definition: basenode.hxx:74
bool for_each_childNode(const css::uno::Reference< css::animations::XAnimationNode > &xNode, Functor &rFunctor)
Apply given functor to every animation node child.
Definition: tools.hxx:365
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
#define DEBUG_NODES_SHOWTREE(a)
Definition: nodetools.hxx:31