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 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 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 278 explicit StateTransition( BaseNode * pNode ) 279 : mpNode(pNode), meToState(INVALID) {} 280 281 ~StateTransition() { 282 clear(); 283 } 284 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 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 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 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 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 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 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 400 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 411 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 422 uno::Reference<animations::XAnimationNode> BaseNode::getXAnimationNode() const 423 { 424 return mxAnimationNode; 425 } 426 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 440 bool BaseNode::init_st() 441 { 442 return true; 443 } 444 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 500 bool BaseNode::resolve_st() 501 { 502 return true; 503 } 504 505 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 530 void BaseNode::activate_st() 531 { 532 scheduleDeactivationEvent(); 533 } 534 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 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 594 void BaseNode::deactivate_st( NodeState ) 595 { 596 } 597 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 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 636 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 659 AnimationNode::NodeState BaseNode::getState() const 660 { 661 return meCurrState; 662 } 663 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 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) 692 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 748 const char* BaseNode::getDescription() const 749 { 750 return "BaseNode"; 751 } 752 753 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