| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_slideshow.hxx" |
| |
| // must be first |
| #include <canvas/debug.hxx> |
| #include <canvas/verbosetrace.hxx> |
| |
| #include <com/sun/star/animations/XAnimate.hpp> |
| #include <com/sun/star/presentation/ParagraphTarget.hpp> |
| #include <com/sun/star/animations/AnimationFill.hpp> |
| #include <com/sun/star/animations/AnimationRestart.hpp> |
| #include <com/sun/star/presentation/EffectNodeType.hpp> |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| |
| #include "basenode.hxx" |
| #include "eventmultiplexer.hxx" |
| #include "basecontainernode.hxx" |
| #include "eventqueue.hxx" |
| #include "delayevent.hxx" |
| #include "tools.hxx" |
| #include "nodetools.hxx" |
| #include "generateevent.hxx" |
| #include "debug.hxx" |
| |
| #include <boost/bind.hpp> |
| #include <vector> |
| #include <algorithm> |
| #include <iterator> |
| |
| using namespace ::com::sun::star; |
| |
| namespace slideshow { |
| namespace internal { |
| |
| namespace { |
| |
| typedef int StateTransitionTable[17]; |
| |
| // State transition tables |
| // ========================================================================= |
| |
| const int* getStateTransitionTable( sal_Int16 nRestartMode, |
| sal_Int16 nFillMode ) |
| { |
| // TODO(F2): restart issues in below tables |
| |
| // transition table for restart=NEVER, fill=REMOVE |
| static const StateTransitionTable stateTransitionTable_Never_Remove = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::ENDED, // active successors for ACTIVE: no freeze here |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, // active successors for FROZEN: this state is unreachable here |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED // active successors for ENDED: this state is a sink here (cannot restart) |
| }; |
| |
| // transition table for restart=WHEN_NOT_ACTIVE, fill=REMOVE |
| static const StateTransitionTable stateTransitionTable_NotActive_Remove = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::ENDED, // active successors for ACTIVE: no freeze here |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, // active successors for FROZEN: |
| // this state is unreachable here |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE // active successors for ENDED: |
| // restart possible when ended |
| }; |
| |
| // transition table for restart=ALWAYS, fill=REMOVE |
| static const StateTransitionTable stateTransitionTable_Always_Remove = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE: restart |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, // active successors for FROZEN: |
| // this state is unreachable here |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED // active successors for ENDED: restart |
| }; |
| |
| // transition table for restart=NEVER, fill=FREEZE |
| static const StateTransitionTable stateTransitionTable_Never_Freeze = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::FROZEN|AnimationNode::ENDED, // active successors for ACTIVE: freeze object |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED, // active successors for FROZEN: end |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED, // active successors for ENDED: this state is a sink here (cannot restart) |
| }; |
| |
| // transition table for restart=WHEN_NOT_ACTIVE, fill=FREEZE |
| static const StateTransitionTable stateTransitionTable_NotActive_Freeze = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::FROZEN|AnimationNode::ENDED, // active successors for ACTIVE: freeze object |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN: |
| // restart possible when ended |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE // active successors for ENDED: |
| // restart possible when ended |
| }; |
| |
| // transition table for restart=ALWAYS, fill=FREEZE |
| static const StateTransitionTable stateTransitionTable_Always_Freeze = { |
| AnimationNode::INVALID, |
| AnimationNode::RESOLVED|AnimationNode::ENDED, // active successors for UNRESOLVED |
| AnimationNode::ACTIVE|AnimationNode::ENDED, // active successors for RESOLVED |
| AnimationNode::INVALID, |
| AnimationNode::FROZEN|AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED, // active successors for ACTIVE: |
| // end object, restart |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::RESOLVED|AnimationNode::ACTIVE, // active successors for FROZEN: restart possible |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::INVALID, |
| AnimationNode::ENDED|AnimationNode::ACTIVE|AnimationNode::RESOLVED // active successors for ENDED: restart |
| }; |
| |
| static const StateTransitionTable* tableGuide[] = { |
| &stateTransitionTable_Never_Remove, |
| &stateTransitionTable_NotActive_Remove, |
| &stateTransitionTable_Always_Remove, |
| &stateTransitionTable_Never_Freeze, |
| &stateTransitionTable_NotActive_Freeze, |
| &stateTransitionTable_Always_Freeze |
| }; |
| |
| int nRestartValue; |
| switch( nRestartMode ) { |
| default: |
| case animations::AnimationRestart::DEFAULT: |
| // same value: animations::AnimationRestart::INHERIT: |
| OSL_ENSURE( |
| false, "getStateTransitionTable(): unexpected case for restart" ); |
| // FALLTHROUGH intended |
| case animations::AnimationRestart::NEVER: |
| nRestartValue = 0; |
| break; |
| case animations::AnimationRestart::WHEN_NOT_ACTIVE: |
| nRestartValue = 1; |
| break; |
| case animations::AnimationRestart::ALWAYS: |
| nRestartValue = 2; |
| break; |
| } |
| |
| int nFillValue; |
| switch( nFillMode ) { |
| default: |
| case animations::AnimationFill::AUTO: |
| case animations::AnimationFill::DEFAULT: |
| // same value: animations::AnimationFill::INHERIT: |
| OSL_ENSURE( |
| false, "getStateTransitionTable(): unexpected case for fill" ); |
| // FALLTHROUGH intended |
| case animations::AnimationFill::REMOVE: |
| nFillValue = 0; |
| break; |
| case animations::AnimationFill::FREEZE: |
| case animations::AnimationFill::HOLD: |
| case animations::AnimationFill::TRANSITION: |
| nFillValue = 1; |
| break; |
| } |
| |
| return *tableGuide[ 3*nFillValue + nRestartValue ]; |
| } |
| |
| /// Little helper predicate, to detect main sequence root node |
| bool isMainSequenceRootNode_( |
| const uno::Reference< animations::XAnimationNode >& xNode ) |
| { |
| // detect main sequence root node (need that for |
| // end-of-mainsequence signalling below) |
| beans::NamedValue const aSearchKey( |
| rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ), |
| uno::makeAny( presentation::EffectNodeType::MAIN_SEQUENCE ) ); |
| |
| uno::Sequence<beans::NamedValue> const userData(xNode->getUserData()); |
| return findNamedValue( userData, aSearchKey ); |
| } |
| |
| } // anon namespace |
| |
| // BaseNode implementation |
| //========================================================================= |
| |
| /** state transition handling |
| */ |
| class BaseNode::StateTransition : private boost::noncopyable |
| { |
| public: |
| enum Options { NONE, FORCE }; |
| |
| explicit StateTransition( BaseNode * pNode ) |
| : mpNode(pNode), meToState(INVALID) {} |
| |
| ~StateTransition() { |
| clear(); |
| } |
| |
| bool enter( NodeState eToState, int options = NONE ) |
| { |
| OSL_ENSURE( meToState == INVALID, |
| "### commit() before enter()ing again!" ); |
| if (meToState != INVALID) |
| return false; |
| bool const bForce = ((options & FORCE) != 0); |
| if (!bForce && !mpNode->isTransition( mpNode->meCurrState, eToState )) |
| return false; |
| // recursion detection: |
| if ((mpNode->meCurrentStateTransition & eToState) != 0) |
| return false; // already in wanted transition |
| // mark transition: |
| mpNode->meCurrentStateTransition |= eToState; |
| meToState = eToState; |
| return true; // in transition |
| } |
| |
| void commit() { |
| OSL_ENSURE( meToState != INVALID, "### nothing to commit!" ); |
| if (meToState != INVALID) { |
| mpNode->meCurrState = meToState; |
| clear(); |
| } |
| |
| // Uncomment the following line to write the node tree to file on |
| // every state change of one of its nodes. |
| // Debug_ShowNodeTree(mpNode->mpSelf); |
| } |
| |
| void clear() { |
| if (meToState != INVALID) { |
| OSL_ASSERT( (mpNode->meCurrentStateTransition & meToState) != 0 ); |
| mpNode->meCurrentStateTransition &= ~meToState; |
| meToState = INVALID; |
| } |
| } |
| |
| private: |
| BaseNode *const mpNode; |
| NodeState meToState; |
| }; |
| |
| BaseNode::BaseNode( const uno::Reference< animations::XAnimationNode >& xNode, |
| const BaseContainerNodeSharedPtr& rParent, |
| const NodeContext& rContext ) : |
| maContext( rContext.maContext ), |
| maDeactivatingListeners(), |
| mxAnimationNode( xNode ), |
| mpParent( rParent ), |
| mpSelf(), |
| mpStateTransitionTable( NULL ), |
| mnStartDelay( rContext.mnStartDelay ), |
| meCurrState( UNRESOLVED ), |
| meCurrentStateTransition( 0 ), |
| mpCurrentEvent(), |
| mbIsMainSequenceRootNode( isMainSequenceRootNode_( xNode ) ) |
| { |
| ENSURE_OR_THROW( mxAnimationNode.is(), |
| "BaseNode::BaseNode(): Invalid XAnimationNode" ); |
| |
| // setup state transition table |
| mpStateTransitionTable = getStateTransitionTable( getRestartMode(), |
| getFillMode() ); |
| } |
| |
| void BaseNode::dispose() |
| { |
| meCurrState = INVALID; |
| |
| // discharge a loaded event, if any: |
| if (mpCurrentEvent) { |
| mpCurrentEvent->dispose(); |
| mpCurrentEvent.reset(); |
| } |
| maDeactivatingListeners.clear(); |
| mxAnimationNode.clear(); |
| mpParent.reset(); |
| mpSelf.reset(); |
| maContext.dispose(); |
| } |
| |
| |
| sal_Int16 BaseNode::getRestartMode() |
| { |
| const sal_Int16 nTmp( mxAnimationNode->getRestart() ); |
| return (nTmp != animations::AnimationRestart::DEFAULT && |
| nTmp != animations::AnimationRestart::INHERIT) |
| ? nTmp : getRestartDefaultMode(); |
| } |
| |
| sal_Int16 BaseNode::getFillMode() |
| { |
| const sal_Int16 nTmp( mxAnimationNode->getFill() ); |
| const sal_Int16 nFill((nTmp != animations::AnimationFill::DEFAULT && |
| nTmp != animations::AnimationFill::INHERIT) |
| ? nTmp : getFillDefaultMode()); |
| |
| // For AUTO fill mode, SMIL specifies that fill mode is FREEZE, |
| // if no explicit active duration is given |
| // (no duration, end, repeatCount or repeatDuration given), |
| // and REMOVE otherwise |
| if( nFill == animations::AnimationFill::AUTO ) { |
| return (isIndefiniteTiming( mxAnimationNode->getDuration() ) && |
| isIndefiniteTiming( mxAnimationNode->getEnd() ) && |
| !mxAnimationNode->getRepeatCount().hasValue() && |
| isIndefiniteTiming( mxAnimationNode->getRepeatDuration() )) |
| ? animations::AnimationFill::FREEZE |
| : animations::AnimationFill::REMOVE; |
| } |
| else { |
| return nFill; |
| } |
| } |
| |
| sal_Int16 BaseNode::getFillDefaultMode() const |
| { |
| sal_Int16 nFillDefault = mxAnimationNode->getFillDefault(); |
| if (nFillDefault == animations::AnimationFill::DEFAULT) { |
| nFillDefault = (mpParent != 0 |
| ? mpParent->getFillDefaultMode() |
| : animations::AnimationFill::AUTO); |
| } |
| return nFillDefault; |
| } |
| |
| sal_Int16 BaseNode::getRestartDefaultMode() const |
| { |
| sal_Int16 nRestartDefaultMode = mxAnimationNode->getRestartDefault(); |
| if (nRestartDefaultMode == animations::AnimationRestart::DEFAULT) { |
| nRestartDefaultMode = (mpParent != 0 |
| ? mpParent->getRestartDefaultMode() |
| : animations::AnimationRestart::ALWAYS); |
| } |
| return nRestartDefaultMode; |
| } |
| |
| uno::Reference<animations::XAnimationNode> BaseNode::getXAnimationNode() const |
| { |
| return mxAnimationNode; |
| } |
| |
| bool BaseNode::init() |
| { |
| if (! checkValidNode()) |
| return false; |
| meCurrState = UNRESOLVED; |
| // discharge a loaded event, if any: |
| if (mpCurrentEvent) { |
| mpCurrentEvent->dispose(); |
| mpCurrentEvent.reset(); |
| } |
| return init_st(); // may call derived class |
| } |
| |
| bool BaseNode::init_st() |
| { |
| return true; |
| } |
| |
| bool BaseNode::resolve() |
| { |
| if (! checkValidNode()) |
| return false; |
| |
| OSL_ASSERT( meCurrState != RESOLVED ); |
| if (inStateOrTransition( RESOLVED )) |
| return true; |
| |
| StateTransition st(this); |
| if (st.enter( RESOLVED ) && |
| isTransition( RESOLVED, ACTIVE ) && |
| resolve_st() /* may call derived class */) |
| { |
| st.commit(); // changing state |
| |
| // discharge a loaded event, if any: |
| if (mpCurrentEvent) |
| mpCurrentEvent->dispose(); |
| |
| // schedule activation event: |
| |
| // This method takes the NodeContext::mnStartDelay value into account, |
| // to cater for iterate container time shifts. We cannot put different |
| // iterations of the iterate container's children into different |
| // subcontainer (such as a 'DelayContainer', which delays resolving its |
| // children by a fixed amount), since all iterations' nodes must be |
| // resolved at the same time (otherwise, the delayed subset creation |
| // will not work, i.e. deactivate the subsets too late in the master |
| // shape). |
| uno::Any const aBegin( mxAnimationNode->getBegin() ); |
| if (aBegin.hasValue()) { |
| mpCurrentEvent = generateEvent( |
| aBegin, boost::bind( &AnimationNode::activate, mpSelf ), |
| maContext, mnStartDelay ); |
| } |
| else { |
| // For some leaf nodes, PPT import yields empty begin time, |
| // although semantically, it should be 0.0 |
| // TODO(F3): That should really be provided by the PPT import |
| |
| // schedule delayed activation event. Take iterate node |
| // timeout into account |
| mpCurrentEvent = makeDelay( |
| boost::bind( &AnimationNode::activate, mpSelf ), |
| mnStartDelay, |
| "AnimationNode::activate with delay"); |
| maContext.mrEventQueue.addEvent( mpCurrentEvent ); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| bool BaseNode::resolve_st() |
| { |
| return true; |
| } |
| |
| |
| bool BaseNode::activate() |
| { |
| if (! checkValidNode()) |
| return false; |
| |
| OSL_ASSERT( meCurrState != ACTIVE ); |
| if (inStateOrTransition( ACTIVE )) |
| return true; |
| |
| StateTransition st(this); |
| if (st.enter( ACTIVE )) { |
| |
| activate_st(); // calling derived class |
| |
| st.commit(); // changing state |
| |
| maContext.mrEventMultiplexer.notifyAnimationStart( mpSelf ); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void BaseNode::activate_st() |
| { |
| scheduleDeactivationEvent(); |
| } |
| |
| void BaseNode::scheduleDeactivationEvent( EventSharedPtr const& pEvent ) |
| { |
| if (mpCurrentEvent) { |
| mpCurrentEvent->dispose(); |
| mpCurrentEvent.reset(); |
| } |
| if (pEvent) { |
| if (maContext.mrEventQueue.addEvent( pEvent )) |
| mpCurrentEvent = pEvent; |
| } |
| else { |
| // This method need not take the |
| // NodeContext::mnStartDelay value into account, |
| // because the deactivation event is only scheduled |
| // when the effect is started: the timeout is then |
| // already respected. |
| |
| // xxx todo: |
| // think about set node, anim base node! |
| // if anim base node has no activity, this is called to schedule deactivatiion, |
| // but what if it does not schedule anything? |
| |
| // TODO(F2): Handle end time attribute, too |
| mpCurrentEvent = generateEvent( |
| mxAnimationNode->getDuration(), |
| boost::bind( &AnimationNode::deactivate, mpSelf ), |
| maContext, 0.0 ); |
| } |
| } |
| |
| void BaseNode::deactivate() |
| { |
| if (inStateOrTransition( ENDED | FROZEN ) || !checkValidNode()) |
| return; |
| |
| if (isTransition( meCurrState, FROZEN, false /* no OSL_ASSERT */ )) { |
| // do transition to FROZEN: |
| StateTransition st(this); |
| if (st.enter( FROZEN, StateTransition::FORCE )) { |
| |
| deactivate_st( FROZEN ); |
| st.commit(); |
| |
| notifyEndListeners(); |
| |
| // discharge a loaded event, before going on: |
| if (mpCurrentEvent) { |
| mpCurrentEvent->dispose(); |
| mpCurrentEvent.reset(); |
| } |
| } |
| } |
| else { |
| // use end instead: |
| end(); |
| } |
| // state has changed either to FROZEN or ENDED |
| } |
| |
| void BaseNode::deactivate_st( NodeState ) |
| { |
| } |
| |
| void BaseNode::end() |
| { |
| bool const bIsFrozenOrInTransitionToFrozen = inStateOrTransition( FROZEN ); |
| if (inStateOrTransition( ENDED ) || !checkValidNode()) |
| return; |
| |
| // END must always be reachable. If not, that's an error in the |
| // transition tables |
| OSL_ENSURE( isTransition( meCurrState, ENDED ), |
| "end state not reachable in transition table" ); |
| |
| StateTransition st(this); |
| if (st.enter( ENDED, StateTransition::FORCE )) { |
| |
| deactivate_st( ENDED ); |
| st.commit(); // changing state |
| |
| // if is FROZEN or is to be FROZEN, then |
| // will/already notified deactivating listeners |
| if (!bIsFrozenOrInTransitionToFrozen) |
| notifyEndListeners(); |
| |
| // discharge a loaded event, before going on: |
| if (mpCurrentEvent) { |
| mpCurrentEvent->dispose(); |
| mpCurrentEvent.reset(); |
| } |
| } |
| } |
| |
| void BaseNode::notifyDeactivating( const AnimationNodeSharedPtr& rNotifier ) |
| { |
| (void) rNotifier; // avoid warning |
| OSL_ASSERT( rNotifier->getState() == FROZEN || |
| rNotifier->getState() == ENDED ); |
| // TODO(F1): for end sync functionality, this might indeed be used some day |
| } |
| |
| void BaseNode::notifyEndListeners() const |
| { |
| // notify all listeners |
| std::for_each( maDeactivatingListeners.begin(), |
| maDeactivatingListeners.end(), |
| boost::bind( &AnimationNode::notifyDeactivating, _1, |
| boost::cref(mpSelf) ) ); |
| |
| // notify state change |
| maContext.mrEventMultiplexer.notifyAnimationEnd( mpSelf ); |
| |
| // notify main sequence end (iff we're the main |
| // sequence root node). This is because the main |
| // sequence determines the active duration of the |
| // slide. All other sequences are secondary, in that |
| // they don't prevent a slide change from happening, |
| // even if they have not been completed. In other |
| // words, all sequences except the main sequence are |
| // optional for the slide lifetime. |
| if (isMainSequenceRootNode()) |
| maContext.mrEventMultiplexer.notifySlideAnimationsEnd(); |
| } |
| |
| AnimationNode::NodeState BaseNode::getState() const |
| { |
| return meCurrState; |
| } |
| |
| bool BaseNode::registerDeactivatingListener( |
| const AnimationNodeSharedPtr& rNotifee ) |
| { |
| if (! checkValidNode()) |
| return false; |
| |
| ENSURE_OR_RETURN_FALSE( |
| rNotifee, |
| "BaseNode::registerDeactivatingListener(): invalid notifee" ); |
| maDeactivatingListeners.push_back( rNotifee ); |
| |
| return true; |
| } |
| |
| void BaseNode::setSelf( const BaseNodeSharedPtr& rSelf ) |
| { |
| ENSURE_OR_THROW( rSelf.get() == this, |
| "BaseNode::setSelf(): got ptr to different object" ); |
| ENSURE_OR_THROW( !mpSelf, |
| "BaseNode::setSelf(): called multiple times" ); |
| |
| mpSelf = rSelf; |
| } |
| |
| // Debug |
| //========================================================================= |
| |
| #if defined(VERBOSE) && defined(DBG_UTIL) |
| void BaseNode::showState() const |
| { |
| const AnimationNode::NodeState eNodeState( getState() ); |
| |
| if( eNodeState == AnimationNode::INVALID ) |
| VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled," |
| "fillcolor=\"0.5,0.2,0.5\"]", |
| (const char*)this+debugGetCurrentOffset(), |
| getDescription() ); |
| else |
| VERBOSE_TRACE( "Node state: n0x%X [label=\"%s\",style=filled," |
| "fillcolor=\"%f,1.0,1.0\"]", |
| (const char*)this+debugGetCurrentOffset(), |
| getDescription(), |
| log(double(getState()))/4.0 ); |
| |
| // determine additional node information |
| uno::Reference<animations::XAnimate> const xAnimate( mxAnimationNode, |
| uno::UNO_QUERY ); |
| if( xAnimate.is() ) |
| { |
| uno::Reference< drawing::XShape > xTargetShape( xAnimate->getTarget(), |
| uno::UNO_QUERY ); |
| |
| if( !xTargetShape.is() ) |
| { |
| ::com::sun::star::presentation::ParagraphTarget aTarget; |
| |
| // no shape provided. Maybe a ParagraphTarget? |
| if( (xAnimate->getTarget() >>= aTarget) ) |
| xTargetShape = aTarget.Shape; |
| } |
| |
| if( xTargetShape.is() ) |
| { |
| uno::Reference< beans::XPropertySet > xPropSet( xTargetShape, |
| uno::UNO_QUERY ); |
| |
| // read shape name |
| ::rtl::OUString aName; |
| if( (xPropSet->getPropertyValue( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Name") ) ) |
| >>= aName) ) |
| { |
| const ::rtl::OString& rAsciiName( |
| ::rtl::OUStringToOString( aName, |
| RTL_TEXTENCODING_ASCII_US ) ); |
| |
| VERBOSE_TRACE( "Node info: n0x%X, name \"%s\"", |
| (const char*)this+debugGetCurrentOffset(), |
| rAsciiName.getStr() ); |
| } |
| } |
| } |
| } |
| |
| const char* BaseNode::getDescription() const |
| { |
| return "BaseNode"; |
| } |
| |
| void BaseNode::showTreeFromWithin() const |
| { |
| // find root node |
| BaseNodeSharedPtr pCurrNode( mpSelf ); |
| while( pCurrNode->mpParent ) pCurrNode = pCurrNode->mpParent; |
| |
| pCurrNode->showState(); |
| } |
| #endif |
| |
| } // namespace internal |
| } // namespace slideshow |
| |