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