/************************************************************** * * 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_sd.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CustomAnimationEffect.hxx" #include #include "animations.hxx" using namespace ::com::sun::star; using namespace ::com::sun::star::presentation; using namespace ::com::sun::star::animations; using ::rtl::OUString; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::XInterface; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::makeAny; using ::com::sun::star::uno::Exception; using ::com::sun::star::uno::RuntimeException; using ::com::sun::star::container::XEnumerationAccess; using ::com::sun::star::container::XEnumeration; using ::com::sun::star::beans::NamedValue; using ::com::sun::star::container::XChild; using ::com::sun::star::container::XElementAccess; using ::com::sun::star::drawing::XShape; using ::com::sun::star::lang::XInitialization; using ::com::sun::star::drawing::XShapes; using ::com::sun::star::drawing::XDrawPage; using ::com::sun::star::text::XText; using ::com::sun::star::text::XTextRange; using ::com::sun::star::beans::XPropertySet; using ::com::sun::star::lang::XMultiServiceFactory; using ::com::sun::star::util::XCloneable; using ::com::sun::star::lang::Locale; using ::com::sun::star::util::XChangesNotifier; using ::com::sun::star::util::XChangesListener; namespace sd { class MainSequenceChangeGuard { public: MainSequenceChangeGuard( EffectSequenceHelper* pSequence ) { mpMainSequence = dynamic_cast< MainSequence* >( pSequence ); if( mpMainSequence == 0 ) { InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence ); if( pI ) mpMainSequence = pI->mpMainSequence; } DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" ); if( mpMainSequence ) mpMainSequence->mbIgnoreChanges++; } ~MainSequenceChangeGuard() { if( mpMainSequence ) mpMainSequence->mbIgnoreChanges++; } private: MainSequence* mpMainSequence; }; CustomAnimationEffect::CustomAnimationEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) : mnNodeType(-1), mnPresetClass(-1), mfBegin(-1.0), mfDuration(-1.0), mfAbsoluteDuration(-1.0), mnGroupId(-1), mnIterateType(0), mfIterateInterval(0.0), mnParaDepth( -1 ), mbHasText(sal_False), mfAcceleration( 1.0 ), mfDecelerate( 1.0 ), mbAutoReverse(false), mnTargetSubItem(0), mnCommand(0), mpEffectSequence( 0 ), mbHasAfterEffect(false), mbAfterEffectOnNextEffect(false) { setNode( xNode ); } // -------------------------------------------------------------------- void CustomAnimationEffect::setNode( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) { mxNode = xNode; mxAudio.clear(); Sequence< NamedValue > aUserData( mxNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); const NamedValue* p = aUserData.getConstArray(); while( nLength-- ) { if( p->Name.equalsAscii( "node-type" ) ) { p->Value >>= mnNodeType; } else if( p->Name.equalsAscii( "preset-id" ) ) { p->Value >>= maPresetId; } else if( p->Name.equalsAscii( "preset-sub-type" ) ) { p->Value >>= maPresetSubType; } else if( p->Name.equalsAscii( "preset-class" ) ) { p->Value >>= mnPresetClass; } else if( p->Name.equalsAscii( "preset-property" ) ) { p->Value >>= maProperty; } else if( p->Name.equalsAscii( "group-id" ) ) { p->Value >>= mnGroupId; } p++; } // get effect start time mxNode->getBegin() >>= mfBegin; mfAcceleration = mxNode->getAcceleration(); mfDecelerate = mxNode->getDecelerate(); mbAutoReverse = mxNode->getAutoReverse(); // get iteration data Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); if( xIter.is() ) { mfIterateInterval = xIter->getIterateInterval(); mnIterateType = xIter->getIterateType(); maTarget = xIter->getTarget(); mnTargetSubItem = xIter->getSubItem(); } else { mfIterateInterval = 0.0f; mnIterateType = 0; } // calculate effect duration and get target shape Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); if( !xChildNode.is() ) continue; if( xChildNode->getType() == AnimationNodeType::AUDIO ) { mxAudio.set( xChildNode, UNO_QUERY ); } else if( xChildNode->getType() == AnimationNodeType::COMMAND ) { Reference< XCommand > xCommand( xChildNode, UNO_QUERY ); if( xCommand.is() ) { mnCommand = xCommand->getCommand(); if( !maTarget.hasValue() ) maTarget = xCommand->getTarget(); } } else { double fBegin = 0.0; double fDuration = 0.0; xChildNode->getBegin() >>= fBegin; xChildNode->getDuration() >>= fDuration; fDuration += fBegin; if( fDuration > mfDuration ) mfDuration = fDuration; // no target shape yet? if( !maTarget.hasValue() ) { // go get it boys! Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY ); if( xAnimate.is() ) { maTarget = xAnimate->getTarget(); mnTargetSubItem = xAnimate->getSubItem(); } } } } } } mfAbsoluteDuration = mfDuration; checkForText(); } // -------------------------------------------------------------------- sal_Int32 CustomAnimationEffect::getNumberOfSubitems( const Any& aTarget, sal_Int16 nIterateType ) { sal_Int32 nSubItems = 0; try { // first get target text sal_Int32 nOnlyPara = -1; Reference< XText > xShape; aTarget >>= xShape; if( !xShape.is() ) { ParagraphTarget aParaTarget; if( aTarget >>= aParaTarget ) { xShape.set( aParaTarget.Shape, UNO_QUERY ); nOnlyPara = aParaTarget.Paragraph; } } // now use the break iterator to iterate over the given text // and count the sub items if( xShape.is() ) { // TODO/LATER: Optimize this, don't create a break iterator each time Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() ); Reference < i18n::XBreakIterator > xBI( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY ); DBG_ASSERT( xBI.is(), "sd::CustomAnimationEffect::getNumberOfSubitems(), could not create a 'com.sun.star.i18n.BreakIterator'!" ); if( xBI.is() ) { Reference< XEnumerationAccess > xEA( xShape, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_QUERY_THROW ); Locale aLocale; const OUString aStrLocaleName( RTL_CONSTASCII_USTRINGPARAM("CharLocale") ); Reference< XTextRange > xParagraph; sal_Int32 nPara = 0; while( xEnumeration->hasMoreElements() ) { xEnumeration->nextElement() >>= xParagraph; // skip this if its not the only paragraph we want to count if( (nOnlyPara != -1) && (nOnlyPara != nPara ) ) continue; if( nIterateType == TextAnimationType::BY_PARAGRAPH ) { nSubItems++; } else { const OUString aText( xParagraph->getString() ); Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW ); xSet->getPropertyValue( aStrLocaleName ) >>= aLocale; sal_Int32 nPos; const sal_Int32 nEndPos = aText.getLength(); if( nIterateType == TextAnimationType::BY_WORD ) { for( nPos = 0; nPos < nEndPos; nPos++ ) { nPos = xBI->getWordBoundary(aText, nPos, aLocale, i18n::WordType::ANY_WORD, sal_True).endPos; nSubItems++; } break; } else { sal_Int32 nDone; for( nPos = 0; nPos < nEndPos; nPos++ ) { nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone); nSubItems++; } } } if( nPara == nOnlyPara ) break; nPara++; } } } } catch( Exception& e ) { (void)e; nSubItems = 0; DBG_ERROR( "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" ); } return nSubItems; } // -------------------------------------------------------------------- CustomAnimationEffect::~CustomAnimationEffect() { } // -------------------------------------------------------------------- CustomAnimationEffectPtr CustomAnimationEffect::clone() const { Reference< XCloneable > xCloneable( mxNode, UNO_QUERY_THROW ); Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW ); CustomAnimationEffectPtr pEffect( new CustomAnimationEffect( xNode ) ); pEffect->setEffectSequence( getEffectSequence() ); return pEffect; } // -------------------------------------------------------------------- sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode ) { sal_Int16 nNodeType = -1; if( xNode.is() ) { Sequence< NamedValue > aUserData( xNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); if( nLength ) { const NamedValue* p = aUserData.getConstArray(); while( nLength-- ) { if( p->Name.equalsAscii( "node-type" ) ) { p->Value >>= nNodeType; break; } p++; } } } return nNodeType; } // -------------------------------------------------------------------- void CustomAnimationEffect::setPresetClass( sal_Int16 nPresetClass ) { if( mnPresetClass != nPresetClass ) { mnPresetClass = nPresetClass; if( mxNode.is() ) { // first try to find a "preset-class" entry in the user data // and change it Sequence< NamedValue > aUserData( mxNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); bool bFound = false; if( nLength ) { NamedValue* p = aUserData.getArray(); while( nLength-- ) { if( p->Name.equalsAscii( "preset-class" ) ) { p->Value <<= mnPresetClass; bFound = true; break; } p++; } } // no "node-type" entry inside user data, so add it if( !bFound ) { nLength = aUserData.getLength(); aUserData.realloc( nLength + 1); aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "preset-class" ) ); aUserData[nLength].Value <<= mnPresetClass; } mxNode->setUserData( aUserData ); } } } void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType ) { if( mnNodeType != nNodeType ) { mnNodeType = nNodeType; if( mxNode.is() ) { // first try to find a "node-type" entry in the user data // and change it Sequence< NamedValue > aUserData( mxNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); bool bFound = false; if( nLength ) { NamedValue* p = aUserData.getArray(); while( nLength-- ) { if( p->Name.equalsAscii( "node-type" ) ) { p->Value <<= mnNodeType; bFound = true; break; } p++; } } // no "node-type" entry inside user data, so add it if( !bFound ) { nLength = aUserData.getLength(); aUserData.realloc( nLength + 1); aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ); aUserData[nLength].Value <<= mnNodeType; } mxNode->setUserData( aUserData ); } } } // -------------------------------------------------------------------- void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId ) { mnGroupId = nGroupId; if( mxNode.is() ) { // first try to find a "group-id" entry in the user data // and change it Sequence< NamedValue > aUserData( mxNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); bool bFound = false; if( nLength ) { NamedValue* p = aUserData.getArray(); while( nLength-- ) { if( p->Name.equalsAscii( "group-id" ) ) { p->Value <<= mnGroupId; bFound = true; break; } p++; } } // no "node-type" entry inside user data, so add it if( !bFound ) { nLength = aUserData.getLength(); aUserData.realloc( nLength + 1); aUserData[nLength].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "group-id" ) ); aUserData[nLength].Value <<= mnGroupId; } mxNode->setUserData( aUserData ); } } // -------------------------------------------------------------------- /** checks if the text for this effect has changed and updates internal flags. returns true if something changed. */ bool CustomAnimationEffect::checkForText() { bool bChange = false; Reference< XText > xText; if( maTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { // calc para depth ParagraphTarget aParaTarget; maTarget >>= aParaTarget; xText = Reference< XText >::query( aParaTarget.Shape ); // get paragraph if( xText.is() ) { Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); if( xEA.is() ) { Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { sal_Bool bHasText = xEnumeration->hasMoreElements(); bChange |= bHasText != mbHasText; mbHasText = bHasText; sal_Int32 nPara = aParaTarget.Paragraph; while( xEnumeration->hasMoreElements() && nPara-- ) xEnumeration->nextElement(); if( xEnumeration->hasMoreElements() ) { Reference< XPropertySet > xParaSet; xEnumeration->nextElement() >>= xParaSet; if( xParaSet.is() ) { sal_Int32 nParaDepth = 0; const OUString strNumberingLevel( RTL_CONSTASCII_USTRINGPARAM("NumberingLevel") ); xParaSet->getPropertyValue( strNumberingLevel ) >>= nParaDepth; bChange |= nParaDepth != mnParaDepth; mnParaDepth = nParaDepth; } } } } } } else { maTarget >>= xText; sal_Bool bHasText = xText.is() && xText->getString().getLength(); bChange |= bHasText != mbHasText; mbHasText = bHasText; } bChange |= calculateIterateDuration(); return bChange; } bool CustomAnimationEffect::calculateIterateDuration() { bool bChange = false; // if we have an iteration, we must also calculate the // 'true' container duration, that is // ( ( is form animated ) ? [contained effects duration] : 0 ) + // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration] Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); if( xIter.is() ) { double fDuration = mfDuration; const double fSubEffectDuration = mfDuration; if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check { const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType ); if( nSubItems ) { const double f = (nSubItems-1) * mfIterateInterval; fDuration += f; } } // if we also animate the form first, we have to add the // sub effect duration to the whole effect duration if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE ) fDuration += fSubEffectDuration; bChange |= fDuration != mfAbsoluteDuration; mfAbsoluteDuration = fDuration; } return bChange; } // -------------------------------------------------------------------- void CustomAnimationEffect::setTarget( const ::com::sun::star::uno::Any& rTarget ) { try { maTarget = rTarget; // first, check special case for random node Reference< XInitialization > xInit( mxNode, UNO_QUERY ); if( xInit.is() ) { const Sequence< Any > aArgs( &maTarget, 1 ); xInit->initialize( aArgs ); } else { Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); if( xIter.is() ) { xIter->setTarget(maTarget); } else { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { const Any aElem( xEnumeration->nextElement() ); Reference< XAnimate > xAnimate( aElem, UNO_QUERY ); if( xAnimate.is() ) xAnimate->setTarget( rTarget ); else { Reference< XCommand > xCommand( aElem, UNO_QUERY ); if( xCommand.is() ) xCommand->setTarget( rTarget ); } } } } } } checkForText(); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setTarget(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem ) { try { mnTargetSubItem = nSubItem; Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); if( xIter.is() ) { xIter->setSubItem(mnTargetSubItem); } else { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); if( xAnimate.is() ) xAnimate->setSubItem( mnTargetSubItem ); } } } } } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setTargetSubItem(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setDuration( double fDuration ) { if( (mfDuration != -1.0) && (mfDuration != fDuration) ) try { double fScale = fDuration / mfDuration; mfDuration = fDuration; mfAbsoluteDuration = mfDuration; // calculate effect duration and get target shape Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); if( !xChildNode.is() ) continue; double fChildBegin = 0.0; xChildNode->getBegin() >>= fChildBegin; if( fChildBegin != 0.0 ) { fChildBegin *= fScale; xChildNode->setBegin( makeAny( fChildBegin ) ); } double fChildDuration = 0.0; xChildNode->getDuration() >>= fChildDuration; if( fChildDuration != 0.0 ) { fChildDuration *= fScale; xChildNode->setDuration( makeAny( fChildDuration ) ); } } } } calculateIterateDuration(); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setDuration(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setBegin( double fBegin ) { if( mxNode.is() ) try { mfBegin = fBegin; mxNode->setBegin( makeAny( fBegin ) ); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setBegin(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setAcceleration( double fAcceleration ) { if( mxNode.is() ) try { mfAcceleration = fAcceleration; mxNode->setAcceleration( fAcceleration ); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setAcceleration(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setDecelerate( double fDecelerate ) { if( mxNode.is() ) try { mfDecelerate = fDecelerate; mxNode->setDecelerate( fDecelerate ); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setDecelerate(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setAutoReverse( sal_Bool bAutoReverse ) { if( mxNode.is() ) try { mbAutoReverse = bAutoReverse; mxNode->setAutoReverse( bAutoReverse ); } catch( Exception& ) { DBG_ERROR( "sd::CustomAnimationEffect::setAutoReverse(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::replaceNode( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) { sal_Int16 nNodeType = mnNodeType; Any aTarget = maTarget; double fBegin = mfBegin; double fDuration = mfDuration; double fAcceleration = mfAcceleration; double fDecelerate = mfDecelerate ; sal_Bool bAutoReverse = mbAutoReverse; Reference< XAudio > xAudio( mxAudio ); sal_Int16 nIterateType = mnIterateType; double fIterateInterval = mfIterateInterval; sal_Int16 nSubItem = mnTargetSubItem; setNode( xNode ); setAudio( xAudio ); setNodeType( nNodeType ); setTarget( aTarget ); setTargetSubItem( nSubItem ); setDuration( fDuration ); setBegin( fBegin ); setAcceleration( fAcceleration ); setDecelerate( fDecelerate ); setAutoReverse( bAutoReverse ); if( nIterateType != mnIterateType ) setIterateType( nIterateType ); if( mnIterateType && ( fIterateInterval != mfIterateInterval ) ) setIterateInterval( fIterateInterval ); } // -------------------------------------------------------------------- Reference< XShape > CustomAnimationEffect::getTargetShape() const { Reference< XShape > xShape; maTarget >>= xShape; if( !xShape.is() ) { ParagraphTarget aParaTarget; if( maTarget >>= aParaTarget ) xShape = aParaTarget.Shape; } return xShape; } // -------------------------------------------------------------------- Any CustomAnimationEffect::getRepeatCount() const { if( mxNode.is() ) { return mxNode->getRepeatCount(); } else { Any aAny; return aAny; } } // -------------------------------------------------------------------- Any CustomAnimationEffect::getEnd() const { if( mxNode.is() ) { return mxNode->getEnd(); } else { Any aAny; return aAny; } } // -------------------------------------------------------------------- sal_Int16 CustomAnimationEffect::getFill() const { if( mxNode.is() ) return mxNode->getFill(); else return 0; } // -------------------------------------------------------------------- void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount ) { if( mxNode.is() ) mxNode->setRepeatCount( rRepeatCount ); } // -------------------------------------------------------------------- void CustomAnimationEffect::setEnd( const Any& rEnd ) { if( mxNode.is() ) mxNode->setEnd( rEnd ); } // -------------------------------------------------------------------- void CustomAnimationEffect::setFill( sal_Int16 nFill ) { if( mxNode.is() ) mxNode->setFill( nFill ); } // -------------------------------------------------------------------- Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const throw (Exception) { DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" ); Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() ); const char* pServiceName = maDimColor.hasValue() ? "com.sun.star.animations.AnimateColor" : "com.sun.star.animations.AnimateSet"; Reference< XAnimate > xAnimate( xMsf->createInstance(OUString::createFromAscii(pServiceName) ), UNO_QUERY_THROW ); Any aTo; OUString aAttributeName; if( maDimColor.hasValue() ) { aTo = maDimColor; aAttributeName = OUString( RTL_CONSTASCII_USTRINGPARAM( "DimColor" ) ); } else { aTo = makeAny( (sal_Bool)sal_False ); aAttributeName = OUString( RTL_CONSTASCII_USTRINGPARAM( "Visibility" ) ); } Any aBegin; if( !mbAfterEffectOnNextEffect ) // sameClick { Event aEvent; aEvent.Source <<= getNode(); aEvent.Trigger = EventTrigger::END_EVENT; aEvent.Repeat = 0; aBegin <<= aEvent; } else { aBegin <<= (double)0.0; } xAnimate->setBegin( aBegin ); xAnimate->setTo( aTo ); xAnimate->setAttributeName( aAttributeName ); xAnimate->setDuration( makeAny( (double)0.001 ) ); xAnimate->setFill( AnimationFill::HOLD ); xAnimate->setTarget( maTarget ); return Reference< XAnimationNode >( xAnimate, UNO_QUERY_THROW ); } // -------------------------------------------------------------------- void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType ) { if( mnIterateType != nIterateType ) try { // do we need to exchange the container node? if( (mnIterateType == 0) || (nIterateType == 0) ) { sal_Int16 nTargetSubItem = mnTargetSubItem; Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() ); const char * pServiceName = nIterateType ? "com.sun.star.animations.IterateContainer" : "com.sun.star.animations.ParallelTimeContainer"; Reference< XTimeContainer > xNewContainer( xMsf->createInstance( OUString::createFromAscii(pServiceName) ), UNO_QUERY_THROW ); Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW ); Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); xOldContainer->removeChild( xChildNode ); xNewContainer->appendChild( xChildNode ); } Reference< XAnimationNode > xNewNode( xNewContainer, UNO_QUERY_THROW ); xNewNode->setBegin( mxNode->getBegin() ); xNewNode->setDuration( mxNode->getDuration() ); xNewNode->setEnd( mxNode->getEnd() ); xNewNode->setEndSync( mxNode->getEndSync() ); xNewNode->setRepeatCount( mxNode->getRepeatCount() ); xNewNode->setFill( mxNode->getFill() ); xNewNode->setFillDefault( mxNode->getFillDefault() ); xNewNode->setRestart( mxNode->getRestart() ); xNewNode->setRestartDefault( mxNode->getRestartDefault() ); xNewNode->setAcceleration( mxNode->getAcceleration() ); xNewNode->setDecelerate( mxNode->getDecelerate() ); xNewNode->setAutoReverse( mxNode->getAutoReverse() ); xNewNode->setRepeatDuration( mxNode->getRepeatDuration() ); xNewNode->setEndSync( mxNode->getEndSync() ); xNewNode->setRepeatCount( mxNode->getRepeatCount() ); xNewNode->setUserData( mxNode->getUserData() ); mxNode = xNewNode; Any aTarget; if( nIterateType ) { Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); xIter->setTarget(maTarget); xIter->setSubItem( nTargetSubItem ); } else { aTarget = maTarget; } Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW ); Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_QUERY_THROW ); while( xE->hasMoreElements() ) { Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY ); if( xAnimate.is() ) { xAnimate->setTarget( aTarget ); xAnimate->setSubItem( nTargetSubItem ); } } } mnIterateType = nIterateType; // if we have an iteration container, we must set its type if( mnIterateType ) { Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); xIter->setIterateType( nIterateType ); } checkForText(); } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::CustomAnimationEffect::setIterateType(), Exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setIterateInterval( double fIterateInterval ) { if( mfIterateInterval != fIterateInterval ) { Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" ); if( xIter.is() ) { mfIterateInterval = fIterateInterval; xIter->setIterateInterval( fIterateInterval ); } calculateIterateDuration(); } } // -------------------------------------------------------------------- ::rtl::OUString CustomAnimationEffect::getPath() const { ::rtl::OUString aPath; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); if( xMotion.is() ) { xMotion->getPath() >>= aPath; break; } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::getPath(), exception caught!" ); } return aPath; } // -------------------------------------------------------------------- void CustomAnimationEffect::setPath( const ::rtl::OUString& rPath ) { if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); if( xMotion.is() ) { MainSequenceChangeGuard aGuard( mpEffectSequence ); xMotion->setPath( Any( rPath ) ); break; } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setPath(), exception caught!" ); } } // -------------------------------------------------------------------- Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue ) { Any aProperty; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) { Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); if( !xAnimate.is() ) continue; if( xAnimate->getType() == nNodeType ) { if( xAnimate->getAttributeName() == rAttributeName ) { switch( eValue ) { case VALUE_FROM: aProperty = xAnimate->getFrom(); break; case VALUE_TO: aProperty = xAnimate->getTo(); break; case VALUE_BY: aProperty = xAnimate->getBy(); break; case VALUE_FIRST: case VALUE_LAST: { Sequence aValues( xAnimate->getValues() ); if( aValues.hasElements() ) aProperty = aValues[ eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1 ]; } break; } } } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::getProperty(), exception caught!" ); } return aProperty; } // -------------------------------------------------------------------- bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, const OUString& rAttributeName, EValue eValue, const Any& rValue ) { bool bChanged = false; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); if( !xAnimate.is() ) continue; if( xAnimate->getType() == nNodeType ) { if( xAnimate->getAttributeName() == rAttributeName ) { switch( eValue ) { case VALUE_FROM: if( xAnimate->getFrom() != rValue ) { xAnimate->setFrom( rValue ); bChanged = true; } break; case VALUE_TO: if( xAnimate->getTo() != rValue ) { xAnimate->setTo( rValue ); bChanged = true; } break; case VALUE_BY: if( xAnimate->getTo() != rValue ) { xAnimate->setBy( rValue ); bChanged = true; } break; case VALUE_FIRST: case VALUE_LAST: { Sequence aValues( xAnimate->getValues() ); if( !aValues.hasElements() ) aValues.realloc(1); sal_Int32 nIndex = eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1; if( aValues[ nIndex ] != rValue ) { aValues[ nIndex ] = rValue; xAnimate->setValues( aValues ); bChanged = true; } } } } } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setProperty(), exception caught!" ); } return bChanged; } // -------------------------------------------------------------------- static bool implIsColorAttribute( const OUString& rAttributeName ) { return rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("FillColor") ) || rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("LineColor") ) || rAttributeName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("CharColor") ); } // -------------------------------------------------------------------- Any CustomAnimationEffect::getColor( sal_Int32 nIndex ) { Any aColor; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() && !aColor.hasValue() ) { Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); if( !xAnimate.is() ) continue; switch( xAnimate->getType() ) { case AnimationNodeType::SET: case AnimationNodeType::ANIMATE: if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) break; case AnimationNodeType::ANIMATECOLOR: Sequence aValues( xAnimate->getValues() ); if( aValues.hasElements() ) { if( aValues.getLength() > nIndex ) aColor = aValues[nIndex]; } else if( nIndex == 0 ) aColor = xAnimate->getFrom(); else aColor = xAnimate->getTo(); } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::getColor(), exception caught!" ); } return aColor; } // -------------------------------------------------------------------- void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor ) { if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); if( !xAnimate.is() ) continue; switch( xAnimate->getType() ) { case AnimationNodeType::SET: case AnimationNodeType::ANIMATE: if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) break; case AnimationNodeType::ANIMATECOLOR: { Sequence aValues( xAnimate->getValues() ); if( aValues.hasElements() ) { if( aValues.getLength() > nIndex ) { aValues[nIndex] = rColor; xAnimate->setValues( aValues ); } } else if( (nIndex == 0) && xAnimate->getFrom().hasValue() ) xAnimate->setFrom(rColor); else if( (nIndex == 1) && xAnimate->getTo().hasValue() ) xAnimate->setTo(rColor); } break; } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setColor(), exception caught!" ); } } // -------------------------------------------------------------------- Any CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType, EValue eValue ) { Any aProperty; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) { Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); if( !xTransform.is() ) continue; if( xTransform->getTransformType() == nTransformType ) { switch( eValue ) { case VALUE_FROM: aProperty = xTransform->getFrom(); break; case VALUE_TO: aProperty = xTransform->getTo(); break; case VALUE_BY: aProperty = xTransform->getBy(); break; case VALUE_FIRST: case VALUE_LAST: { Sequence aValues( xTransform->getValues() ); if( aValues.hasElements() ) aProperty = aValues[ eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1 ]; } break; } } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::getTransformationProperty(), exception caught!" ); } return aProperty; } // -------------------------------------------------------------------- bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue ) { bool bChanged = false; if( mxNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); if( xEnumerationAccess.is() ) { Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY ); if( xEnumeration.is() ) { while( xEnumeration->hasMoreElements() ) { Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); if( !xTransform.is() ) continue; if( xTransform->getTransformType() == nTransformType ) { switch( eValue ) { case VALUE_FROM: if( xTransform->getFrom() != rValue ) { xTransform->setFrom( rValue ); bChanged = true; } break; case VALUE_TO: if( xTransform->getTo() != rValue ) { xTransform->setTo( rValue ); bChanged = true; } break; case VALUE_BY: if( xTransform->getBy() != rValue ) { xTransform->setBy( rValue ); bChanged = true; } break; case VALUE_FIRST: case VALUE_LAST: { Sequence aValues( xTransform->getValues() ); if( !aValues.hasElements() ) aValues.realloc(1); sal_Int32 nIndex = eValue == VALUE_FIRST ? 0 : aValues.getLength() - 1; if( aValues[nIndex] != rValue ) { aValues[nIndex] = rValue; xTransform->setValues( aValues ); bChanged = true; } } } } } } } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setTransformationProperty(), exception caught!" ); } return bChanged; } // -------------------------------------------------------------------- void CustomAnimationEffect::createAudio( const ::com::sun::star::uno::Any& rSource, double fVolume /* = 1.0 */ ) { DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" ); if( !mxAudio.is() ) try { Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() ); Reference< XAudio > xAudio( xMsf->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.Audio") ) ), UNO_QUERY_THROW ); xAudio->setSource( rSource ); xAudio->setVolume( fVolume ); setAudio( xAudio ); } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::createAudio(), exception caught!" ); } } // -------------------------------------------------------------------- static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode ) { Reference< XCommand > xCommand; if( xRootNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( !xCommand.is() && xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY ); if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) ) xCommand.set( xNode, UNO_QUERY_THROW ); } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::findCommandNode(), exception caught!" ); } return xCommand; } void CustomAnimationEffect::removeAudio() { try { Reference< XAnimationNode > xChild; if( mxAudio.is() ) { xChild.set( mxAudio, UNO_QUERY ); mxAudio.clear(); } else if( mnCommand == EffectCommands::STOPAUDIO ) { xChild.set( findCommandNode( mxNode ), UNO_QUERY ); mnCommand = 0; } if( xChild.is() ) { Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); if( xContainer.is() ) xContainer->removeChild( xChild ); } } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::removeAudio(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setAudio( const Reference< ::com::sun::star::animations::XAudio >& xAudio ) { if( mxAudio != xAudio ) try { removeAudio(); mxAudio = xAudio; Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); Reference< XAnimationNode > xChild( mxAudio, UNO_QUERY ); if( xContainer.is() && xChild.is() ) xContainer->appendChild( xChild ); } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setAudio(), exception caught!" ); } } // -------------------------------------------------------------------- void CustomAnimationEffect::setStopAudio() { if( mnCommand != EffectCommands::STOPAUDIO ) try { if( mxAudio.is() ) removeAudio(); Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() ); Reference< XCommand > xCommand( xMsf->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.Command") ) ), UNO_QUERY_THROW ); xCommand->setCommand( EffectCommands::STOPAUDIO ); Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW ); Reference< XAnimationNode > xChild( xCommand, UNO_QUERY_THROW ); xContainer->appendChild( xChild ); mnCommand = EffectCommands::STOPAUDIO; } catch( Exception& e ) { (void)e; DBG_ERROR("sd::CustomAnimationEffect::setStopAudio(), exception caught!" ); } } // -------------------------------------------------------------------- bool CustomAnimationEffect::getStopAudio() const { return mnCommand == EffectCommands::STOPAUDIO; } // -------------------------------------------------------------------- SdrPathObj* CustomAnimationEffect::createSdrPathObjFromPath() { SdrPathObj * pPathObj = new SdrPathObj( OBJ_PATHLINE ); updateSdrPathObjFromPath( *pPathObj ); return pPathObj; } // -------------------------------------------------------------------- void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj& rPathObj ) { ::basegfx::B2DPolyPolygon xPolyPoly; if( ::basegfx::tools::importFromSvgD( xPolyPoly, getPath(), true, 0 ) ) { SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() ); if( pObj ) { SdrPage* pPage = pObj->GetPage(); if( pPage ) { const Size aPageSize( pPage->GetSize() ); xPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix((double)aPageSize.Width(), (double)aPageSize.Height())); } const Rectangle aBoundRect( pObj->GetCurrentBoundRect() ); const Point aCenter( aBoundRect.Center() ); xPolyPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y())); } } rPathObj.SetPathPoly( xPolyPoly ); } // -------------------------------------------------------------------- void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj& rPathObj ) { ::basegfx::B2DPolyPolygon xPolyPoly( rPathObj.GetPathPoly() ); SdrObject* pObj = GetSdrObjectFromXShape( getTargetShape() ); if( pObj ) { Rectangle aBoundRect(0,0,0,0); const drawinglayer::primitive2d::Primitive2DSequence xPrimitives(pObj->GetViewContact().getViewIndependentPrimitive2DSequence()); const drawinglayer::geometry::ViewInformation2D aViewInformation2D; const basegfx::B2DRange aRange(drawinglayer::primitive2d::getB2DRangeFromPrimitive2DSequence(xPrimitives, aViewInformation2D)); if(!aRange.isEmpty()) { aBoundRect = Rectangle( (sal_Int32)floor(aRange.getMinX()), (sal_Int32)floor(aRange.getMinY()), (sal_Int32)ceil(aRange.getMaxX()), (sal_Int32)ceil(aRange.getMaxY())); } const Point aCenter( aBoundRect.Center() ); xPolyPoly.transform(basegfx::tools::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y())); SdrPage* pPage = pObj->GetPage(); if( pPage ) { const Size aPageSize( pPage->GetSize() ); xPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix( 1.0 / (double)aPageSize.Width(), 1.0 / (double)aPageSize.Height())); } } setPath( ::basegfx::tools::exportToSvgD( xPolyPoly, true, true, true) ); } // ==================================================================== EffectSequenceHelper::EffectSequenceHelper() : mnSequenceType( EffectNodeType::DEFAULT ) { } // -------------------------------------------------------------------- EffectSequenceHelper::EffectSequenceHelper( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XTimeContainer >& xSequenceRoot ) : mxSequenceRoot( xSequenceRoot ), mnSequenceType( EffectNodeType::DEFAULT ) { Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW ); create( xNode ); } // -------------------------------------------------------------------- EffectSequenceHelper::~EffectSequenceHelper() { reset(); } // -------------------------------------------------------------------- void EffectSequenceHelper::reset() { EffectSequence::iterator aIter( maEffects.begin() ); EffectSequence::iterator aEnd( maEffects.end() ); if( aIter != aEnd ) { CustomAnimationEffectPtr pEffect = (*aIter++); pEffect->setEffectSequence(0); } maEffects.clear(); } Reference< XAnimationNode > EffectSequenceHelper::getRootNode() { Reference< XAnimationNode > xRoot( mxSequenceRoot, UNO_QUERY ); return xRoot; } // -------------------------------------------------------------------- void EffectSequenceHelper::append( const CustomAnimationEffectPtr& pEffect ) { pEffect->setEffectSequence( this ); maEffects.push_back(pEffect); rebuild(); } // -------------------------------------------------------------------- CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ ) { CustomAnimationEffectPtr pEffect; if( pPreset.get() ) { OUString strEmpty; Reference< XAnimationNode > xNode( pPreset->create( strEmpty ) ); if( xNode.is() ) { // first, filter all only ui relevant user data std::vector< NamedValue > aNewUserData; Sequence< NamedValue > aUserData( xNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); const NamedValue* p = aUserData.getConstArray(); bool bFilter = false; while( nLength-- ) { if( !p->Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "text-only" ) ) && !p->Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "preset-property" ) ) ) { aNewUserData.push_back( *p ); bFilter = true; } p++; } if( bFilter ) { aUserData = ::comphelper::containerToSequence< NamedValue, std::vector< NamedValue > >( aNewUserData ); xNode->setUserData( aUserData ); } // check target, maybe we need to force it to text Any aTarget( rTarget ); sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; if( aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { nSubItem = ShapeAnimationSubType::ONLY_TEXT; } else if( pPreset->isTextOnly() ) { Reference< XShape > xShape; aTarget >>= xShape; if( xShape.is() ) { // thats bad, we target a shape here but the effect is only for text // so change subitem nSubItem = ShapeAnimationSubType::ONLY_TEXT; } } // now create effect from preset pEffect.reset( new CustomAnimationEffect( xNode ) ); pEffect->setEffectSequence( this ); pEffect->setTarget( aTarget ); pEffect->setTargetSubItem( nSubItem ); if( fDuration != -1.0 ) pEffect->setDuration( fDuration ); maEffects.push_back(pEffect); rebuild(); } } DBG_ASSERT( pEffect.get(), "sd::EffectSequenceHelper::append(), failed!" ); return pEffect; } // -------------------------------------------------------------------- CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */ ) { CustomAnimationEffectPtr pEffect; if( fDuration <= 0.0 ) fDuration = 2.0; try { Reference< XTimeContainer > xEffectContainer( createParallelTimeContainer() ); const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.animations.AnimateMotion" ) ); Reference< XAnimationNode > xAnimateMotion( ::comphelper::getProcessServiceFactory()->createInstance(aServiceName), UNO_QUERY_THROW ); xAnimateMotion->setDuration( Any( fDuration ) ); xAnimateMotion->setFill( AnimationFill::HOLD ); xEffectContainer->appendChild( xAnimateMotion ); sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; if( rTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) nSubItem = ShapeAnimationSubType::ONLY_TEXT; Reference< XAnimationNode > xEffectNode( xEffectContainer, UNO_QUERY_THROW ); pEffect.reset( new CustomAnimationEffect( xEffectNode ) ); pEffect->setEffectSequence( this ); pEffect->setTarget( rTarget ); pEffect->setTargetSubItem( nSubItem ); pEffect->setNodeType( ::com::sun::star::presentation::EffectNodeType::ON_CLICK ); pEffect->setPresetClass( ::com::sun::star::presentation::EffectPresetClass::MOTIONPATH ); pEffect->setAcceleration( 0.5 ); pEffect->setDecelerate( 0.5 ); pEffect->setFill( AnimationFill::HOLD ); pEffect->setBegin( 0.0 ); pEffect->updatePathFromSdrPathObj( rPathObj ); if( fDuration != -1.0 ) pEffect->setDuration( fDuration ); maEffects.push_back(pEffect); rebuild(); } catch( Exception& ) { DBG_ERROR( "sd::EffectSequenceHelper::append(), exception caught!" ); } return pEffect; } // -------------------------------------------------------------------- void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ ) { if( pEffect.get() && pPreset.get() ) try { Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) ); if( xNewNode.is() ) { pEffect->replaceNode( xNewNode ); if( fDuration != -1.0 ) pEffect->setDuration( fDuration ); } rebuild(); } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::EffectSequenceHelper::replace(), exception caught!" ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ ) { OUString strEmpty; replace( pEffect, pPreset, strEmpty, fDuration ); } // -------------------------------------------------------------------- void EffectSequenceHelper::remove( const CustomAnimationEffectPtr& pEffect ) { if( pEffect.get() ) { pEffect->setEffectSequence( 0 ); maEffects.remove( pEffect ); } rebuild(); } // -------------------------------------------------------------------- void EffectSequenceHelper::rebuild() { implRebuild(); } // -------------------------------------------------------------------- void EffectSequenceHelper::implRebuild() { try { // first we delete all time containers on the first two levels Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW ); Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW ); Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xChildEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW ); xChildContainer->removeChild( xNode ); } mxSequenceRoot->removeChild( xChildNode ); } // second, rebuild main sequence EffectSequence::iterator aIter( maEffects.begin() ); EffectSequence::iterator aEnd( maEffects.end() ); if( aIter != aEnd ) { AfterEffectNodeList aAfterEffects; CustomAnimationEffectPtr pEffect = (*aIter++); bool bFirst = true; do { // create a par container for the next click node and all following with and after effects Reference< XTimeContainer > xOnClickContainer( createParallelTimeContainer() ); Event aEvent; if( mxEventSource.is() ) { aEvent.Source <<= mxEventSource; aEvent.Trigger = EventTrigger::ON_CLICK; } else { aEvent.Trigger = EventTrigger::ON_NEXT; } aEvent.Repeat = 0; Any aBegin( makeAny( aEvent ) ); if( bFirst ) { // if the first node is not a click action, this click container // must not have INDEFINITE begin but start at 0s bFirst = false; if( pEffect->getNodeType() != EffectNodeType::ON_CLICK ) aBegin <<= (double)0.0; } xOnClickContainer->setBegin( aBegin ); Reference< XAnimationNode > xOnClickContainerNode( xOnClickContainer, UNO_QUERY_THROW ); mxSequenceRoot->appendChild( xOnClickContainerNode ); double fBegin = 0.0; do { // create a par container for the current click or after effect node and all following with effects Reference< XTimeContainer > xWithContainer( createParallelTimeContainer() ); Reference< XAnimationNode > xWithContainerNode( xWithContainer, UNO_QUERY_THROW ); xWithContainer->setBegin( makeAny( fBegin ) ); xOnClickContainer->appendChild( xWithContainerNode ); double fDuration = 0.0; do { Reference< XAnimationNode > xEffectNode( pEffect->getNode() ); xWithContainer->appendChild( xEffectNode ); if( pEffect->hasAfterEffect() ) { Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() ); AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() ); aAfterEffects.push_back( a ); } double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration(); if( fTemp > fDuration ) fDuration = fTemp; if( aIter != aEnd ) pEffect = (*aIter++); else pEffect.reset(); } while( pEffect.get() && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) ); fBegin += fDuration; } while( pEffect.get() && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) ); } while( pEffect.get() ); // process after effect nodes std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func ); updateTextGroups(); // reset duration, might have been altered (see below) mxSequenceRoot->setDuration( Any() ); } else { // empty sequence, set duration to 0.0 explicitely // (otherwise, this sequence will never end) mxSequenceRoot->setDuration( makeAny((double)0.0) ); } } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::EffectSequenceHelper::rebuild(), exception caught!" ); } } // -------------------------------------------------------------------- Reference< XTimeContainer > EffectSequenceHelper::createParallelTimeContainer() const { const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.animations.ParallelTimeContainer" ) ); return Reference< XTimeContainer >( ::comphelper::getProcessServiceFactory()->createInstance(aServiceName), UNO_QUERY ); } // -------------------------------------------------------------------- stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xSearchNode ) : mxSearchNode( xSearchNode ) { } // -------------------------------------------------------------------- bool stl_CustomAnimationEffect_search_node_predict::operator()( CustomAnimationEffectPtr pEffect ) const { return pEffect->getNode() == mxSearchNode; } // -------------------------------------------------------------------- static bool implFindNextContainer( Reference< XTimeContainer >& xParent, Reference< XTimeContainer >& xCurrent, Reference< XTimeContainer >& xNext ) throw(Exception) { Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() ); if( xEnumeration.is() ) { Reference< XInterface > x; while( xEnumeration->hasMoreElements() && !xNext.is() ) { if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) ) { if( xEnumeration->hasMoreElements() ) xEnumeration->nextElement() >>= xNext; } } } return xNext.is(); } // -------------------------------------------------------------------- void stl_process_after_effect_node_func(AfterEffectNode& rNode) { try { if( rNode.mxNode.is() && rNode.mxMaster.is() ) { // set master node Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_QUERY_THROW ); Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() ); sal_Int32 nSize = aUserData.getLength(); aUserData.realloc(nSize+1); aUserData[nSize].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "master-element" ) ); aUserData[nSize].Value <<= xMasterNode; rNode.mxNode->setUserData( aUserData ); // insert after effect node into timeline Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW ); if( !rNode.mbOnNextEffect ) // sameClick { // insert the aftereffect after its effect is animated xContainer->insertAfter( rNode.mxNode, rNode.mxMaster ); } else // nextClick { Reference< XMultiServiceFactory > xMsf( ::comphelper::getProcessServiceFactory() ); // insert the aftereffect in the next group Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW ); Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW ); Reference< XTimeContainer > xNextContainer; // first try if we have an after effect container if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) ) { Reference< XTimeContainer > xNextClickContainer; // if not, try to find the next click effect container if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) ) { Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); if( xEnumeration->hasMoreElements() ) { // the next container is the first child container xEnumeration->nextElement() >>= xNextContainer; } else { // this does not yet have a child container, create one const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.ParallelTimeContainer") ); xNextContainer = Reference< XTimeContainer >::query( xMsf->createInstance(aServiceName) ); if( xNextContainer.is() ) { Reference< XAnimationNode > xNode( xNextContainer, UNO_QUERY_THROW ); xNode->setBegin( makeAny( (double)0.0 ) ); // xNode->setFill( AnimationFill::HOLD ); xNextClickContainer->appendChild( xNode ); } } DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" ); } } // if we don't have a next container, we add one to the sequence container if( !xNextContainer.is() ) { const OUString aServiceName( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.ParallelTimeContainer") ); Reference< XTimeContainer > xNewClickContainer( xMsf->createInstance(aServiceName), UNO_QUERY_THROW ); Reference< XAnimationNode > xNewClickNode( xNewClickContainer, UNO_QUERY_THROW ); Event aEvent; aEvent.Trigger = EventTrigger::ON_NEXT; aEvent.Repeat = 0; xNewClickNode->setBegin( makeAny( aEvent ) ); Reference< XAnimationNode > xRefNode( xClickContainer, UNO_QUERY_THROW ); xSequenceContainer->insertAfter( xNewClickNode, xRefNode ); xNextContainer = Reference< XTimeContainer >::query( xMsf->createInstance(aServiceName) ); DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not create container!" ); if( xNextContainer.is() ) { Reference< XAnimationNode > xNode( xNextContainer, UNO_QUERY_THROW ); xNode->setBegin( makeAny( (double)0.0 ) ); // xNode->setFill( AnimationFill::HOLD ); xNewClickContainer->appendChild( xNode ); } } if( xNextContainer.is() ) { // find begin time of first element Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); if( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChild; // the next container is the first child container xEnumeration->nextElement() >>= xChild; if( xChild.is() ) { Any aBegin( xChild->getBegin() ); double fBegin = 0.0; if( (aBegin >>= fBegin) && (fBegin >= 0.0)) rNode.mxNode->setBegin( aBegin ); } } xNextContainer->appendChild( rNode.mxNode ); } } } } catch( Exception& e ) { (void)e; DBG_ERROR( "ppt::stl_process_after_effect_node_func::operator(), exception caught!" ); } } // -------------------------------------------------------------------- EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect ) { return std::find( maEffects.begin(), maEffects.end(), pEffect ); } // -------------------------------------------------------------------- CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) const { CustomAnimationEffectPtr pEffect; EffectSequence::const_iterator aIter( maEffects.begin() ); for( ; aIter != maEffects.end(); aIter++ ) { if( (*aIter)->getNode() == xNode ) { pEffect = (*aIter); break; } } return pEffect; } // -------------------------------------------------------------------- sal_Int32 EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr& xEffect ) const { sal_Int32 nOffset = 0; EffectSequence::const_iterator aIter( maEffects.begin() ); for( ; aIter != maEffects.end(); aIter++, nOffset++ ) { if( (*aIter) == xEffect ) return nOffset; } return -1; } // -------------------------------------------------------------------- CustomAnimationEffectPtr EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset ) const { EffectSequence::const_iterator aIter( maEffects.begin() ); while( nOffset-- && aIter != maEffects.end() ) aIter++; CustomAnimationEffectPtr pEffect; if( aIter != maEffects.end() ) pEffect = (*aIter); return pEffect; } // -------------------------------------------------------------------- bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape ) { bool bChanges = false; EffectSequence::iterator aIter( maEffects.begin() ); while( aIter != maEffects.end() ) { if( (*aIter)->getTargetShape() == xShape ) { (*aIter)->setEffectSequence( 0 ); bChanges = true; aIter = maEffects.erase( aIter ); } else { aIter++; } } return bChanges; } // -------------------------------------------------------------------- bool EffectSequenceHelper::hasEffect( const com::sun::star::uno::Reference< com::sun::star::drawing::XShape >& xShape ) { EffectSequence::iterator aIter( maEffects.begin() ); while( aIter != maEffects.end() ) { if( (*aIter)->getTargetShape() == xShape ) return true; aIter++; } return false; } // -------------------------------------------------------------------- void EffectSequenceHelper::insertTextRange( const com::sun::star::uno::Any& aTarget ) { bool bChanges = false; ParagraphTarget aParaTarget; if( !(aTarget >>= aParaTarget ) ) return; EffectSequence::iterator aIter( maEffects.begin() ); while( aIter != maEffects.end() ) { if( (*aIter)->getTargetShape() == aParaTarget.Shape ) bChanges |= (*aIter)->checkForText(); aIter++; } if( bChanges ) rebuild(); } // -------------------------------------------------------------------- void EffectSequenceHelper::disposeTextRange( const com::sun::star::uno::Any& aTarget ) { ParagraphTarget aParaTarget; if( !(aTarget >>= aParaTarget ) ) return; bool bChanges = false; bool bErased = false; EffectSequence::iterator aIter( maEffects.begin() ); while( aIter != maEffects.end() ) { Any aIterTarget( (*aIter)->getTarget() ); if( aIterTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { ParagraphTarget aIterParaTarget; if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) ) { if( aIterParaTarget.Paragraph == aParaTarget.Paragraph ) { // delete this effect if it targets the disposed paragraph directly (*aIter)->setEffectSequence( 0 ); aIter = maEffects.erase( aIter ); bChanges = true; bErased = true; } else { if( aIterParaTarget.Paragraph > aParaTarget.Paragraph ) { // shift all paragraphs after disposed paragraph aIterParaTarget.Paragraph--; (*aIter)->setTarget( makeAny( aIterParaTarget ) ); } } } } else if( (*aIter)->getTargetShape() == aParaTarget.Shape ) { bChanges |= (*aIter)->checkForText(); } if( bErased ) bErased = false; else aIter++; } if( bChanges ) rebuild(); } // -------------------------------------------------------------------- CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId ) : maTarget( rTarget ), mnGroupId( nGroupId ) { reset(); } // -------------------------------------------------------------------- void CustomAnimationTextGroup::reset() { mnTextGrouping = -1; mbAnimateForm = false; mbTextReverse = false; mfGroupingAuto = -1.0; mnLastPara = -1; // used to check for TextReverse int i = 5; while( i-- ) mnDepthFlags[i] = 0; maEffects.clear(); } // -------------------------------------------------------------------- void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr& pEffect ) { maEffects.push_back( pEffect ); Any aTarget( pEffect->getTarget() ); if( aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { // now look at the paragraph ParagraphTarget aParaTarget; aTarget >>= aParaTarget; if( mnLastPara != -1 ) mbTextReverse = mnLastPara > aParaTarget.Paragraph; mnLastPara = aParaTarget.Paragraph; const sal_Int32 nParaDepth = pEffect->getParaDepth(); // only look at the first 5 levels if( nParaDepth < 5 ) { // our first paragraph with this level? if( mnDepthFlags[nParaDepth] == 0 ) { // so set it to the first found mnDepthFlags[nParaDepth] = (sal_Int8)pEffect->getNodeType(); } else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() ) { mnDepthFlags[nParaDepth] = -1; } if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS ) mfGroupingAuto = pEffect->getBegin(); mnTextGrouping = 0; while( (mnTextGrouping < 5) && (mnDepthFlags[mnTextGrouping] > 0) ) mnTextGrouping++; } } else { // if we have an effect with the shape as a target, we animate the background mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT; } } // -------------------------------------------------------------------- class TextGroupMapImpl : public std::map< sal_Int32, CustomAnimationTextGroup* > { public: CustomAnimationTextGroup* findGroup( sal_Int32 nGroupId ); }; // -------------------------------------------------------------------- CustomAnimationTextGroupPtr EffectSequenceHelper::findGroup( sal_Int32 nGroupId ) { CustomAnimationTextGroupPtr aPtr; CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) ); if( aIter != maGroupMap.end() ) aPtr = (*aIter).second; return aPtr; } // -------------------------------------------------------------------- void EffectSequenceHelper::updateTextGroups() { maGroupMap.clear(); // first create all the groups EffectSequence::iterator aIter( maEffects.begin() ); const EffectSequence::iterator aEnd( maEffects.end() ); while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); const sal_Int32 nGroupId = pEffect->getGroupId(); if( nGroupId == -1 ) continue; // trivial case, no group CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId ); if( !pGroup.get() ) { pGroup.reset( new CustomAnimationTextGroup( pEffect->getTargetShape(), nGroupId ) ); maGroupMap[nGroupId] = pGroup; } pGroup->addEffect( pEffect ); } } // -------------------------------------------------------------------- CustomAnimationTextGroupPtr EffectSequenceHelper::createTextGroup( CustomAnimationEffectPtr pEffect, sal_Int32 nTextGrouping, double fTextGroupingAuto, sal_Bool bAnimateForm, sal_Bool bTextReverse ) { // first finde a free group-id sal_Int32 nGroupId = 0; CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() ); const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() ); while( aIter != aEnd ) { if( (*aIter).first == nGroupId ) { nGroupId++; aIter = maGroupMap.begin(); } else { aIter++; } } Reference< XShape > xTarget( pEffect->getTargetShape() ); CustomAnimationTextGroupPtr pTextGroup( new CustomAnimationTextGroup( xTarget, nGroupId ) ); maGroupMap[nGroupId] = pTextGroup; bool bUsed = false; // do we need to target the shape? if( (nTextGrouping == 0) || bAnimateForm ) { sal_Int16 nSubItem; if( nTextGrouping == 0) nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT; else nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND; pEffect->setTarget( makeAny( xTarget ) ); pEffect->setTargetSubItem( nSubItem ); pEffect->setEffectSequence( this ); pEffect->setGroupId( nGroupId ); pTextGroup->addEffect( pEffect ); bUsed = true; } pTextGroup->mnTextGrouping = nTextGrouping; pTextGroup->mfGroupingAuto = fTextGroupingAuto; pTextGroup->mbTextReverse = bTextReverse; // now add an effect for each paragraph createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed ); notify_listeners(); return pTextGroup; } // -------------------------------------------------------------------- void EffectSequenceHelper::createTextGroupParagraphEffects( CustomAnimationTextGroupPtr pTextGroup, CustomAnimationEffectPtr pEffect, bool bUsed ) { Reference< XShape > xTarget( pTextGroup->maTarget ); sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; double fTextGroupingAuto = pTextGroup->mfGroupingAuto; sal_Bool bTextReverse = pTextGroup->mbTextReverse; // now add an effect for each paragraph if( nTextGrouping >= 0 ) try { EffectSequence::iterator aInsertIter( find( pEffect ) ); const OUString strNumberingLevel( RTL_CONSTASCII_USTRINGPARAM("NumberingLevel") ); Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_QUERY_THROW ); std::list< sal_Int16 > aParaList; sal_Int16 nPara; // fill the list with all valid paragraphs for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ ) { Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY ); if( xRange.is() && xRange->getString().getLength() ) { if( bTextReverse ) // sort them aParaList.push_front( nPara ); else aParaList.push_back( nPara ); } } ParagraphTarget aTarget; aTarget.Shape = xTarget; std::list< sal_Int16 >::iterator aIter( aParaList.begin() ); std::list< sal_Int16 >::iterator aEnd( aParaList.end() ); while( aIter != aEnd ) { aTarget.Paragraph = (*aIter++); CustomAnimationEffectPtr pNewEffect; if( bUsed ) { // clone a new effect from first effect pNewEffect = pEffect->clone(); ++aInsertIter; aInsertIter = maEffects.insert( aInsertIter, pNewEffect ); } else { // reuse first effect if its not yet used pNewEffect = pEffect; bUsed = true; aInsertIter = find( pNewEffect ); } // set target and group-id pNewEffect->setTarget( makeAny( aTarget ) ); pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); pNewEffect->setGroupId( pTextGroup->mnGroupId ); pNewEffect->setEffectSequence( this ); // set correct node type if( pNewEffect->getParaDepth() < nTextGrouping ) { if( fTextGroupingAuto == -1.0 ) { pNewEffect->setNodeType( EffectNodeType::ON_CLICK ); pNewEffect->setBegin( 0.0 ); } else { pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); pNewEffect->setBegin( fTextGroupingAuto ); } } else { pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); pNewEffect->setBegin( 0.0 ); } pTextGroup->addEffect( pNewEffect ); } notify_listeners(); } catch( Exception& e ) { (void)e; DBG_ERROR("sd::EffectSequenceHelper::createTextGroup(), exception caught!" ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::setTextGrouping( CustomAnimationTextGroupPtr pTextGroup, sal_Int32 nTextGrouping ) { if( pTextGroup->mnTextGrouping == nTextGrouping ) { // first case, trivial case, do nothing } else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) ) { // second case, we need to add new effects for each paragraph CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() ); pTextGroup->mnTextGrouping = nTextGrouping; createTextGroupParagraphEffects( pTextGroup, pEffect, true ); notify_listeners(); } else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) ) { // third case, we need to remove effects for each paragraph EffectSequence aEffects( pTextGroup->maEffects ); pTextGroup->reset(); EffectSequence::iterator aIter( aEffects.begin() ); const EffectSequence::iterator aEnd( aEffects.end() ); while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) ) remove( pEffect ); else pTextGroup->addEffect( pEffect ); } notify_listeners(); } else { // fourth case, we need to change the node types for the text nodes double fTextGroupingAuto = pTextGroup->mfGroupingAuto; EffectSequence aEffects( pTextGroup->maEffects ); pTextGroup->reset(); EffectSequence::iterator aIter( aEffects.begin() ); const EffectSequence::iterator aEnd( aEffects.end() ); while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { // set correct node type if( pEffect->getParaDepth() < nTextGrouping ) { if( fTextGroupingAuto == -1.0 ) { pEffect->setNodeType( EffectNodeType::ON_CLICK ); pEffect->setBegin( 0.0 ); } else { pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); pEffect->setBegin( fTextGroupingAuto ); } } else { pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); pEffect->setBegin( 0.0 ); } } pTextGroup->addEffect( pEffect ); } notify_listeners(); } } // -------------------------------------------------------------------- void EffectSequenceHelper::setAnimateForm( CustomAnimationTextGroupPtr pTextGroup, sal_Bool bAnimateForm ) { if( pTextGroup->mbAnimateForm == bAnimateForm ) { // trivial case, do nothing } else { EffectSequence aEffects( pTextGroup->maEffects ); pTextGroup->reset(); EffectSequence::iterator aIter( aEffects.begin() ); const EffectSequence::iterator aEnd( aEffects.end() ); // first insert if we have to if( bAnimateForm ) { EffectSequence::iterator aInsertIter( find( (*aIter) ) ); CustomAnimationEffectPtr pEffect; if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::getCppuType((const ParagraphTarget*)0) ) ) { // special case, only one effect and that targets whole text, // convert this to target whole shape pEffect = (*aIter++); pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE ); } else { pEffect = (*aIter)->clone(); pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) ); pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND ); maEffects.insert( aInsertIter, pEffect ); } pTextGroup->addEffect( pEffect ); } if( !bAnimateForm && (aEffects.size() == 1) ) { CustomAnimationEffectPtr pEffect( (*aIter) ); pEffect->setTarget( makeAny( (*aIter)->getTargetShape() ) ); pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); pTextGroup->addEffect( pEffect ); } else { // readd the rest to the group again while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { pTextGroup->addEffect( pEffect ); } else { DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" ); remove( pEffect ); } } } notify_listeners(); } } // -------------------------------------------------------------------- void EffectSequenceHelper::setTextGroupingAuto( CustomAnimationTextGroupPtr pTextGroup, double fTextGroupingAuto ) { sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; EffectSequence aEffects( pTextGroup->maEffects ); pTextGroup->reset(); EffectSequence::iterator aIter( aEffects.begin() ); const EffectSequence::iterator aEnd( aEffects.end() ); while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); if( pEffect->getTarget().getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { // set correct node type if( pEffect->getParaDepth() < nTextGrouping ) { if( fTextGroupingAuto == -1.0 ) { pEffect->setNodeType( EffectNodeType::ON_CLICK ); pEffect->setBegin( 0.0 ); } else { pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); pEffect->setBegin( fTextGroupingAuto ); } } else { pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); pEffect->setBegin( 0.0 ); } } pTextGroup->addEffect( pEffect ); } notify_listeners(); } // -------------------------------------------------------------------- struct ImplStlTextGroupSortHelper { ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {}; bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ); bool mbReverse; sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 ); }; // -------------------------------------------------------------------- sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 ) { const Any aTarget(p1->getTarget()); if( aTarget.hasValue() && aTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) ) { ParagraphTarget aParaTarget; aTarget >>= aParaTarget; return aParaTarget.Paragraph; } else { return mbReverse ? 0x7fffffff : -1; } } // -------------------------------------------------------------------- bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ) { if( mbReverse ) { return getTargetParagraph( p2 ) < getTargetParagraph( p1 ); } else { return getTargetParagraph( p1 ) < getTargetParagraph( p2 ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::setTextReverse( CustomAnimationTextGroupPtr pTextGroup, sal_Bool bTextReverse ) { if( pTextGroup->mbTextReverse == bTextReverse ) { // do nothing } else { std::vector< CustomAnimationEffectPtr > aSortedVector(pTextGroup->maEffects.size()); std::copy( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end(), aSortedVector.begin() ); ImplStlTextGroupSortHelper aSortHelper( bTextReverse ); std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper ); pTextGroup->reset(); std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() ); const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() ); if( aIter != aEnd ) { pTextGroup->addEffect( (*aIter ) ); EffectSequence::iterator aInsertIter( find( (*aIter++) ) ); while( aIter != aEnd ) { CustomAnimationEffectPtr pEffect( (*aIter++) ); maEffects.erase( find( pEffect ) ); aInsertIter = maEffects.insert( ++aInsertIter, pEffect ); pTextGroup->addEffect( pEffect ); } } notify_listeners(); } } // -------------------------------------------------------------------- void EffectSequenceHelper::addListener( ISequenceListener* pListener ) { if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() ) maListeners.push_back( pListener ); } // -------------------------------------------------------------------- void EffectSequenceHelper::removeListener( ISequenceListener* pListener ) { maListeners.remove( pListener ); } // -------------------------------------------------------------------- struct stl_notify_listeners_func : public std::unary_function { stl_notify_listeners_func() {} void operator()(ISequenceListener* pListener) { pListener->notify_change(); } }; // -------------------------------------------------------------------- void EffectSequenceHelper::notify_listeners() { stl_notify_listeners_func aFunc; std::for_each( maListeners.begin(), maListeners.end(), aFunc ); } // -------------------------------------------------------------------- void EffectSequenceHelper::create( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) { DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" ); if( xNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); createEffectsequence( xChildNode ); } } catch( Exception& ) { DBG_ERROR( "sd::EffectSequenceHelper::create(), exception caught!" ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode ) { DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" ); if( xNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); createEffects( xChildNode ); } } catch( Exception& ) { DBG_ERROR( "sd::EffectSequenceHelper::createEffectsequence(), exception caught!" ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode ) { DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" ); if( xNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); switch( xChildNode->getType() ) { // found an effect case AnimationNodeType::PAR: case AnimationNodeType::ITERATE: { CustomAnimationEffectPtr pEffect( new CustomAnimationEffect( xChildNode ) ); if( pEffect->mnNodeType != -1 ) { pEffect->setEffectSequence( this ); maEffects.push_back(pEffect); } } break; // found an after effect case AnimationNodeType::SET: case AnimationNodeType::ANIMATECOLOR: { processAfterEffect( xChildNode ); } break; } } } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::EffectSequenceHelper::createEffects(), exception caught!" ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode ) { try { Reference< XAnimationNode > xMaster; Sequence< NamedValue > aUserData( xNode->getUserData() ); sal_Int32 nLength = aUserData.getLength(); const NamedValue* p = aUserData.getConstArray(); while( nLength-- ) { if( p->Name.equalsAscii( "master-element" ) ) { p->Value >>= xMaster; break; } p++; } // only process if this is a valid after effect if( xMaster.is() ) { CustomAnimationEffectPtr pMasterEffect; // find the master effect stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster ); EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) ); if( aIter != maEffects.end() ) pMasterEffect = (*aIter ); if( pMasterEffect.get() ) { pMasterEffect->setHasAfterEffect( true ); // find out what kind of after effect this is if( xNode->getType() == AnimationNodeType::ANIMATECOLOR ) { // its a dim Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW ); pMasterEffect->setDimColor( xAnimate->getTo() ); pMasterEffect->setAfterEffectOnNext( true ); } else { // its a hide Reference< XChild > xNodeChild( xNode, UNO_QUERY_THROW ); Reference< XChild > xMasterChild( xMaster, UNO_QUERY_THROW ); pMasterEffect->setAfterEffectOnNext( xNodeChild->getParent() != xMasterChild->getParent() ); } } } } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::EffectSequenceHelper::processAfterEffect(), exception caught!" ); } } /* double EffectSequenceHelper::calculateIterateNodeDuration( { Reference< i18n::XBreakIterator > xBI( ImplGetBreakIterator() ); sal_Int32 nDone; sal_Int32 nNextCellBreak( xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone) ); i18n::Boundary nNextWordBoundary( xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True) ); sal_Int32 nNextSentenceBreak( xBI->endOfSentence(rTxt, nIdx, rLocale) ); const sal_Int32 nEndPos( nIdx + nLen ); sal_Int32 i, currOffset(0); for( i=nIdx; inextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); } if( i == nNextWordBoundary.endPos ) { rMtf.AddAction( new MetaCommentAction( "XTEXT_EOW" ) ); nNextWordBoundary = xBI->getWordBoundary(rTxt, i+1, rLocale, i18n::WordType::ANY_WORD, sal_True); } if( i == nNextSentenceBreak ) { rMtf.AddAction( new MetaCommentAction( "XTEXT_EOS" ) ); nNextSentenceBreak = xBI->endOfSentence(rTxt, i+1, rLocale); } } } */ // ==================================================================== class AnimationChangeListener : public cppu::WeakImplHelper1< XChangesListener > { public: AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {} virtual void SAL_CALL changesOccurred( const ::com::sun::star::util::ChangesEvent& Event ) throw (RuntimeException); virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (RuntimeException); private: MainSequence* mpMainSequence; }; void SAL_CALL AnimationChangeListener::changesOccurred( const ::com::sun::star::util::ChangesEvent& ) throw (RuntimeException) { if( mpMainSequence ) mpMainSequence->startRecreateTimer(); } void SAL_CALL AnimationChangeListener::disposing( const ::com::sun::star::lang::EventObject& ) throw (RuntimeException) { } // ==================================================================== MainSequence::MainSequence() : mxTimingRootNode( ::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer"))), UNO_QUERY ) , mbRebuilding( false ) , mnRebuildLockGuard( 0 ) , mbPendingRebuildRequest( false ) { if( mxTimingRootNode.is() ) { Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 ); aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ); aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::MAIN_SEQUENCE; mxTimingRootNode->setUserData( aUserData ); } init(); } // -------------------------------------------------------------------- MainSequence::MainSequence( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) : mxTimingRootNode( xNode, UNO_QUERY ) , mbRebuilding( false ) , mnRebuildLockGuard( 0 ) , mbPendingRebuildRequest( false ) , mbIgnoreChanges( 0 ) { init(); } // -------------------------------------------------------------------- MainSequence::~MainSequence() { reset(); } // -------------------------------------------------------------------- void MainSequence::init() { mnSequenceType = EffectNodeType::MAIN_SEQUENCE; maTimer.SetTimeoutHdl( LINK(this, MainSequence, onTimerHdl) ); maTimer.SetTimeout(500); mxChangesListener.set( new AnimationChangeListener( this ) ); createMainSequence(); } // -------------------------------------------------------------------- void MainSequence::reset( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xTimingRootNode ) { reset(); mxTimingRootNode.set( xTimingRootNode, UNO_QUERY ); createMainSequence(); } // -------------------------------------------------------------------- Reference< ::com::sun::star::animations::XAnimationNode > MainSequence::getRootNode() { DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, ist this really what you want?" ); if( maTimer.IsActive() && mbTimerMode ) { // force a rebuild NOW if one is pending maTimer.Stop(); implRebuild(); } return EffectSequenceHelper::getRootNode(); } // -------------------------------------------------------------------- void MainSequence::createMainSequence() { if( mxTimingRootNode.is() ) try { Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode ); if( nNodeType == EffectNodeType::MAIN_SEQUENCE ) { mxSequenceRoot.set( xChildNode, UNO_QUERY ); EffectSequenceHelper::create( xChildNode ); } else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE ) { Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW ); InteractiveSequencePtr pIS( new InteractiveSequence( xInteractiveRoot, this ) ); pIS->addListener( this ); maInteractiveSequenceList.push_back( pIS ); } } // see if we have a mainsequence at all. if not, create one... if( !mxSequenceRoot.is() ) { mxSequenceRoot = Reference< XTimeContainer >::query(::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer")))); if( mxSequenceRoot.is() ) { uno::Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 ); aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ); aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::MAIN_SEQUENCE; mxSequenceRoot->setUserData( aUserData ); // empty sequence until now, set duration to 0.0 // explicitely (otherwise, this sequence will never // end) mxSequenceRoot->setDuration( makeAny((double)0.0) ); Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW ); mxTimingRootNode->appendChild( xMainSequenceNode ); } } updateTextGroups(); notify_listeners(); Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); if( xNotifier.is() ) xNotifier->addChangesListener( mxChangesListener ); } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::MainSequence::create(), exception caught!" ); return; } DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" ); } // -------------------------------------------------------------------- void MainSequence::reset() { EffectSequenceHelper::reset(); InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ ) (*aIter)->reset(); maInteractiveSequenceList.clear(); try { Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); if( xNotifier.is() ) xNotifier->removeChangesListener( mxChangesListener ); } catch( Exception& ) { // ... } } // -------------------------------------------------------------------- InteractiveSequencePtr MainSequence::createInteractiveSequence( const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& xShape ) { InteractiveSequencePtr pIS; // create a new interactive sequence container Reference< XTimeContainer > xISRoot( ::comphelper::getProcessServiceFactory()->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.animations.SequenceTimeContainer"))), UNO_QUERY ); DBG_ASSERT( xISRoot.is(), "sd::MainSequence::createInteractiveSequence(), could not create \"com.sun.star.animations.SequenceTimeContainer\"!"); if( xISRoot.is() ) { uno::Sequence< ::com::sun::star::beans::NamedValue > aUserData( 1 ); aUserData[0].Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "node-type" ) ); aUserData[0].Value <<= ::com::sun::star::presentation::EffectNodeType::INTERACTIVE_SEQUENCE ; xISRoot->setUserData( aUserData ); Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); Reference< XAnimationNode > xISNode( xISRoot, UNO_QUERY_THROW ); Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); xParent->appendChild( xISNode ); } pIS.reset( new InteractiveSequence( xISRoot, this) ); pIS->setTriggerShape( xShape ); pIS->addListener( this ); maInteractiveSequenceList.push_back( pIS ); return pIS; } // -------------------------------------------------------------------- CustomAnimationEffectPtr MainSequence::findEffect( const ::com::sun::star::uno::Reference< ::com::sun::star::animations::XAnimationNode >& xNode ) const { CustomAnimationEffectPtr pEffect = EffectSequenceHelper::findEffect( xNode ); if( pEffect.get() == 0 ) { InteractiveSequenceList::const_iterator aIter; for( aIter = maInteractiveSequenceList.begin(); (aIter != maInteractiveSequenceList.end()) && (pEffect.get() == 0); aIter++ ) { pEffect = (*aIter)->findEffect( xNode ); } } return pEffect; } // -------------------------------------------------------------------- sal_Int32 MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr& pEffect ) const { sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect ); if( nOffset != -1 ) return nOffset; nOffset = EffectSequenceHelper::getCount(); InteractiveSequenceList::const_iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ ) { sal_Int32 nTemp = (*aIter)->getOffsetFromEffect( pEffect ); if( nTemp != -1 ) return nOffset + nTemp; nOffset += (*aIter)->getCount(); } return -1; } // -------------------------------------------------------------------- CustomAnimationEffectPtr MainSequence::getEffectFromOffset( sal_Int32 nOffset ) const { if( nOffset >= 0 ) { if( nOffset < getCount() ) return EffectSequenceHelper::getEffectFromOffset( nOffset ); nOffset -= getCount(); InteractiveSequenceList::const_iterator aIter( maInteractiveSequenceList.begin() ); while( (aIter != maInteractiveSequenceList.end()) && (nOffset > (*aIter)->getCount()) ) nOffset -= (*aIter++)->getCount(); if( (aIter != maInteractiveSequenceList.end()) && (nOffset >= 0) ) return (*aIter)->getEffectFromOffset( nOffset ); } CustomAnimationEffectPtr pEffect; return pEffect; } // -------------------------------------------------------------------- bool MainSequence::disposeShape( const Reference< XShape >& xShape ) { bool bChanges = EffectSequenceHelper::disposeShape( xShape ); InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); ) { bChanges |= (*aIter++)->disposeShape( xShape ); } if( bChanges ) startRebuildTimer(); return bChanges; } // -------------------------------------------------------------------- bool MainSequence::hasEffect( const com::sun::star::uno::Reference< com::sun::star::drawing::XShape >& xShape ) { if( EffectSequenceHelper::hasEffect( xShape ) ) return true; InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); ) { if( (*aIter)->getTriggerShape() == xShape ) return true; if( (*aIter++)->hasEffect( xShape ) ) return true; } return false; } // -------------------------------------------------------------------- void MainSequence::insertTextRange( const com::sun::star::uno::Any& aTarget ) { EffectSequenceHelper::insertTextRange( aTarget ); InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ ) { (*aIter)->insertTextRange( aTarget ); } } // -------------------------------------------------------------------- void MainSequence::disposeTextRange( const com::sun::star::uno::Any& aTarget ) { EffectSequenceHelper::disposeTextRange( aTarget ); InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ ) { (*aIter)->disposeTextRange( aTarget ); } } // -------------------------------------------------------------------- /** callback from the sd::View when an object just left text edit mode */ void MainSequence::onTextChanged( const Reference< XShape >& xShape ) { EffectSequenceHelper::onTextChanged( xShape ); InteractiveSequenceList::iterator aIter; for( aIter = maInteractiveSequenceList.begin(); aIter != maInteractiveSequenceList.end(); aIter++ ) { (*aIter)->onTextChanged( xShape ); } } // -------------------------------------------------------------------- void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape ) { bool bChanges = false; EffectSequence::iterator aIter; for( aIter = maEffects.begin(); aIter != maEffects.end(); aIter++ ) { if( (*aIter)->getTargetShape() == xShape ) bChanges |= (*aIter)->checkForText(); } if( bChanges ) EffectSequenceHelper::implRebuild(); } // -------------------------------------------------------------------- void MainSequence::rebuild() { startRebuildTimer(); } // -------------------------------------------------------------------- void MainSequence::lockRebuilds() { mnRebuildLockGuard++; } // -------------------------------------------------------------------- void MainSequence::unlockRebuilds() { DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" ); if( mnRebuildLockGuard ) mnRebuildLockGuard--; if( (mnRebuildLockGuard == 0) && mbPendingRebuildRequest ) { mbPendingRebuildRequest = false; startRebuildTimer(); } } // -------------------------------------------------------------------- void MainSequence::implRebuild() { if( mnRebuildLockGuard ) { mbPendingRebuildRequest = true; return; } mbRebuilding = true; EffectSequenceHelper::implRebuild(); InteractiveSequenceList::iterator aIter( maInteractiveSequenceList.begin() ); const InteractiveSequenceList::iterator aEnd( maInteractiveSequenceList.end() ); while( aIter != aEnd ) { InteractiveSequencePtr pIS( (*aIter) ); if( pIS->maEffects.empty() ) { // remove empty interactive sequences aIter = maInteractiveSequenceList.erase( aIter ); Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW ); xParent->removeChild( xISNode ); } else { pIS->implRebuild(); aIter++; } } notify_listeners(); mbRebuilding = false; } // -------------------------------------------------------------------- void MainSequence::notify_change() { notify_listeners(); } // -------------------------------------------------------------------- bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& xTriggerShape ) { EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence(); EffectSequenceHelper* pNewSequence = 0; if( xTriggerShape.is() ) { InteractiveSequenceList::iterator aIter( maInteractiveSequenceList.begin() ); const InteractiveSequenceList::iterator aEnd( maInteractiveSequenceList.end() ); while( aIter != aEnd ) { InteractiveSequencePtr pIS( (*aIter++) ); if( pIS->getTriggerShape() == xTriggerShape ) { pNewSequence = pIS.get(); break; } } if( !pNewSequence ) pNewSequence = createInteractiveSequence( xTriggerShape ).get(); } else { pNewSequence = this; } if( pOldSequence != pNewSequence ) { if( pOldSequence ) pOldSequence->maEffects.remove( pEffect ); if( pNewSequence ) pNewSequence->maEffects.push_back( pEffect ); pEffect->setEffectSequence( pNewSequence ); return true; } else { return false; } } // -------------------------------------------------------------------- IMPL_LINK( MainSequence, onTimerHdl, Timer *, EMPTYARG ) { if( mbTimerMode ) { implRebuild(); } else { reset(); createMainSequence(); } return 0; } // -------------------------------------------------------------------- /** starts a timer that recreates the internal structure from the API core after 1 second */ void MainSequence::startRecreateTimer() { if( !mbRebuilding && (mbIgnoreChanges == 0) ) { mbTimerMode = false; maTimer.Start(); } } // -------------------------------------------------------------------- /** starts a timer that rebuilds the API core from the internal structure after 1 second */ void MainSequence::startRebuildTimer() { mbTimerMode = true; maTimer.Start(); } // ==================================================================== InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence ) : EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence ) { mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE; try { if( mxSequenceRoot.is() ) { Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_QUERY_THROW ); while( !mxEventSource.is() && xEnumeration->hasMoreElements() ) { Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); Event aEvent; if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) ) aEvent.Source >>= mxEventSource; } } } catch( Exception& e ) { (void)e; DBG_ERROR( "sd::InteractiveSequence::InteractiveSequence(), exception caught!" ); return; } } // -------------------------------------------------------------------- void InteractiveSequence::rebuild() { mpMainSequence->rebuild(); } void InteractiveSequence::implRebuild() { EffectSequenceHelper::implRebuild(); } // -------------------------------------------------------------------- MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr& pMainSequence ) : mpMainSequence( pMainSequence ) { if( mpMainSequence.get() ) mpMainSequence->lockRebuilds(); } MainSequenceRebuildGuard::~MainSequenceRebuildGuard() { if( mpMainSequence.get() ) mpMainSequence->unlockRebuilds(); } }