170f497fbSAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 370f497fbSAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 470f497fbSAndrew Rist * or more contributor license agreements. See the NOTICE file 570f497fbSAndrew Rist * distributed with this work for additional information 670f497fbSAndrew Rist * regarding copyright ownership. The ASF licenses this file 770f497fbSAndrew Rist * to you under the Apache License, Version 2.0 (the 870f497fbSAndrew Rist * "License"); you may not use this file except in compliance 970f497fbSAndrew Rist * with the License. You may obtain a copy of the License at 1070f497fbSAndrew Rist * 1170f497fbSAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 1270f497fbSAndrew Rist * 1370f497fbSAndrew Rist * Unless required by applicable law or agreed to in writing, 1470f497fbSAndrew Rist * software distributed under the License is distributed on an 1570f497fbSAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1670f497fbSAndrew Rist * KIND, either express or implied. See the License for the 1770f497fbSAndrew Rist * specific language governing permissions and limitations 1870f497fbSAndrew Rist * under the License. 1970f497fbSAndrew Rist * 2070f497fbSAndrew Rist *************************************************************/ 2170f497fbSAndrew Rist 2270f497fbSAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 25cdf0e10cSrcweir #include "precompiled_slideshow.hxx" 26cdf0e10cSrcweir 27cdf0e10cSrcweir // must be first 28cdf0e10cSrcweir #include <canvas/debug.hxx> 29cdf0e10cSrcweir #include <canvas/verbosetrace.hxx> 30cdf0e10cSrcweir #include <cppuhelper/exc_hlp.hxx> 31cdf0e10cSrcweir #include <comphelper/anytostring.hxx> 32cdf0e10cSrcweir #include <com/sun/star/presentation/ParagraphTarget.hpp> 33cdf0e10cSrcweir #include <com/sun/star/animations/Timing.hpp> 34cdf0e10cSrcweir #include <com/sun/star/animations/AnimationAdditiveMode.hpp> 35cdf0e10cSrcweir #include <com/sun/star/presentation/ShapeAnimationSubType.hpp> 36cdf0e10cSrcweir 37cdf0e10cSrcweir #include "nodetools.hxx" 38cdf0e10cSrcweir #include "doctreenode.hxx" 39cdf0e10cSrcweir #include "animationbasenode.hxx" 40cdf0e10cSrcweir #include "delayevent.hxx" 41cdf0e10cSrcweir #include "framerate.hxx" 42cdf0e10cSrcweir 43cdf0e10cSrcweir #include <boost/bind.hpp> 44cdf0e10cSrcweir #include <boost/optional.hpp> 45cdf0e10cSrcweir #include <algorithm> 46cdf0e10cSrcweir 47cdf0e10cSrcweir using namespace com::sun::star; 48cdf0e10cSrcweir 49cdf0e10cSrcweir namespace slideshow { 50cdf0e10cSrcweir namespace internal { 51cdf0e10cSrcweir 52cdf0e10cSrcweir AnimationBaseNode::AnimationBaseNode( 53cdf0e10cSrcweir const uno::Reference< animations::XAnimationNode >& xNode, 54cdf0e10cSrcweir const BaseContainerNodeSharedPtr& rParent, 55cdf0e10cSrcweir const NodeContext& rContext ) 56cdf0e10cSrcweir : BaseNode( xNode, rParent, rContext ), 57cdf0e10cSrcweir mxAnimateNode( xNode, uno::UNO_QUERY_THROW ), 58cdf0e10cSrcweir maAttributeLayerHolder(), 59cdf0e10cSrcweir maSlideSize( rContext.maSlideSize ), 60cdf0e10cSrcweir mpActivity(), 61cdf0e10cSrcweir mpShape(), 62cdf0e10cSrcweir mpShapeSubset(), 63cdf0e10cSrcweir mpSubsetManager(rContext.maContext.mpSubsettableShapeManager), 64cdf0e10cSrcweir mbIsIndependentSubset( rContext.mbIsIndependentSubset ) 65cdf0e10cSrcweir { 66cdf0e10cSrcweir // extract native node targets 67cdf0e10cSrcweir // =========================== 68cdf0e10cSrcweir 69cdf0e10cSrcweir // plain shape target 70cdf0e10cSrcweir uno::Reference< drawing::XShape > xShape( mxAnimateNode->getTarget(), 71cdf0e10cSrcweir uno::UNO_QUERY ); 72cdf0e10cSrcweir 73cdf0e10cSrcweir // distinguish 5 cases: 74cdf0e10cSrcweir // 75cdf0e10cSrcweir // - plain shape target 76cdf0e10cSrcweir // (NodeContext.mpMasterShapeSubset full set) 77cdf0e10cSrcweir // 78cdf0e10cSrcweir // - parent-generated subset (generate an 79cdf0e10cSrcweir // independent subset) 80cdf0e10cSrcweir // 81cdf0e10cSrcweir // - parent-generated subset from iteration 82cdf0e10cSrcweir // (generate a dependent subset) 83cdf0e10cSrcweir // 84cdf0e10cSrcweir // - XShape target at the XAnimatioNode (generate 85cdf0e10cSrcweir // a plain shape target) 86cdf0e10cSrcweir // 87cdf0e10cSrcweir // - ParagraphTarget target at the XAnimationNode 88cdf0e10cSrcweir // (generate an independent shape subset) 89cdf0e10cSrcweir if( rContext.mpMasterShapeSubset ) 90cdf0e10cSrcweir { 91cdf0e10cSrcweir if( rContext.mpMasterShapeSubset->isFullSet() ) 92cdf0e10cSrcweir { 93cdf0e10cSrcweir // case 1: plain shape target from parent 94cdf0e10cSrcweir mpShape = rContext.mpMasterShapeSubset->getSubsetShape(); 95cdf0e10cSrcweir } 96cdf0e10cSrcweir else 97cdf0e10cSrcweir { 98cdf0e10cSrcweir // cases 2 & 3: subset shape 99cdf0e10cSrcweir mpShapeSubset = rContext.mpMasterShapeSubset; 100cdf0e10cSrcweir } 101cdf0e10cSrcweir } 102cdf0e10cSrcweir else 103cdf0e10cSrcweir { 104cdf0e10cSrcweir // no parent-provided shape, try to extract 105cdf0e10cSrcweir // from XAnimationNode - cases 4 and 5 106cdf0e10cSrcweir 107cdf0e10cSrcweir if( xShape.is() ) 108cdf0e10cSrcweir { 109cdf0e10cSrcweir mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager, 110cdf0e10cSrcweir xShape ); 111cdf0e10cSrcweir } 112cdf0e10cSrcweir else 113cdf0e10cSrcweir { 114cdf0e10cSrcweir // no shape provided. Maybe a ParagraphTarget? 115cdf0e10cSrcweir presentation::ParagraphTarget aTarget; 116cdf0e10cSrcweir 117cdf0e10cSrcweir if( !(mxAnimateNode->getTarget() >>= aTarget) ) 118cdf0e10cSrcweir ENSURE_OR_THROW( 119cdf0e10cSrcweir false, "could not extract any target information" ); 120cdf0e10cSrcweir 121cdf0e10cSrcweir xShape = aTarget.Shape; 122cdf0e10cSrcweir 123cdf0e10cSrcweir ENSURE_OR_THROW( xShape.is(), "invalid shape in ParagraphTarget" ); 124cdf0e10cSrcweir 125cdf0e10cSrcweir mpShape = lookupAttributableShape( getContext().mpSubsettableShapeManager, 126cdf0e10cSrcweir xShape ); 127cdf0e10cSrcweir 128cdf0e10cSrcweir // NOTE: For shapes with ParagraphTarget, we ignore 129cdf0e10cSrcweir // the SubItem property. We implicitely assume that it 130cdf0e10cSrcweir // is set to ONLY_TEXT. 131cdf0e10cSrcweir OSL_ENSURE( 132cdf0e10cSrcweir mxAnimateNode->getSubItem() == 133cdf0e10cSrcweir presentation::ShapeAnimationSubType::ONLY_TEXT || 134cdf0e10cSrcweir mxAnimateNode->getSubItem() == 135cdf0e10cSrcweir presentation::ShapeAnimationSubType::AS_WHOLE, 136cdf0e10cSrcweir "ParagraphTarget given, but subitem not AS_TEXT or AS_WHOLE? " 137cdf0e10cSrcweir "Make up your mind, I'll ignore the subitem." ); 138cdf0e10cSrcweir 139cdf0e10cSrcweir // okay, found a ParagraphTarget with a valid XShape. Does the shape 140cdf0e10cSrcweir // provide the given paragraph? 141cdf0e10cSrcweir const DocTreeNode& rTreeNode( 142cdf0e10cSrcweir mpShape->getTreeNodeSupplier().getTreeNode( 143cdf0e10cSrcweir aTarget.Paragraph, 144cdf0e10cSrcweir DocTreeNode::NODETYPE_LOGICAL_PARAGRAPH ) ); 145cdf0e10cSrcweir 146cdf0e10cSrcweir // CAUTION: the creation of the subset shape 147cdf0e10cSrcweir // _must_ stay in the node constructor, since 148cdf0e10cSrcweir // Slide::prefetchShow() initializes shape 149cdf0e10cSrcweir // attributes right after animation import (or 150cdf0e10cSrcweir // the Slide class must be changed). 151cdf0e10cSrcweir mpShapeSubset.reset( 152cdf0e10cSrcweir new ShapeSubset( mpShape, 153cdf0e10cSrcweir rTreeNode, 154cdf0e10cSrcweir mpSubsetManager )); 155cdf0e10cSrcweir 156cdf0e10cSrcweir // Override NodeContext, and flag this node as 157cdf0e10cSrcweir // a special independent subset one. This is 158cdf0e10cSrcweir // important when applying initial attributes: 159cdf0e10cSrcweir // independent shape subsets must be setup 160cdf0e10cSrcweir // when the slide starts, since they, as their 161cdf0e10cSrcweir // name suggest, can have state independent to 162cdf0e10cSrcweir // the master shape. The following example 163cdf0e10cSrcweir // might illustrate that: a master shape has 164cdf0e10cSrcweir // no effect, one of the text paragraphs 165cdf0e10cSrcweir // within it has an appear effect. Now, the 166cdf0e10cSrcweir // respective paragraph must be invisible when 167cdf0e10cSrcweir // the slide is initially shown, and become 168cdf0e10cSrcweir // visible only when the effect starts. 169cdf0e10cSrcweir mbIsIndependentSubset = true; 170cdf0e10cSrcweir 171cdf0e10cSrcweir // already enable subset right here, the 172cdf0e10cSrcweir // setup of initial shape attributes of 173cdf0e10cSrcweir // course needs the subset shape 174cdf0e10cSrcweir // generated, to apply e.g. visibility 175cdf0e10cSrcweir // changes. 176cdf0e10cSrcweir mpShapeSubset->enableSubsetShape(); 177cdf0e10cSrcweir } 178cdf0e10cSrcweir } 179cdf0e10cSrcweir } 180cdf0e10cSrcweir 181cdf0e10cSrcweir void AnimationBaseNode::dispose() 182cdf0e10cSrcweir { 183cdf0e10cSrcweir if (mpActivity) { 184cdf0e10cSrcweir mpActivity->dispose(); 185cdf0e10cSrcweir mpActivity.reset(); 186cdf0e10cSrcweir } 187cdf0e10cSrcweir 188cdf0e10cSrcweir maAttributeLayerHolder.reset(); 189cdf0e10cSrcweir mxAnimateNode.clear(); 190cdf0e10cSrcweir mpShape.reset(); 191cdf0e10cSrcweir mpShapeSubset.reset(); 192cdf0e10cSrcweir 193cdf0e10cSrcweir BaseNode::dispose(); 194cdf0e10cSrcweir } 195cdf0e10cSrcweir 196cdf0e10cSrcweir bool AnimationBaseNode::init_st() 197cdf0e10cSrcweir { 198cdf0e10cSrcweir // if we've still got an old activity lying around, dispose it: 199cdf0e10cSrcweir if (mpActivity) { 200cdf0e10cSrcweir mpActivity->dispose(); 201cdf0e10cSrcweir mpActivity.reset(); 202cdf0e10cSrcweir } 203cdf0e10cSrcweir 204cdf0e10cSrcweir // note: actually disposing the activity too early might cause problems, 205cdf0e10cSrcweir // because on dequeued() it calls endAnimation(pAnim->end()), thus ending 206cdf0e10cSrcweir // animation _after_ last screen update. 207cdf0e10cSrcweir // review that end() is properly called (which calls endAnimation(), too). 208cdf0e10cSrcweir 209cdf0e10cSrcweir try { 210cdf0e10cSrcweir // TODO(F2): For restart functionality, we must regenerate activities, 211cdf0e10cSrcweir // since they are not able to reset their state (or implement _that_) 212cdf0e10cSrcweir mpActivity = createActivity(); 213cdf0e10cSrcweir } 214cdf0e10cSrcweir catch (uno::Exception const&) { 215cdf0e10cSrcweir OSL_ENSURE( false, rtl::OUStringToOString( 216cdf0e10cSrcweir comphelper::anyToString(cppu::getCaughtException()), 217cdf0e10cSrcweir RTL_TEXTENCODING_UTF8 ) ); 218cdf0e10cSrcweir // catch and ignore. We later handle empty activities, but for 219cdf0e10cSrcweir // other nodes to function properly, the core functionality of 220cdf0e10cSrcweir // this node must remain up and running. 221cdf0e10cSrcweir } 222cdf0e10cSrcweir return true; 223cdf0e10cSrcweir } 224cdf0e10cSrcweir 225cdf0e10cSrcweir bool AnimationBaseNode::resolve_st() 226cdf0e10cSrcweir { 227cdf0e10cSrcweir // enable shape subset for automatically generated 228cdf0e10cSrcweir // subsets. Independent subsets are already setup 229cdf0e10cSrcweir // during construction time. Doing it only here 230cdf0e10cSrcweir // saves us a lot of sprites and shapes lying 231cdf0e10cSrcweir // around. This is especially important for 232cdf0e10cSrcweir // character-wise iterations, since the shape 233cdf0e10cSrcweir // content (e.g. thousands of characters) would 234cdf0e10cSrcweir // otherwise be painted character-by-character. 235cdf0e10cSrcweir if (isDependentSubsettedShape() && mpShapeSubset) { 236cdf0e10cSrcweir mpShapeSubset->enableSubsetShape(); 237cdf0e10cSrcweir } 238cdf0e10cSrcweir return true; 239cdf0e10cSrcweir } 240cdf0e10cSrcweir 241cdf0e10cSrcweir void AnimationBaseNode::activate_st() 242cdf0e10cSrcweir { 243cdf0e10cSrcweir // create new attribute layer 244cdf0e10cSrcweir maAttributeLayerHolder.createAttributeLayer( getShape() ); 245cdf0e10cSrcweir 246cdf0e10cSrcweir ENSURE_OR_THROW( maAttributeLayerHolder.get(), 247cdf0e10cSrcweir "Could not generate shape attribute layer" ); 248cdf0e10cSrcweir 249cdf0e10cSrcweir // TODO(Q2): This affects the way mpActivity 250cdf0e10cSrcweir // works, but is performed here because of 251cdf0e10cSrcweir // locality (we're fiddling with the additive mode 252cdf0e10cSrcweir // here, anyway, and it's the only place where we 253cdf0e10cSrcweir // do). OTOH, maybe the complete additive mode 254cdf0e10cSrcweir // setup should be moved to the activities. 255cdf0e10cSrcweir 256cdf0e10cSrcweir // for simple by-animations, the SMIL spec 257cdf0e10cSrcweir // requires us to emulate "0,by-value" value list 258cdf0e10cSrcweir // behaviour, with additive mode forced to "sum", 259cdf0e10cSrcweir // no matter what the input is 260cdf0e10cSrcweir // (http://www.w3.org/TR/smil20/animation.html#adef-by). 261cdf0e10cSrcweir if( mxAnimateNode->getBy().hasValue() && 262cdf0e10cSrcweir !mxAnimateNode->getTo().hasValue() && 263cdf0e10cSrcweir !mxAnimateNode->getFrom().hasValue() ) 264cdf0e10cSrcweir { 265cdf0e10cSrcweir // force attribute mode to REPLACE (note the 266cdf0e10cSrcweir // subtle discrepancy to the paragraph above, 267cdf0e10cSrcweir // where SMIL requires SUM. This is internally 268cdf0e10cSrcweir // handled by the FromToByActivity, and is 269cdf0e10cSrcweir // because otherwise DOM values would not be 270cdf0e10cSrcweir // handled correctly: the activity cannot 271cdf0e10cSrcweir // determine whether an 272cdf0e10cSrcweir // Activity::getUnderlyingValue() yields the 273cdf0e10cSrcweir // DOM value, or already a summed-up conglomerate) 274cdf0e10cSrcweir // 275cdf0e10cSrcweir // Note that this poses problems with our 276cdf0e10cSrcweir // hybrid activity duration (time or min number of frames), 277cdf0e10cSrcweir // since if activities 278cdf0e10cSrcweir // exceed their duration, wrong 'by' start 279cdf0e10cSrcweir // values might arise ('Laser effect') 280cdf0e10cSrcweir maAttributeLayerHolder.get()->setAdditiveMode( 281cdf0e10cSrcweir animations::AnimationAdditiveMode::REPLACE ); 282cdf0e10cSrcweir } 283cdf0e10cSrcweir else 284cdf0e10cSrcweir { 285cdf0e10cSrcweir // apply additive mode to newly created Attribute layer 286cdf0e10cSrcweir maAttributeLayerHolder.get()->setAdditiveMode( 287cdf0e10cSrcweir mxAnimateNode->getAdditive() ); 288cdf0e10cSrcweir } 289cdf0e10cSrcweir 290cdf0e10cSrcweir // fake normal animation behaviour, even if we 291cdf0e10cSrcweir // show nothing. This is the appropriate way to 292cdf0e10cSrcweir // handle errors on Activity generation, because 293cdf0e10cSrcweir // maybe all other effects on the slide are 294cdf0e10cSrcweir // correctly initialized (but won't run, if we 295cdf0e10cSrcweir // signal an error here) 296cdf0e10cSrcweir if (mpActivity) { 297cdf0e10cSrcweir // supply Activity (and the underlying Animation) with 298cdf0e10cSrcweir // it's AttributeLayer, to perform the animation on 299cdf0e10cSrcweir mpActivity->setTargets( getShape(), maAttributeLayerHolder.get() ); 300cdf0e10cSrcweir 301cdf0e10cSrcweir // add to activities queue 302cdf0e10cSrcweir getContext().mrActivitiesQueue.addActivity( mpActivity ); 303cdf0e10cSrcweir } 304cdf0e10cSrcweir else { 305cdf0e10cSrcweir // Actually, DO generate the event for empty activity, 306cdf0e10cSrcweir // to keep the chain of animations running 307cdf0e10cSrcweir BaseNode::scheduleDeactivationEvent(); 308cdf0e10cSrcweir } 309cdf0e10cSrcweir } 310cdf0e10cSrcweir 311cdf0e10cSrcweir void AnimationBaseNode::deactivate_st( NodeState eDestState ) 312cdf0e10cSrcweir { 313cdf0e10cSrcweir if (eDestState == FROZEN) { 314cdf0e10cSrcweir if (mpActivity) 315cdf0e10cSrcweir mpActivity->end(); 316cdf0e10cSrcweir } 317cdf0e10cSrcweir 318cdf0e10cSrcweir if (isDependentSubsettedShape()) { 319cdf0e10cSrcweir // for dependent subsets, remove subset shape 320cdf0e10cSrcweir // from layer, re-integrate subsetted part 321cdf0e10cSrcweir // back into original shape. For independent 322cdf0e10cSrcweir // subsets, we cannot make any assumptions 323cdf0e10cSrcweir // about subset attribute state relative to 324cdf0e10cSrcweir // master shape, thus, have to keep it. This 325cdf0e10cSrcweir // will effectively re-integrate the subsetted 326cdf0e10cSrcweir // part into the original shape (whose 327cdf0e10cSrcweir // animation will hopefully have ended, too) 328cdf0e10cSrcweir 329cdf0e10cSrcweir // this statement will save a whole lot of 330cdf0e10cSrcweir // sprites for iterated text effects, since 331cdf0e10cSrcweir // those sprites will only exist during the 332cdf0e10cSrcweir // actual lifetime of the effects 333cdf0e10cSrcweir if (mpShapeSubset) { 334cdf0e10cSrcweir mpShapeSubset->disableSubsetShape(); 335cdf0e10cSrcweir } 336cdf0e10cSrcweir } 337cdf0e10cSrcweir 338cdf0e10cSrcweir if (eDestState == ENDED) { 339cdf0e10cSrcweir 340cdf0e10cSrcweir // no shape anymore, no layer needed: 341cdf0e10cSrcweir maAttributeLayerHolder.reset(); 342cdf0e10cSrcweir 343cdf0e10cSrcweir if (! isDependentSubsettedShape()) { 344cdf0e10cSrcweir 345cdf0e10cSrcweir // for all other shapes, removing the 346cdf0e10cSrcweir // attribute layer quite possibly changes 347cdf0e10cSrcweir // shape display. Thus, force update 348cdf0e10cSrcweir AttributableShapeSharedPtr const pShape( getShape() ); 349cdf0e10cSrcweir 350cdf0e10cSrcweir // don't anybody dare to check against 351cdf0e10cSrcweir // pShape->isVisible() here, removing the 352cdf0e10cSrcweir // attribute layer might actually make the 353cdf0e10cSrcweir // shape invisible! 354cdf0e10cSrcweir getContext().mpSubsettableShapeManager->notifyShapeUpdate( pShape ); 355cdf0e10cSrcweir } 356cdf0e10cSrcweir 357cdf0e10cSrcweir if (mpActivity) { 358cdf0e10cSrcweir // kill activity, if still running 359cdf0e10cSrcweir mpActivity->dispose(); 360cdf0e10cSrcweir mpActivity.reset(); 361cdf0e10cSrcweir } 362cdf0e10cSrcweir } 363cdf0e10cSrcweir } 364cdf0e10cSrcweir 365cdf0e10cSrcweir bool AnimationBaseNode::hasPendingAnimation() const 366cdf0e10cSrcweir { 367cdf0e10cSrcweir // TODO(F1): This might not always be true. Are there 'inactive' 368cdf0e10cSrcweir // animation nodes? 369cdf0e10cSrcweir return true; 370cdf0e10cSrcweir } 371cdf0e10cSrcweir 372cdf0e10cSrcweir #if defined(VERBOSE) && defined(DBG_UTIL) 373cdf0e10cSrcweir void AnimationBaseNode::showState() const 374cdf0e10cSrcweir { 375cdf0e10cSrcweir BaseNode::showState(); 376cdf0e10cSrcweir 377cdf0e10cSrcweir VERBOSE_TRACE( "AnimationBaseNode info: independent subset=%s", 378cdf0e10cSrcweir mbIsIndependentSubset ? "y" : "n" ); 379cdf0e10cSrcweir } 380cdf0e10cSrcweir #endif 381cdf0e10cSrcweir 382cdf0e10cSrcweir ActivitiesFactory::CommonParameters 383cdf0e10cSrcweir AnimationBaseNode::fillCommonParameters() const 384cdf0e10cSrcweir { 385cdf0e10cSrcweir double nDuration = 0.0; 386cdf0e10cSrcweir 387cdf0e10cSrcweir // TODO(F3): Duration/End handling is barely there 388cdf0e10cSrcweir if( !(mxAnimateNode->getDuration() >>= nDuration) ) { 389cdf0e10cSrcweir mxAnimateNode->getEnd() >>= nDuration; // Wah. 390cdf0e10cSrcweir } 391cdf0e10cSrcweir 392cdf0e10cSrcweir // minimal duration we fallback to (avoid 0 here!) 393cdf0e10cSrcweir nDuration = ::std::max( 0.001, nDuration ); 394cdf0e10cSrcweir 395cdf0e10cSrcweir const bool bAutoReverse( mxAnimateNode->getAutoReverse() ); 396cdf0e10cSrcweir 397cdf0e10cSrcweir boost::optional<double> aRepeats; 398cdf0e10cSrcweir double nRepeats = 0; 399*bf97b58dSAndre Fischer bool bRepeatIndefinite = false; 400*bf97b58dSAndre Fischer animations::Timing eTiming; 401*bf97b58dSAndre Fischer 402*bf97b58dSAndre Fischer // Search parent nodes for an explicitly stated repeat count. 403*bf97b58dSAndre Fischer BaseNodeSharedPtr const pSelf( getSelf() ); 404*bf97b58dSAndre Fischer for ( boost::shared_ptr<BaseNode> pNode( pSelf ); 405*bf97b58dSAndre Fischer pNode; 406*bf97b58dSAndre Fischer pNode = pNode->getParentNode() ) 407*bf97b58dSAndre Fischer { 408*bf97b58dSAndre Fischer uno::Reference<animations::XAnimationNode> const xAnimationNode( 409*bf97b58dSAndre Fischer pNode->getXAnimationNode() ); 410*bf97b58dSAndre Fischer if( (xAnimationNode->getRepeatCount() >>= nRepeats) ) 411*bf97b58dSAndre Fischer { 412*bf97b58dSAndre Fischer // Found an explicit repeat count. 413*bf97b58dSAndre Fischer break; 414*bf97b58dSAndre Fischer } 415*bf97b58dSAndre Fischer if( (xAnimationNode->getRepeatCount() >>= eTiming) && 416*bf97b58dSAndre Fischer (eTiming == animations::Timing_INDEFINITE )) 417*bf97b58dSAndre Fischer { 418*bf97b58dSAndre Fischer // Found an explicit repeat count of Timing::INDEFINITE. 419*bf97b58dSAndre Fischer bRepeatIndefinite = true; 420*bf97b58dSAndre Fischer break; 421*bf97b58dSAndre Fischer } 422*bf97b58dSAndre Fischer } 423*bf97b58dSAndre Fischer 424*bf97b58dSAndre Fischer if( nRepeats || bRepeatIndefinite ) { 425*bf97b58dSAndre Fischer if (nRepeats) 426*bf97b58dSAndre Fischer { 427*bf97b58dSAndre Fischer aRepeats.reset( nRepeats ); 428*bf97b58dSAndre Fischer } 429cdf0e10cSrcweir } 430cdf0e10cSrcweir else { 431cdf0e10cSrcweir if( (mxAnimateNode->getRepeatDuration() >>= nRepeats) ) { 432cdf0e10cSrcweir // when repeatDuration is given, 433cdf0e10cSrcweir // autoreverse does _not_ modify the 434cdf0e10cSrcweir // active duration. Thus, calc repeat 435cdf0e10cSrcweir // count with already adapted simple 436cdf0e10cSrcweir // duration (twice the specified duration) 437cdf0e10cSrcweir 438cdf0e10cSrcweir // convert duration back to repeat counts 439cdf0e10cSrcweir if( bAutoReverse ) 440cdf0e10cSrcweir aRepeats.reset( nRepeats / (2.0 * nDuration) ); 441cdf0e10cSrcweir else 442cdf0e10cSrcweir aRepeats.reset( nRepeats / nDuration ); 443cdf0e10cSrcweir } 444cdf0e10cSrcweir else { 445cdf0e10cSrcweir // no double value for both values - Timing::INDEFINITE? 446cdf0e10cSrcweir animations::Timing eTiming; 447cdf0e10cSrcweir 448cdf0e10cSrcweir if( !(mxAnimateNode->getRepeatDuration() >>= eTiming) || 449cdf0e10cSrcweir eTiming != animations::Timing_INDEFINITE ) 450cdf0e10cSrcweir { 451cdf0e10cSrcweir if( !(mxAnimateNode->getRepeatCount() >>= eTiming) || 452cdf0e10cSrcweir eTiming != animations::Timing_INDEFINITE ) 453cdf0e10cSrcweir { 454cdf0e10cSrcweir // no indefinite timing, no other values given - 455cdf0e10cSrcweir // use simple run, i.e. repeat of 1.0 456cdf0e10cSrcweir aRepeats.reset( 1.0 ); 457cdf0e10cSrcweir } 458cdf0e10cSrcweir } 459cdf0e10cSrcweir } 460cdf0e10cSrcweir } 461*bf97b58dSAndre Fischer 462cdf0e10cSrcweir // calc accel/decel: 463cdf0e10cSrcweir double nAcceleration = 0.0; 464cdf0e10cSrcweir double nDeceleration = 0.0; 465*bf97b58dSAndre Fischer en for ( boost::shared_ptr<BaseNode> pNode( pSelf ); 466cdf0e10cSrcweir pNode; pNode = pNode->getParentNode() ) 467cdf0e10cSrcweir { 468cdf0e10cSrcweir uno::Reference<animations::XAnimationNode> const xAnimationNode( 469cdf0e10cSrcweir pNode->getXAnimationNode() ); 470cdf0e10cSrcweir nAcceleration = std::max( nAcceleration, 471cdf0e10cSrcweir xAnimationNode->getAcceleration() ); 472cdf0e10cSrcweir nDeceleration = std::max( nDeceleration, 473cdf0e10cSrcweir xAnimationNode->getDecelerate() ); 474cdf0e10cSrcweir } 475cdf0e10cSrcweir 476cdf0e10cSrcweir EventSharedPtr pEndEvent; 477cdf0e10cSrcweir if (pSelf) { 478cdf0e10cSrcweir pEndEvent = makeEvent( 479cdf0e10cSrcweir boost::bind( &AnimationNode::deactivate, pSelf ), 480cdf0e10cSrcweir "AnimationBaseNode::deactivate"); 481cdf0e10cSrcweir } 482cdf0e10cSrcweir 483cdf0e10cSrcweir // Calculate the minimum frame count that depends on the duration and 484cdf0e10cSrcweir // the minimum frame count. 485cdf0e10cSrcweir const sal_Int32 nMinFrameCount (basegfx::clamp<sal_Int32>( 486cdf0e10cSrcweir basegfx::fround(nDuration * FrameRate::MinimumFramesPerSecond), 1, 10)); 487cdf0e10cSrcweir 488cdf0e10cSrcweir return ActivitiesFactory::CommonParameters( 489cdf0e10cSrcweir pEndEvent, 490cdf0e10cSrcweir getContext().mrEventQueue, 491cdf0e10cSrcweir getContext().mrActivitiesQueue, 492cdf0e10cSrcweir nDuration, 493cdf0e10cSrcweir nMinFrameCount, 494cdf0e10cSrcweir bAutoReverse, 495cdf0e10cSrcweir aRepeats, 496cdf0e10cSrcweir nAcceleration, 497cdf0e10cSrcweir nDeceleration, 498cdf0e10cSrcweir getShape(), 499cdf0e10cSrcweir getSlideSize()); 500cdf0e10cSrcweir } 501cdf0e10cSrcweir 502cdf0e10cSrcweir AttributableShapeSharedPtr AnimationBaseNode::getShape() const 503cdf0e10cSrcweir { 504cdf0e10cSrcweir // any subsetting at all? 505cdf0e10cSrcweir if (mpShapeSubset) 506cdf0e10cSrcweir return mpShapeSubset->getSubsetShape(); 507cdf0e10cSrcweir else 508cdf0e10cSrcweir return mpShape; // nope, plain shape always 509cdf0e10cSrcweir } 510cdf0e10cSrcweir 511cdf0e10cSrcweir } // namespace internal 512cdf0e10cSrcweir } // namespace slideshow 513cdf0e10cSrcweir 514