1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_slideshow.hxx"
30 
31 // must be first
32 #include <canvas/debug.hxx>
33 #include <canvas/verbosetrace.hxx>
34 
35 #include <com/sun/star/animations/XAnimate.hpp>
36 #include <com/sun/star/presentation/ParagraphTarget.hpp>
37 #include <com/sun/star/animations/AnimationFill.hpp>
38 #include <com/sun/star/animations/AnimationRestart.hpp>
39 #include <com/sun/star/presentation/EffectNodeType.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 
42 #include "basenode.hxx"
43 #include "eventmultiplexer.hxx"
44 #include "basecontainernode.hxx"
45 #include "eventqueue.hxx"
46 #include "delayevent.hxx"
47 #include "tools.hxx"
48 #include "nodetools.hxx"
49 #include "generateevent.hxx"
50 #include "debug.hxx"
51 
52 #include <boost/bind.hpp>
53 #include <vector>
54 #include <algorithm>
55 #include <iterator>
56 
57 using namespace ::com::sun::star;
58 
59 namespace slideshow {
60 namespace internal {
61 
62 namespace {
63 
64 typedef int StateTransitionTable[17];
65 
66 // State transition tables
67 // =========================================================================
68 
69 const int* getStateTransitionTable( sal_Int16 nRestartMode,
70                                     sal_Int16 nFillMode )
71 {
72     // TODO(F2): restart issues in below tables
73 
74     // transition table for restart=NEVER, fill=REMOVE
75     static const StateTransitionTable stateTransitionTable_Never_Remove = {
76         AnimationNode::INVALID,
77         AnimationNode::RESOLVED|AnimationNode::ENDED,   // active successors for UNRESOLVED
78         AnimationNode::ACTIVE|AnimationNode::ENDED,     // active successors for RESOLVED
79         AnimationNode::INVALID,
80         AnimationNode::ENDED,                           // active successors for ACTIVE: no freeze here
81         AnimationNode::INVALID,
82         AnimationNode::INVALID,
83         AnimationNode::INVALID,
84         AnimationNode::INVALID,                         // active successors for FROZEN: this state is unreachable here
85         AnimationNode::INVALID,
86         AnimationNode::INVALID,
87         AnimationNode::INVALID,
88         AnimationNode::INVALID,
89         AnimationNode::INVALID,
90         AnimationNode::INVALID,
91         AnimationNode::INVALID,
92         AnimationNode::ENDED                            // active successors for ENDED: this state is a sink here (cannot restart)
93     };
94 
95     // transition table for restart=WHEN_NOT_ACTIVE, fill=REMOVE
96     static const StateTransitionTable stateTransitionTable_NotActive_Remove = {
97         AnimationNode::INVALID,
98         AnimationNode::RESOLVED|AnimationNode::ENDED,                       // active successors for UNRESOLVED
99         AnimationNode::ACTIVE|AnimationNode::ENDED,                         // active successors for RESOLVED
100         AnimationNode::INVALID,
101         AnimationNode::ENDED,                                               // active successors for ACTIVE: no freeze here
102         AnimationNode::INVALID,
103         AnimationNode::INVALID,
104         AnimationNode::INVALID,
105         AnimationNode::INVALID,                                             // active successors for FROZEN:
106                                                                             // this state is unreachable here
107         AnimationNode::INVALID,
108         AnimationNode::INVALID,
109         AnimationNode::INVALID,
110         AnimationNode::INVALID,
111         AnimationNode::INVALID,
112         AnimationNode::INVALID,
113         AnimationNode::INVALID,
114         AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE  // active successors for ENDED:
115                                                                             // restart possible when ended
116     };
117 
118     // transition table for restart=ALWAYS, fill=REMOVE
119     static const StateTransitionTable stateTransitionTable_Always_Remove = {
120         AnimationNode::INVALID,
121         AnimationNode::RESOLVED|AnimationNode::ENDED,                       // active successors for UNRESOLVED
122         AnimationNode::ACTIVE|AnimationNode::ENDED,                         // active successors for RESOLVED
123         AnimationNode::INVALID,
124         AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE: restart
125         AnimationNode::INVALID,
126         AnimationNode::INVALID,
127         AnimationNode::INVALID,
128         AnimationNode::INVALID,                                             // active successors for FROZEN:
129                                                                             // this state is unreachable here
130         AnimationNode::INVALID,
131         AnimationNode::INVALID,
132         AnimationNode::INVALID,
133         AnimationNode::INVALID,
134         AnimationNode::INVALID,
135         AnimationNode::INVALID,
136         AnimationNode::INVALID,
137         AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED  // active successors for ENDED: restart
138     };
139 
140     // transition table for restart=NEVER, fill=FREEZE
141     static const StateTransitionTable stateTransitionTable_Never_Freeze = {
142         AnimationNode::INVALID,
143         AnimationNode::RESOLVED|AnimationNode::ENDED,   // active successors for UNRESOLVED
144         AnimationNode::ACTIVE|AnimationNode::ENDED,     // active successors for RESOLVED
145         AnimationNode::INVALID,
146         AnimationNode::FROZEN|AnimationNode::ENDED,     // active successors for ACTIVE: freeze object
147         AnimationNode::INVALID,
148         AnimationNode::INVALID,
149         AnimationNode::INVALID,
150         AnimationNode::ENDED,                           // active successors for FROZEN: end
151         AnimationNode::INVALID,
152         AnimationNode::INVALID,
153         AnimationNode::INVALID,
154         AnimationNode::INVALID,
155         AnimationNode::INVALID,
156         AnimationNode::INVALID,
157         AnimationNode::INVALID,
158         AnimationNode::ENDED,                           // active successors for ENDED: this state is a sink here (cannot restart)
159     };
160 
161     // transition table for restart=WHEN_NOT_ACTIVE, fill=FREEZE
162     static const StateTransitionTable stateTransitionTable_NotActive_Freeze = {
163         AnimationNode::INVALID,
164         AnimationNode::RESOLVED|AnimationNode::ENDED,                       // active successors for UNRESOLVED
165         AnimationNode::ACTIVE|AnimationNode::ENDED,                         // active successors for RESOLVED
166         AnimationNode::INVALID,
167         AnimationNode::FROZEN|AnimationNode::ENDED,                         // active successors for ACTIVE: freeze object
168         AnimationNode::INVALID,
169         AnimationNode::INVALID,
170         AnimationNode::INVALID,
171         AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN:
172                                                                             // restart possible when ended
173         AnimationNode::INVALID,
174         AnimationNode::INVALID,
175         AnimationNode::INVALID,
176         AnimationNode::INVALID,
177         AnimationNode::INVALID,
178         AnimationNode::INVALID,
179         AnimationNode::INVALID,
180         AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE  // active successors for ENDED:
181                                                                             // restart possible when ended
182     };
183 
184     // transition table for restart=ALWAYS, fill=FREEZE
185     static const StateTransitionTable stateTransitionTable_Always_Freeze = {
186         AnimationNode::INVALID,
187         AnimationNode::RESOLVED|AnimationNode::ENDED,                       // active successors for UNRESOLVED
188         AnimationNode::ACTIVE|AnimationNode::ENDED,                         // active successors for RESOLVED
189         AnimationNode::INVALID,
190         AnimationNode::FROZEN|AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE:
191                                                                                                   // end object, restart
192         AnimationNode::INVALID,
193         AnimationNode::INVALID,
194         AnimationNode::INVALID,
195         AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN: restart possible
196         AnimationNode::INVALID,
197         AnimationNode::INVALID,
198         AnimationNode::INVALID,
199         AnimationNode::INVALID,
200         AnimationNode::INVALID,
201         AnimationNode::INVALID,
202         AnimationNode::INVALID,
203         AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED  // active successors for ENDED: restart
204     };
205 
206     static const StateTransitionTable* tableGuide[] = {
207         &stateTransitionTable_Never_Remove,
208         &stateTransitionTable_NotActive_Remove,
209         &stateTransitionTable_Always_Remove,
210         &stateTransitionTable_Never_Freeze,
211         &stateTransitionTable_NotActive_Freeze,
212         &stateTransitionTable_Always_Freeze
213     };
214 
215     int nRestartValue;
216     switch( nRestartMode ) {
217     default:
218     case animations::AnimationRestart::DEFAULT:
219         // same value: animations::AnimationRestart::INHERIT:
220         OSL_ENSURE(
221             false, "getStateTransitionTable(): unexpected case for restart" );
222         // FALLTHROUGH intended
223     case animations::AnimationRestart::NEVER:
224         nRestartValue = 0;
225         break;
226     case animations::AnimationRestart::WHEN_NOT_ACTIVE:
227         nRestartValue = 1;
228         break;
229     case animations::AnimationRestart::ALWAYS:
230         nRestartValue = 2;
231         break;
232     }
233 
234     int nFillValue;
235     switch( nFillMode ) {
236     default:
237     case animations::AnimationFill::AUTO:
238     case animations::AnimationFill::DEFAULT:
239         // same value: animations::AnimationFill::INHERIT:
240         OSL_ENSURE(
241             false, "getStateTransitionTable(): unexpected case for fill" );
242         // FALLTHROUGH intended
243     case animations::AnimationFill::REMOVE:
244         nFillValue = 0;
245         break;
246     case animations::AnimationFill::FREEZE:
247     case animations::AnimationFill::HOLD:
248     case animations::AnimationFill::TRANSITION:
249         nFillValue = 1;
250         break;
251     }
252 
253     return *tableGuide[ 3*nFillValue + nRestartValue ];
254 }
255 
256 /// Little helper predicate, to detect main sequence root node
257 bool isMainSequenceRootNode_(
258     const uno::Reference< animations::XAnimationNode >& xNode )
259 {
260     // detect main sequence root node (need that for
261     // end-of-mainsequence signalling below)
262     beans::NamedValue const aSearchKey(
263         rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ),
264         uno::makeAny( presentation::EffectNodeType::MAIN_SEQUENCE ) );
265 
266     uno::Sequence<beans::NamedValue> const userData(xNode->getUserData());
267     return findNamedValue( userData, aSearchKey );
268 }
269 
270 } // anon namespace
271 
272 // BaseNode implementation
273 //=========================================================================
274 
275 /** state transition handling
276  */
277 class BaseNode::StateTransition : private boost::noncopyable
278 {
279 public:
280     enum Options { NONE, FORCE };
281 
282     explicit StateTransition( BaseNode * pNode )
283         : mpNode(pNode), meToState(INVALID) {}
284 
285     ~StateTransition() {
286         clear();
287     }
288 
289     bool enter( NodeState eToState, int options = NONE )
290     {
291         OSL_ENSURE( meToState == INVALID,
292                     "### commit() before enter()ing again!" );
293         if (meToState != INVALID)
294             return false;
295         bool const bForce = ((options & FORCE) != 0);
296         if (!bForce && !mpNode->isTransition( mpNode->meCurrState, eToState ))
297             return false;
298         // recursion detection:
299         if ((mpNode->meCurrentStateTransition & eToState) != 0)
300             return false; // already in wanted transition
301         // mark transition:
302         mpNode->meCurrentStateTransition |= eToState;
303         meToState = eToState;
304         return true; // in transition
305     }
306 
307     void commit() {
308         OSL_ENSURE( meToState != INVALID, "### nothing to commit!" );
309         if (meToState != INVALID) {
310             mpNode->meCurrState = meToState;
311             clear();
312         }
313 
314         // Uncomment the following line to write the node tree to file on
315         // every state change of one of its nodes.
316         //     Debug_ShowNodeTree(mpNode->mpSelf);
317     }
318 
319     void clear() {
320         if (meToState != INVALID) {
321             OSL_ASSERT( (mpNode->meCurrentStateTransition & meToState) != 0 );
322             mpNode->meCurrentStateTransition &= ~meToState;
323             meToState = INVALID;
324         }
325     }
326 
327 private:
328     BaseNode *const mpNode;
329     NodeState meToState;
330 };
331 
332 BaseNode::BaseNode( const uno::Reference< animations::XAnimationNode >& xNode,
333                     const BaseContainerNodeSharedPtr&                   rParent,
334                     const NodeContext&                                  rContext ) :
335     maContext( rContext.maContext ),
336     maDeactivatingListeners(),
337     mxAnimationNode( xNode ),
338     mpParent( rParent ),
339     mpSelf(),
340     mpStateTransitionTable( NULL ),
341     mnStartDelay( rContext.mnStartDelay ),
342     meCurrState( UNRESOLVED ),
343     meCurrentStateTransition( 0 ),
344     mpCurrentEvent(),
345     mbIsMainSequenceRootNode( isMainSequenceRootNode_( xNode ) )
346 {
347     ENSURE_OR_THROW( mxAnimationNode.is(),
348                       "BaseNode::BaseNode(): Invalid XAnimationNode" );
349 
350     // setup state transition table
351     mpStateTransitionTable = getStateTransitionTable( getRestartMode(),
352                                                       getFillMode() );
353 }
354 
355 void BaseNode::dispose()
356 {
357     meCurrState = INVALID;
358 
359     // discharge a loaded event, if any:
360     if (mpCurrentEvent) {
361         mpCurrentEvent->dispose();
362         mpCurrentEvent.reset();
363     }
364     maDeactivatingListeners.clear();
365     mxAnimationNode.clear();
366     mpParent.reset();
367     mpSelf.reset();
368     maContext.dispose();
369 }
370 
371 
372 sal_Int16 BaseNode::getRestartMode()
373 {
374     const sal_Int16 nTmp( mxAnimationNode->getRestart() );
375     return (nTmp != animations::AnimationRestart::DEFAULT &&
376             nTmp != animations::AnimationRestart::INHERIT)
377         ? nTmp : getRestartDefaultMode();
378 }
379 
380 sal_Int16 BaseNode::getFillMode()
381 {
382     const sal_Int16 nTmp( mxAnimationNode->getFill() );
383     const sal_Int16 nFill((nTmp != animations::AnimationFill::DEFAULT &&
384                            nTmp != animations::AnimationFill::INHERIT)
385                           ? nTmp : getFillDefaultMode());
386 
387     // For AUTO fill mode, SMIL specifies that fill mode is FREEZE,
388     // if no explicit active duration is given
389     // (no duration, end, repeatCount or repeatDuration given),
390     // and REMOVE otherwise
391     if( nFill == animations::AnimationFill::AUTO ) {
392         return (isIndefiniteTiming( mxAnimationNode->getDuration() ) &&
393                 isIndefiniteTiming( mxAnimationNode->getEnd() ) &&
394                 !mxAnimationNode->getRepeatCount().hasValue() &&
395                 isIndefiniteTiming( mxAnimationNode->getRepeatDuration() ))
396             ? animations::AnimationFill::FREEZE
397             : animations::AnimationFill::REMOVE;
398     }
399     else {
400         return nFill;
401     }
402 }
403 
404 sal_Int16 BaseNode::getFillDefaultMode() const
405 {
406     sal_Int16 nFillDefault = mxAnimationNode->getFillDefault();
407     if (nFillDefault  == animations::AnimationFill::DEFAULT) {
408         nFillDefault = (mpParent != 0
409                         ? mpParent->getFillDefaultMode()
410                         : animations::AnimationFill::AUTO);
411     }
412     return nFillDefault;
413 }
414 
415 sal_Int16 BaseNode::getRestartDefaultMode() const
416 {
417     sal_Int16 nRestartDefaultMode = mxAnimationNode->getRestartDefault();
418     if (nRestartDefaultMode == animations::AnimationRestart::DEFAULT) {
419         nRestartDefaultMode = (mpParent != 0
420                                ? mpParent->getRestartDefaultMode()
421                                : animations::AnimationRestart::ALWAYS);
422     }
423     return nRestartDefaultMode;
424 }
425 
426 uno::Reference<animations::XAnimationNode> BaseNode::getXAnimationNode() const
427 {
428     return mxAnimationNode;
429 }
430 
431 bool BaseNode::init()
432 {
433     if (! checkValidNode())
434         return false;
435     meCurrState = UNRESOLVED;
436     // discharge a loaded event, if any:
437     if (mpCurrentEvent) {
438         mpCurrentEvent->dispose();
439         mpCurrentEvent.reset();
440     }
441     return init_st(); // may call derived class
442 }
443 
444 bool BaseNode::init_st()
445 {
446     return true;
447 }
448 
449 bool BaseNode::resolve()
450 {
451     if (! checkValidNode())
452         return false;
453 
454     OSL_ASSERT( meCurrState != RESOLVED );
455     if (inStateOrTransition( RESOLVED ))
456         return true;
457 
458     StateTransition st(this);
459     if (st.enter( RESOLVED ) &&
460         isTransition( RESOLVED, ACTIVE ) &&
461         resolve_st() /* may call derived class */)
462     {
463         st.commit(); // changing state
464 
465         // discharge a loaded event, if any:
466         if (mpCurrentEvent)
467             mpCurrentEvent->dispose();
468 
469         // schedule activation event:
470 
471         // This method takes the NodeContext::mnStartDelay value into account,
472         // to cater for iterate container time shifts. We cannot put different
473         // iterations of the iterate container's children into different
474         // subcontainer (such as a 'DelayContainer', which delays resolving its
475         // children by a fixed amount), since all iterations' nodes must be
476         // resolved at the same time (otherwise, the delayed subset creation
477         // will not work, i.e. deactivate the subsets too late in the master
478         // shape).
479         uno::Any const aBegin( mxAnimationNode->getBegin() );
480         if (aBegin.hasValue()) {
481             mpCurrentEvent = generateEvent(
482                 aBegin, boost::bind( &AnimationNode::activate, mpSelf ),
483                 maContext, mnStartDelay );
484         }
485         else {
486             // For some leaf nodes, PPT import yields empty begin time,
487             // although semantically, it should be 0.0
488             // TODO(F3): That should really be provided by the PPT import
489 
490             // schedule delayed activation event. Take iterate node
491             // timeout into account
492             mpCurrentEvent = makeDelay(
493                 boost::bind( &AnimationNode::activate, mpSelf ),
494                 mnStartDelay,
495                 "AnimationNode::activate with delay");
496             maContext.mrEventQueue.addEvent( mpCurrentEvent );
497         }
498 
499         return true;
500     }
501     return false;
502 }
503 
504 bool BaseNode::resolve_st()
505 {
506     return true;
507 }
508 
509 
510 bool BaseNode::activate()
511 {
512     if (! checkValidNode())
513         return false;
514 
515     OSL_ASSERT( meCurrState != ACTIVE );
516     if (inStateOrTransition( ACTIVE ))
517         return true;
518 
519     StateTransition st(this);
520     if (st.enter( ACTIVE )) {
521 
522         activate_st(); // calling derived class
523 
524         st.commit(); // changing state
525 
526         maContext.mrEventMultiplexer.notifyAnimationStart( mpSelf );
527 
528         return true;
529     }
530 
531     return false;
532 }
533 
534 void BaseNode::activate_st()
535 {
536     scheduleDeactivationEvent();
537 }
538 
539 void BaseNode::scheduleDeactivationEvent( EventSharedPtr const& pEvent )
540 {
541     if (mpCurrentEvent) {
542         mpCurrentEvent->dispose();
543         mpCurrentEvent.reset();
544     }
545     if (pEvent) {
546         if (maContext.mrEventQueue.addEvent( pEvent ))
547             mpCurrentEvent = pEvent;
548     }
549     else {
550         // This method need not take the
551         // NodeContext::mnStartDelay value into account,
552         // because the deactivation event is only scheduled
553         // when the effect is started: the timeout is then
554         // already respected.
555 
556         // xxx todo:
557         // think about set node, anim base node!
558         // if anim base node has no activity, this is called to schedule deactivatiion,
559         // but what if it does not schedule anything?
560 
561         // TODO(F2): Handle end time attribute, too
562         mpCurrentEvent = generateEvent(
563             mxAnimationNode->getDuration(),
564             boost::bind( &AnimationNode::deactivate, mpSelf ),
565             maContext, 0.0 );
566     }
567 }
568 
569 void BaseNode::deactivate()
570 {
571     if (inStateOrTransition( ENDED | FROZEN ) || !checkValidNode())
572         return;
573 
574     if (isTransition( meCurrState, FROZEN, false /* no OSL_ASSERT */ )) {
575         // do transition to FROZEN:
576         StateTransition st(this);
577         if (st.enter( FROZEN, StateTransition::FORCE )) {
578 
579             deactivate_st( FROZEN );
580             st.commit();
581 
582             notifyEndListeners();
583 
584             // discharge a loaded event, before going on:
585             if (mpCurrentEvent) {
586                 mpCurrentEvent->dispose();
587                 mpCurrentEvent.reset();
588             }
589         }
590     }
591     else {
592         // use end instead:
593         end();
594     }
595     // state has changed either to FROZEN or ENDED
596 }
597 
598 void BaseNode::deactivate_st( NodeState )
599 {
600 }
601 
602 void BaseNode::end()
603 {
604     bool const bIsFrozenOrInTransitionToFrozen = inStateOrTransition( FROZEN );
605     if (inStateOrTransition( ENDED ) || !checkValidNode())
606         return;
607 
608     // END must always be reachable. If not, that's an error in the
609     // transition tables
610     OSL_ENSURE( isTransition( meCurrState, ENDED ),
611                 "end state not reachable in transition table" );
612 
613     StateTransition st(this);
614     if (st.enter( ENDED, StateTransition::FORCE )) {
615 
616         deactivate_st( ENDED );
617         st.commit(); // changing state
618 
619         // if is FROZEN or is to be FROZEN, then
620         // will/already notified deactivating listeners
621         if (!bIsFrozenOrInTransitionToFrozen)
622             notifyEndListeners();
623 
624         // discharge a loaded event, before going on:
625         if (mpCurrentEvent) {
626             mpCurrentEvent->dispose();
627             mpCurrentEvent.reset();
628         }
629     }
630 }
631 
632 void BaseNode::notifyDeactivating( const AnimationNodeSharedPtr& rNotifier )
633 {
634     (void) rNotifier; // avoid warning
635     OSL_ASSERT( rNotifier->getState() == FROZEN ||
636                 rNotifier->getState() == ENDED );
637     // TODO(F1): for end sync functionality, this might indeed be used some day
638 }
639 
640 void BaseNode::notifyEndListeners() const
641 {
642     // notify all listeners
643     std::for_each( maDeactivatingListeners.begin(),
644                    maDeactivatingListeners.end(),
645                    boost::bind( &AnimationNode::notifyDeactivating, _1,
646                                 boost::cref(mpSelf) ) );
647 
648     // notify state change
649     maContext.mrEventMultiplexer.notifyAnimationEnd( mpSelf );
650 
651     // notify main sequence end (iff we're the main
652     // sequence root node). This is because the main
653     // sequence determines the active duration of the
654     // slide. All other sequences are secondary, in that
655     // they don't prevent a slide change from happening,
656     // even if they have not been completed. In other
657     // words, all sequences except the main sequence are
658     // optional for the slide lifetime.
659     if (isMainSequenceRootNode())
660         maContext.mrEventMultiplexer.notifySlideAnimationsEnd();
661 }
662 
663 AnimationNode::NodeState BaseNode::getState() const
664 {
665     return meCurrState;
666 }
667 
668 bool BaseNode::registerDeactivatingListener(
669     const AnimationNodeSharedPtr& rNotifee )
670 {
671     if (! checkValidNode())
672         return false;
673 
674     ENSURE_OR_RETURN_FALSE(
675         rNotifee,
676         "BaseNode::registerDeactivatingListener(): invalid notifee" );
677     maDeactivatingListeners.push_back( rNotifee );
678 
679     return true;
680 }
681 
682 void BaseNode::setSelf( const BaseNodeSharedPtr& rSelf )
683 {
684     ENSURE_OR_THROW( rSelf.get() == this,
685                       "BaseNode::setSelf(): got ptr to different object" );
686     ENSURE_OR_THROW( !mpSelf,
687                       "BaseNode::setSelf(): called multiple times" );
688 
689     mpSelf = rSelf;
690 }
691 
692 // Debug
693 //=========================================================================
694 
695 #if defined(VERBOSE) && defined(DBG_UTIL)
696 void BaseNode::showState() const
697 {
698     const AnimationNode::NodeState eNodeState( getState() );
699 
700     if( eNodeState == AnimationNode::INVALID )
701         VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled,"
702                        "fillcolor=\"0.5,0.2,0.5\"]",
703                        (const char*)this+debugGetCurrentOffset(),
704                        getDescription() );
705     else
706         VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled,"
707                        "fillcolor=\"%f,1.0,1.0\"]",
708                        (const char*)this+debugGetCurrentOffset(),
709                        getDescription(),
710                        log(double(getState()))/4.0 );
711 
712     // determine additional node information
713     uno::Reference<animations::XAnimate> const xAnimate( mxAnimationNode,
714                                                          uno::UNO_QUERY );
715     if( xAnimate.is() )
716     {
717         uno::Reference< drawing::XShape > xTargetShape( xAnimate->getTarget(),
718                                                         uno::UNO_QUERY );
719 
720         if( !xTargetShape.is() )
721         {
722             ::com::sun::star::presentation::ParagraphTarget aTarget;
723 
724             // no shape provided. Maybe a ParagraphTarget?
725             if( (xAnimate->getTarget() >>= aTarget) )
726                 xTargetShape = aTarget.Shape;
727         }
728 
729         if( xTargetShape.is() )
730         {
731             uno::Reference< beans::XPropertySet > xPropSet( xTargetShape,
732                                                             uno::UNO_QUERY );
733 
734             // read shape name
735             ::rtl::OUString aName;
736             if( (xPropSet->getPropertyValue(
737                      ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Name") ) )
738                  >>= aName) )
739             {
740                 const ::rtl::OString& rAsciiName(
741                     ::rtl::OUStringToOString( aName,
742                                               RTL_TEXTENCODING_ASCII_US ) );
743 
744                 VERBOSE_TRACE( "Node info: n0x%X, name \"%s\"",
745                                (const char*)this+debugGetCurrentOffset(),
746                                rAsciiName.getStr() );
747             }
748         }
749     }
750 }
751 
752 const char* BaseNode::getDescription() const
753 {
754     return "BaseNode";
755 }
756 
757 void BaseNode::showTreeFromWithin() const
758 {
759     // find root node
760     BaseNodeSharedPtr pCurrNode( mpSelf );
761     while( pCurrNode->mpParent ) pCurrNode = pCurrNode->mpParent;
762 
763     pCurrNode->showState();
764 }
765 #endif
766 
767 } // namespace internal
768 } // namespace slideshow
769 
770