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