1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include <com/sun/star/uno/XComponentContext.hpp> 25 #include <com/sun/star/lang/XServiceInfo.hpp> 26 #include <com/sun/star/lang/XTypeProvider.hpp> 27 #include <com/sun/star/animations/XTargetPropertiesCreator.hpp> 28 #include <com/sun/star/animations/XIterateContainer.hpp> 29 #include <com/sun/star/animations/TargetProperties.hpp> 30 #include <com/sun/star/presentation/ParagraphTarget.hpp> 31 #include <com/sun/star/registry/XRegistryKey.hpp> 32 #include <com/sun/star/lang/XInitialization.hpp> 33 #include <com/sun/star/lang/XServiceName.hpp> 34 #include <com/sun/star/lang/XSingleServiceFactory.hpp> 35 #include <com/sun/star/drawing/XShape.hpp> 36 #include <com/sun/star/animations/AnimationNodeType.hpp> 37 #include <com/sun/star/animations/XAnimate.hpp> 38 #include <cppuhelper/compbase3.hxx> 39 #include <cppuhelper/factory.hxx> 40 #include <cppuhelper/implementationentry.hxx> 41 #include <comphelper/broadcasthelper.hxx> 42 #include <comphelper/sequence.hxx> 43 44 #include <animations/animationnodehelper.hxx> 45 46 #include <vector> 47 #include <hash_map> 48 49 50 using namespace ::com::sun::star; 51 52 #define IMPLEMENTATION_NAME "animcore::TargetPropertiesCreator" 53 #define SERVICE_NAME "com.sun.star.animations.TargetPropertiesCreator" 54 55 namespace animcore 56 { 57 typedef ::cppu::WeakComponentImplHelper3< ::com::sun::star::animations::XTargetPropertiesCreator, 58 lang::XServiceInfo, 59 lang::XServiceName > TargetPropertiesCreator_Base; 60 61 class TargetPropertiesCreator : public ::comphelper::OBaseMutex, 62 public TargetPropertiesCreator_Base 63 { 64 public: 65 static uno::Reference< uno::XInterface > SAL_CALL createInstance( const uno::Reference< uno::XComponentContext >& xContext ) throw ( uno::Exception ) 66 { 67 return uno::Reference< uno::XInterface >( static_cast<cppu::OWeakObject*>(new TargetPropertiesCreator( xContext )) ); 68 } 69 70 /// Dispose all internal references 71 virtual void SAL_CALL disposing(); 72 73 // XTargetPropertiesCreator 74 virtual uno::Sequence< animations::TargetProperties > SAL_CALL createInitialTargetProperties( const uno::Reference< animations::XAnimationNode >& rootNode ) throw (uno::RuntimeException); 75 76 // XServiceInfo 77 virtual ::rtl::OUString SAL_CALL getImplementationName() throw( uno::RuntimeException ); 78 virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ); 79 virtual uno::Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames() throw( uno::RuntimeException ); 80 81 // XServiceName 82 virtual ::rtl::OUString SAL_CALL getServiceName( ) throw (uno::RuntimeException); 83 84 protected: 85 ~TargetPropertiesCreator(); // we're a ref-counted UNO class. _We_ destroy ourselves. 86 87 private: 88 // default: disabled copy/assignment 89 TargetPropertiesCreator(const TargetPropertiesCreator&); 90 TargetPropertiesCreator& operator=( const TargetPropertiesCreator& ); 91 92 TargetPropertiesCreator( const uno::Reference< uno::XComponentContext >& rxContext ); 93 }; 94 95 // -------------------------------------------------------------------- 96 97 uno::Reference< uno::XInterface > SAL_CALL createInstance_TargetPropertiesCreator( const uno::Reference< uno::XComponentContext > & rSMgr ) throw (uno::Exception) 98 { 99 return TargetPropertiesCreator::createInstance( rSMgr ); 100 } 101 102 ::rtl::OUString getImplementationName_TargetPropertiesCreator() 103 { 104 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) ); 105 } 106 107 uno::Sequence< ::rtl::OUString > getSupportedServiceNames_TargetPropertiesCreator(void) 108 { 109 uno::Sequence< ::rtl::OUString > aRet(1); 110 aRet.getArray()[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICE_NAME ) ); 111 return aRet; 112 } 113 114 // -------------------------------------------------------------------- 115 116 namespace 117 { 118 // Vector containing all properties for a given shape 119 typedef ::std::vector< beans::NamedValue > VectorOfNamedValues; 120 121 /** The hash map key 122 123 This key contains both XShape reference and a paragraph 124 index, as we somehow have to handle shape and paragraph 125 targets with the same data structure. 126 */ 127 struct ShapeHashKey 128 { 129 /// Shape target 130 uno::Reference< drawing::XShape > mxRef; 131 132 /** Paragraph index. 133 134 If this is a pure shape target, mnParagraphIndex is 135 set to -1. 136 */ 137 sal_Int16 mnParagraphIndex; 138 139 /// Comparison needed for hash_map 140 bool operator==( const ShapeHashKey& rRHS ) const 141 { 142 return mxRef == rRHS.mxRef && mnParagraphIndex == rRHS.mnParagraphIndex; 143 } 144 }; 145 146 // A hash map which maps a XShape to the corresponding vector of initial properties 147 typedef ::std::hash_map< ShapeHashKey, 148 VectorOfNamedValues, 149 ::std::size_t (*)(const ShapeHashKey&) > XShapeHash; 150 151 ::std::size_t refhasher( const ShapeHashKey& rKey ) 152 { 153 // TODO(P2): Maybe a better hash function would be to 154 // spread mnParagraphIndex to 32 bit: a0b0c0d0e0... Hakmem 155 // should have a formula. 156 // 157 // Yes it has: 158 // x = (x & 0x0000FF00) << 8) | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF; 159 // x = (x & 0x00F000F0) << 4) | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F; 160 // x = (x & 0x0C0C0C0C) << 2) | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3; 161 // x = (x & 0x22222222) << 1) | (x >> 1) & 0x22222222 | x & 0x99999999; 162 // 163 // Costs about 17 cycles on a RISC machine with infinite 164 // instruction level parallelism (~42 basic 165 // instructions). Thus I truly doubt this pays off... 166 return reinterpret_cast< ::std::size_t >(rKey.mxRef.get()) ^ (rKey.mnParagraphIndex << 16L); 167 } 168 169 170 class NodeFunctor 171 { 172 public: 173 explicit NodeFunctor( XShapeHash& rShapeHash ) : 174 mrShapeHash( rShapeHash ), 175 mxTargetShape(), 176 mnParagraphIndex( -1 ) 177 { 178 } 179 180 NodeFunctor( XShapeHash& rShapeHash, 181 const uno::Reference< drawing::XShape >& rTargetShape, 182 sal_Int16 nParagraphIndex ) : 183 mrShapeHash( rShapeHash ), 184 mxTargetShape( rTargetShape ), 185 mnParagraphIndex( nParagraphIndex ) 186 { 187 } 188 189 void operator()( const uno::Reference< animations::XAnimationNode >& xNode ) const 190 { 191 if( !xNode.is() ) 192 { 193 OSL_ENSURE( false, 194 "AnimCore: NodeFunctor::operator(): invalid XAnimationNode" ); 195 return; 196 } 197 198 uno::Reference< drawing::XShape > xTargetShape( mxTargetShape ); 199 sal_Int16 nParagraphIndex( mnParagraphIndex ); 200 201 switch( xNode->getType() ) 202 { 203 case animations::AnimationNodeType::ITERATE: 204 { 205 // extract target shape from iterate node 206 // (will override the target for all children) 207 // -------------------------------------------------- 208 209 uno::Reference< animations::XIterateContainer > xIterNode( xNode, 210 uno::UNO_QUERY ); 211 212 // TODO(E1): I'm not too sure what to expect here... 213 if( !xIterNode->getTarget().hasValue() ) 214 { 215 OSL_ENSURE( false, 216 "animcore: NodeFunctor::operator(): no target on ITERATE node" ); 217 return; 218 } 219 220 xTargetShape.set( xIterNode->getTarget(), 221 uno::UNO_QUERY ); 222 223 if( !xTargetShape.is() ) 224 { 225 ::com::sun::star::presentation::ParagraphTarget aTarget; 226 227 // no shape provided. Maybe a ParagraphTarget? 228 if( !(xIterNode->getTarget() >>= aTarget) ) 229 { 230 OSL_ENSURE( false, 231 "animcore: NodeFunctor::operator(): could not extract any " 232 "target information" ); 233 return; 234 } 235 236 xTargetShape = aTarget.Shape; 237 nParagraphIndex = aTarget.Paragraph; 238 239 if( !xTargetShape.is() ) 240 { 241 OSL_ENSURE( false, 242 "animcore: NodeFunctor::operator(): invalid shape in ParagraphTarget" ); 243 return; 244 } 245 } 246 } 247 // FALLTHROUGH intended 248 case animations::AnimationNodeType::PAR: 249 // FALLTHROUGH intended 250 case animations::AnimationNodeType::SEQ: 251 { 252 NodeFunctor aFunctor( mrShapeHash, 253 xTargetShape, 254 nParagraphIndex ); 255 if( !::anim::for_each_childNode( xNode, 256 aFunctor ) ) 257 { 258 OSL_ENSURE( false, 259 "AnimCore: NodeFunctor::operator(): child node iteration failed, " 260 "or extraneous container nodes encountered" ); 261 } 262 } 263 break; 264 265 case animations::AnimationNodeType::CUSTOM: 266 // FALLTHROUGH intended 267 case animations::AnimationNodeType::ANIMATE: 268 // FALLTHROUGH intended 269 case animations::AnimationNodeType::ANIMATEMOTION: 270 // FALLTHROUGH intended 271 case animations::AnimationNodeType::ANIMATECOLOR: 272 // FALLTHROUGH intended 273 case animations::AnimationNodeType::ANIMATETRANSFORM: 274 // FALLTHROUGH intended 275 case animations::AnimationNodeType::TRANSITIONFILTER: 276 // FALLTHROUGH intended 277 case animations::AnimationNodeType::AUDIO: 278 // FALLTHROUGH intended 279 default: 280 // ignore this node, no valuable content for now. 281 break; 282 283 case animations::AnimationNodeType::SET: 284 { 285 // evaluate set node content 286 uno::Reference< animations::XAnimate > xAnimateNode( xNode, 287 uno::UNO_QUERY ); 288 289 if( !xAnimateNode.is() ) 290 break; // invalid node 291 292 // determine target shape (if any) 293 ShapeHashKey aTarget; 294 if( xTargetShape.is() ) 295 { 296 // override target shape with parent-supplied 297 aTarget.mxRef = xTargetShape; 298 aTarget.mnParagraphIndex = nParagraphIndex; 299 } 300 else 301 { 302 // no parent-supplied target, retrieve 303 // node target 304 if( (xAnimateNode->getTarget() >>= aTarget.mxRef) ) 305 { 306 // pure shape target - set paragraph 307 // index to magic 308 aTarget.mnParagraphIndex = -1; 309 } 310 else 311 { 312 // not a pure shape target - maybe a 313 // ParagraphTarget? 314 presentation::ParagraphTarget aUnoTarget; 315 316 if( !(xAnimateNode->getTarget() >>= aUnoTarget) ) 317 { 318 OSL_ENSURE( false, 319 "AnimCore: NodeFunctor::operator(): unknown target type encountered" ); 320 break; 321 } 322 323 aTarget.mxRef = aUnoTarget.Shape; 324 aTarget.mnParagraphIndex = aUnoTarget.Paragraph; 325 } 326 } 327 328 if( !aTarget.mxRef.is() ) 329 { 330 OSL_ENSURE( false, 331 "AnimCore: NodeFunctor::operator(): Found target, but XShape is NULL" ); 332 break; // invalid target XShape 333 } 334 335 // check whether we already have an entry for 336 // this target (we only take the first set 337 // effect for every shape) 338 XShapeHash::const_iterator aIter; 339 if( (aIter=mrShapeHash.find( aTarget )) != mrShapeHash.end() ) 340 break; // already an entry in existence for given XShape 341 342 // if this is an appear effect, hide shape 343 // initially. This is currently the only place 344 // where a shape effect influences shape 345 // attributes outside it's effective duration. 346 if( xAnimateNode->getAttributeName().equalsIgnoreAsciiCaseAscii("visibility") ) 347 { 348 sal_Bool bVisible( sal_False ); 349 350 uno::Any aAny( xAnimateNode->getTo() ); 351 352 // try to extract bool value 353 if( !(aAny >>= bVisible) ) 354 { 355 // try to extract string 356 ::rtl::OUString aString; 357 if( (aAny >>= aString) ) 358 { 359 // we also take the strings "true" and "false", 360 // as well as "on" and "off" here 361 if( aString.equalsIgnoreAsciiCaseAscii("true") || 362 aString.equalsIgnoreAsciiCaseAscii("on") ) 363 { 364 bVisible = sal_True; 365 } 366 if( aString.equalsIgnoreAsciiCaseAscii("false") || 367 aString.equalsIgnoreAsciiCaseAscii("off") ) 368 { 369 bVisible = sal_False; 370 } 371 } 372 } 373 374 if( bVisible ) 375 { 376 // target is set to 'visible' at the 377 // first relevant effect. Thus, target 378 // must be initially _hidden_, for the 379 // effect to have visible impact. 380 mrShapeHash.insert( 381 XShapeHash::value_type( 382 aTarget, 383 VectorOfNamedValues( 384 1, 385 beans::NamedValue( 386 xAnimateNode->getAttributeName(), 387 uno::makeAny( sal_False ) ) ) ) ); 388 } 389 } 390 } 391 break; 392 } 393 } 394 395 private: 396 XShapeHash& mrShapeHash; 397 uno::Reference< drawing::XShape > mxTargetShape; 398 sal_Int16 mnParagraphIndex; 399 }; 400 } 401 402 // -------------------------------------------------------------------- 403 404 TargetPropertiesCreator::TargetPropertiesCreator( const uno::Reference< uno::XComponentContext >& ) : 405 TargetPropertiesCreator_Base( m_aMutex ) 406 { 407 } 408 409 TargetPropertiesCreator::~TargetPropertiesCreator() 410 { 411 } 412 413 void SAL_CALL TargetPropertiesCreator::disposing() 414 { 415 ::osl::MutexGuard aGuard( m_aMutex ); 416 } 417 418 // XTargetPropertiesCreator 419 uno::Sequence< animations::TargetProperties > SAL_CALL TargetPropertiesCreator::createInitialTargetProperties 420 ( 421 const uno::Reference< animations::XAnimationNode >& xRootNode 422 ) throw (uno::RuntimeException) 423 { 424 ::osl::MutexGuard aGuard( m_aMutex ); 425 426 // scan all nodes for visibility changes, and record first 427 // 'visibility=true' for each shape 428 XShapeHash aShapeHash( 101, 429 &refhasher ); 430 431 NodeFunctor aFunctor( aShapeHash ); 432 433 // TODO(F1): Maybe limit functor application to main sequence 434 // alone (CL said something that shape visibility is only 435 // affected by effects in the main sequence for PPT). 436 // 437 // OTOH, client code can pass us only the main sequence (which 438 // it actually does right now, for the slideshow implementation). 439 aFunctor( xRootNode ); 440 441 442 // output to result sequence 443 // ---------------------------------------------------------------------- 444 445 uno::Sequence< animations::TargetProperties > aRes( aShapeHash.size() ); 446 447 ::std::size_t nCurrIndex(0); 448 XShapeHash::const_iterator aCurr( aShapeHash.begin() ); 449 const XShapeHash::const_iterator aEnd ( aShapeHash.end() ); 450 while( aCurr != aEnd ) 451 { 452 animations::TargetProperties& rCurrProps( aRes[ nCurrIndex++ ] ); 453 454 if( aCurr->first.mnParagraphIndex == -1 ) 455 { 456 rCurrProps.Target = uno::makeAny( aCurr->first.mxRef ); 457 } 458 else 459 { 460 rCurrProps.Target = uno::makeAny( 461 presentation::ParagraphTarget( 462 aCurr->first.mxRef, 463 aCurr->first.mnParagraphIndex ) ); 464 } 465 466 rCurrProps.Properties = ::comphelper::containerToSequence( aCurr->second ); 467 468 ++aCurr; 469 } 470 471 return aRes; 472 } 473 474 // XServiceInfo 475 ::rtl::OUString SAL_CALL TargetPropertiesCreator::getImplementationName() throw( uno::RuntimeException ) 476 { 477 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); 478 } 479 480 sal_Bool SAL_CALL TargetPropertiesCreator::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) 481 { 482 return ServiceName.equalsIgnoreAsciiCaseAscii( SERVICE_NAME ); 483 } 484 485 uno::Sequence< ::rtl::OUString > SAL_CALL TargetPropertiesCreator::getSupportedServiceNames() throw( uno::RuntimeException ) 486 { 487 uno::Sequence< ::rtl::OUString > aRet(1); 488 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); 489 490 return aRet; 491 } 492 493 // XServiceName 494 ::rtl::OUString SAL_CALL TargetPropertiesCreator::getServiceName( ) throw (uno::RuntimeException) 495 { 496 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICE_NAME ) ); 497 } 498 499 } // namespace animcore 500