/************************************************************************* * * $RCSfile: animationnodefactory.cxx,v $ * * $Revision: 1.2 $ * * last change: $Author: kz $ $Date: 2005-01-21 17:02:44 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (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.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ // must be first #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _COM_SUN_STAR_DRAWING_XSHAPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XANIMATE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_ANIMATIONNODETYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_PRESENTATION_EFFECTNODETYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_PRESENTATION_TEXTANIMATIONTYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XANIMATESET_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XITERATECONTAINER_HPP_ #include #endif #ifndef _COM_SUN_STAR_PRESENTATION_SHAPEANIMATIONSUBTYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XANIMATEMOTION_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XANIMATECOLOR_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XANIMATETRANSFORM_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_ANIMATIONTRANSFORMTYPE_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XTRANSITIONFILTER_HPP_ #include #endif #ifndef _COM_SUN_STAR_ANIMATIONS_XAUDIO_HPP_ #include #endif #ifndef _COM_SUN_STAR_PRESENTATION_PARAGRAPHTARGET_HPP_ #include #endif #ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_ #include #endif #ifndef _BGFX_NUMERIC_FTOOLS_HXX #include #endif #ifndef BOOST_BIND_HPP_INCLUDED #include #endif #ifndef BOOST_MEM_FN_HPP_INCLUDED #include #endif #include #include #include using namespace ::com::sun::star; namespace presentation { namespace internal { namespace { // forward declaration needed by NodeCreator BaseNodeSharedPtr implCreateAnimationNode( const uno::Reference< animations::XAnimationNode >& xNode, const BaseContainerNodeSharedPtr& rParent, const NodeContext& rContext ); class NodeCreator { public: NodeCreator( BaseContainerNodeSharedPtr& rParent, const NodeContext& rContext ) : mrParent( rParent ), mrContext( rContext ) { } void operator()( const uno::Reference< animations::XAnimationNode >& xChildNode ) const { createChild( xChildNode, mrContext ); } protected: void createChild( const uno::Reference< animations::XAnimationNode >& xChildNode, const NodeContext& rContext ) const { BaseNodeSharedPtr pChild( implCreateAnimationNode( xChildNode, mrParent, rContext ) ); OSL_ENSURE( pChild.get(), "NodeCreator::operator(): child creation failed" ); // TODO(Q1): This yields circular references, which, it seems, is // unavoidable here if( pChild.get() ) mrParent->appendChildNode( pChild ); } BaseContainerNodeSharedPtr& mrParent; const NodeContext& mrContext; }; /** Same as NodeCreator, only that NodeContext's SubsetShape is cloned for every child node. This is used for iterated animation node generation */ class CloningNodeCreator : private NodeCreator { public: CloningNodeCreator( BaseContainerNodeSharedPtr& rParent, const NodeContext& rContext ) : NodeCreator( rParent, rContext ) { } void operator()( const uno::Reference< animations::XAnimationNode >& xChildNode ) const { NodeContext aContext( mrContext ); // TODO(Q1): There's a catch here. If you clone a // subset whose actual subsetting has already been // realized (i.e. if enableSubsetShape() has been // called already), and the original of your clone // goes out of scope, then your subset will be // gone (LayerManager::revokeSubset() be // called). As of now, this behaviour is not // triggered here (we either clone, XOR we enable // subset initially), but one might consider // reworking DrawShape/ShapeSubset to avoid this. // clone ShapeSubset, since each node needs their // own version of the ShapeSubset (otherwise, // e.g. activity counting does not work - subset // would be removed after first animation node // disables it). // // NOTE: this is only a problem for animation // nodes that explicitely call // disableSubsetShape(). Independent shape subsets // (like those created for ParagraphTargets) // solely rely on the ShapeSubset destructor to // normalize things, which does the right thing // here: the subset is only removed after _the // last_ animation node releases the shared ptr. aContext.mpMasterShapeSubset.reset( new ShapeSubset( *aContext.mpMasterShapeSubset ) ); createChild( xChildNode, aContext ); } }; bool implCreateIteratedNodes( const uno::Reference< animations::XIterateContainer >& xIterNode, BaseContainerNodeSharedPtr& rParent, const NodeContext& rContext ) { ENSURE_AND_THROW( xIterNode.is(), "implCreateIteratedNodes(): Invalid node" ); // TODO(F2): getIterateInterval() yields a procentual time shift. // Currently, we're assuming 2 seconds total animation runtime, // which might of course be totally wrong. const double nIntervalTimeout( xIterNode->getIterateInterval() / 50.0 ); // valid iterate interval? We're ruling out monstrous // values here, to avoid pseudo 'hangs' in the // presentation if( nIntervalTimeout < 0.0 || nIntervalTimeout > 1000.0 ) { return false; // not an active iteration } if( ::basegfx::fTools::equalZero( nIntervalTimeout ) ) OSL_TRACE( "implCreateIteratedNodes(): iterate interval close to zero, there's " "no point in defining such an effect (visually equivalent to whole-shape effect)" ); // Determine target shape (or subset) // ================================== // TODO(E1): I'm not too sure what to expect here... ENSURE_AND_RETURN( xIterNode->getTarget().hasValue(), "implCreateIteratedNodes(): no target on ITERATE node" ); uno::Reference< drawing::XShape > xTargetShape( xIterNode->getTarget(), uno::UNO_QUERY ); ::com::sun::star::presentation::ParagraphTarget aTarget; sal_Int16 nSubItem( xIterNode->getSubItem() ); bool bParagraphTarget( false ); if( !xTargetShape.is() ) { // no shape provided. Maybe a ParagraphTarget? if( !(xIterNode->getTarget() >>= aTarget) ) ENSURE_AND_RETURN( false, "implCreateIteratedNodes(): could not extract any " "target information" ); xTargetShape = aTarget.Shape; ENSURE_AND_RETURN( xTargetShape.is(), "implCreateIteratedNodes(): invalid shape in ParagraphTarget" ); // we've a paragraph target to iterate over, thus, // the whole animation container refers only to // the text nSubItem = ::com::sun::star::presentation::ShapeAnimationSubType::ONLY_TEXT; bParagraphTarget = true; } // Lookup shape, and fill NodeContext // ================================== AttributableShapeSharedPtr pTargetShape( lookupAttributableShape( rContext.maContext.mpLayerManager, xTargetShape ) ); ShapeSubsetSharedPtr pTargetSubset; NodeContext aContext( rContext ); // paragraph targets already need a subset as the // master shape (they're representing only a single // paragraph) if( bParagraphTarget ) { pTargetSubset.reset( new ShapeSubset( pTargetShape, DocTreeNode::createFromShape( pTargetShape, aTarget.Paragraph, DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ), rContext.maContext.mpLayerManager )); // iterate target is not the whole shape, but only // the selected paragraph - subset _must_ be // independent, to be able to affect visibility // independent of master shape aContext.mbIsIndependentSubset = true; // already enable parent subset right here, to // make potentially generated subsets subtract // their content from the parent subset (and not // the original shape). Otherwise, already // subsetted parents (e.g. paragraphs) would not // have their characters removed, when the child // iterations start. // Furthermore, the setup of initial shape // attributes of course needs the subset shape // generated, to apply e.g. visibility changes. pTargetSubset->enableSubsetShape(); } else { pTargetSubset.reset( new ShapeSubset( pTargetShape, rContext.maContext.mpLayerManager ) ); } aContext.mpMasterShapeSubset = pTargetSubset; uno::Reference< animations::XAnimationNode > xNode( xIterNode, uno::UNO_QUERY_THROW ); // Generate subsets // ================ if( bParagraphTarget || nSubItem != ::com::sun::star::presentation::ShapeAnimationSubType::ONLY_TEXT ) { // prepend with animations for // full Shape (will be subtracted // from the subset parts within // the Shape::createSubset() // method). For ONLY_TEXT effects, // we skip this part, to animate // only the text. // // OR // // prepend with subset animation for full // _paragraph_, from which the individual // paragraph subsets are subtracted. Note that the // subitem is superfluous here, we always assume // ONLY_TEXT, if a paragraph is referenced as the // master of an iteration effect. NodeCreator aCreator( rParent, aContext ); if( !::anim::for_each_childNode( xNode, aCreator ) ) { ENSURE_AND_RETURN( false, "implCreateIteratedNodes(): iterated child node creation failed" ); } } // TODO(F2): This does not do the correct // thing. Having nSubItem be set to ONLY_BACKGROUND // should result in the text staying unanimated in the // foreground, while the shape moves in the background // (this behaviour is perfectly possible with the // slideshow engine, only that the text won't be // currently visible, because animations are always in // the foreground) if( pTargetSubset->getSubsetShape()->getNumberOfTreeNodes() > 0 && nSubItem != ::com::sun::star::presentation::ShapeAnimationSubType::ONLY_BACKGROUND ) { // determine type of subitem iteration (logical // text unit to animate) DocTreeNode::NodeType eIterateNodeType( DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL ); switch( xIterNode->getIterateType() ) { case ::com::sun::star::presentation::TextAnimationType::BY_PARAGRAPH: eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH; break; case ::com::sun::star::presentation::TextAnimationType::BY_WORD: eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_WORD; break; case ::com::sun::star::presentation::TextAnimationType::BY_LETTER: eIterateNodeType = DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL; break; default: ENSURE_AND_THROW(false, "implCreateIteratedNodes(): Unexpected IterateType on XIterateContainer"); break; } if( bParagraphTarget && eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_WORD && eIterateNodeType != DocTreeNode::NODETYPE_LOGICAL_CHARACTER_CELL ) { // will not animate the whole paragraph, when // only the paragraph is animated at all. OSL_ENSURE( false, "implCreateIteratedNodes(): Ignoring paragraph iteration for paragraph master" ); } else { // setup iteration parameters // -------------------------- // iterate target is the whole shape (or the // whole parent subshape), thus, can save // loads of subset shapes by generating them // only when the effects become active - // before and after the effect active // duration, all attributes are shared by // master shape and subset (since the iterated // effects are all the same). aContext.mbIsIndependentSubset = false; // create document tree for given subitem type DocTreeNode::VectorOfDocTreeNodes aTreeNodes; if( bParagraphTarget ) { // create the iterated subset _relative_ to // the given paragraph index (i.e. animate the // given subset type, but only when it's part // of the given paragraph) generateSubsets( aTreeNodes, pTargetShape, eIterateNodeType, aTarget.Paragraph ); } else { // generate normal subset generateSubsets( aTreeNodes, pTargetShape, eIterateNodeType ); } // iterate node, generate copies of the children for each subset // ------------------------------------------------------------- const ::std::size_t nTreeNodeChildren( aTreeNodes.size() ); // NodeContext::mnStartDelay contains additional node delay. This will // make the duplicated nodes for each iteration start increasingly later. aContext.mnStartDelay = nIntervalTimeout; for( ::std::size_t i=0; i& xNode, const BaseContainerNodeSharedPtr& rParent, const NodeContext& rContext ) { ENSURE_AND_THROW( xNode.is(), "implCreateAnimationNode(): invalid XAnimationNode" ); BaseNodeSharedPtr pCreatedNode; BaseContainerNodeSharedPtr pCreatedContainer; // create the internal node, corresponding to xNode switch( xNode->getType() ) { case animations::AnimationNodeType::CUSTOM: OSL_ENSURE( false, "implCreateAnimationNode(): CUSTOM not yet implemented" ); return pCreatedNode; case animations::AnimationNodeType::PAR: pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr( new ParallelTimeContainer( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::ITERATE: // map iterate container to ParallelTimeContainer. // the iterating functionality is to be found // below, (see method implCreateIteratedNodes) pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr( new ParallelTimeContainer( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::SEQ: pCreatedNode = pCreatedContainer = BaseContainerNodeSharedPtr( new SequentialTimeContainer( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::ANIMATE: pCreatedNode.reset( new PropertyAnimationNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::SET: pCreatedNode.reset( new AnimationSetNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::ANIMATEMOTION: pCreatedNode.reset( new AnimationPathMotionNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::ANIMATECOLOR: pCreatedNode.reset( new AnimationColorNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::ANIMATETRANSFORM: pCreatedNode.reset( new AnimationTransformNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::TRANSITIONFILTER: pCreatedNode.reset( new AnimationTransitionFilterNode( xNode, rParent, rContext ) ); break; case animations::AnimationNodeType::AUDIO: pCreatedNode.reset( new AnimationAudioNode( xNode, rParent, rContext ) ); break; default: OSL_ENSURE( false, "implCreateAnimationNode(): invalid AnimationNodeType" ); return pCreatedNode; } // TODO(Q1): This yields circular references, which, it seems, is // unavoidable here // HACK: node objects need shared_ptr to themselves, // which we pass them here. pCreatedNode->setSelf( pCreatedNode ); // if we've got a container node object, recursively add // its children if( pCreatedContainer.get() ) { uno::Reference< animations::XIterateContainer > xIterNode( xNode, uno::UNO_QUERY ); // when this node is an XIterateContainer with // active iterations, this method will generate // the appropriate children if( xIterNode.is() ) { // note that implCreateIteratedNodes() might // choose not to generate any child nodes // (e.g. when the iterate timeout is outside // sensible limits). Then, no child nodes are // generated at all, since typically, child // node attribute are incomplete for iteration // children. implCreateIteratedNodes( xIterNode, pCreatedContainer, rContext ); } else { // no iterate subset node, just plain child generation now NodeCreator aCreator( pCreatedContainer, rContext ); if( !::anim::for_each_childNode( xNode, aCreator ) ) { OSL_ENSURE( false, "implCreateAnimationNode(): child node creation failed" ); return BaseNodeSharedPtr(); } } } return pCreatedNode; } } AnimationNodeSharedPtr AnimationNodeFactory::createAnimationNode( const uno::Reference< animations::XAnimationNode >& xNode, const SlideShowContext& rContext ) { ENSURE_AND_THROW( xNode.is(), "AnimationNodeFactory::createAnimationNode(): invalid XAnimationNode" ); return BaseNodeSharedPtr( implCreateAnimationNode( xNode, BaseContainerNodeSharedPtr(), // no parent NodeContext( rContext ) ) ); } #if defined(VERBOSE) && defined(DBG_UTIL) void AnimationNodeFactory::showTree( AnimationNodeSharedPtr& pRootNode ) { if( pRootNode.get() ) DEBUG_NODES_SHOWTREE( dynamic_cast( pRootNode.get() ) ); } #endif } }