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 sal_Bool bVisible( sal_False ); 347 if( xAnimateNode->getAttributeName().equalsIgnoreAsciiCaseAscii("visibility") ) 348 { 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 */ 381 } 382 // target is set the 'visible' value, 383 // so we should record the opposite value 384 mrShapeHash.insert( 385 XShapeHash::value_type( 386 aTarget, 387 VectorOfNamedValues( 388 1, 389 beans::NamedValue( 390 //xAnimateNode->getAttributeName(), 391 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("visibility")), 392 uno::makeAny( !bVisible ) ) ) ) ); 393 //} 394 //} 395 } 396 break; 397 } 398 } 399 400 private: 401 XShapeHash& mrShapeHash; 402 uno::Reference< drawing::XShape > mxTargetShape; 403 sal_Int16 mnParagraphIndex; 404 }; 405 } 406 407 // -------------------------------------------------------------------- 408 409 TargetPropertiesCreator::TargetPropertiesCreator( const uno::Reference< uno::XComponentContext >& ) : 410 TargetPropertiesCreator_Base( m_aMutex ) 411 { 412 } 413 414 TargetPropertiesCreator::~TargetPropertiesCreator() 415 { 416 } 417 418 void SAL_CALL TargetPropertiesCreator::disposing() 419 { 420 ::osl::MutexGuard aGuard( m_aMutex ); 421 } 422 423 // XTargetPropertiesCreator 424 uno::Sequence< animations::TargetProperties > SAL_CALL TargetPropertiesCreator::createInitialTargetProperties 425 ( 426 const uno::Reference< animations::XAnimationNode >& xRootNode 427 ) throw (uno::RuntimeException) 428 { 429 ::osl::MutexGuard aGuard( m_aMutex ); 430 431 // scan all nodes for visibility changes, and record first 432 // 'visibility=true' for each shape 433 XShapeHash aShapeHash( 101, 434 &refhasher ); 435 436 NodeFunctor aFunctor( aShapeHash ); 437 438 // TODO(F1): Maybe limit functor application to main sequence 439 // alone (CL said something that shape visibility is only 440 // affected by effects in the main sequence for PPT). 441 // 442 // OTOH, client code can pass us only the main sequence (which 443 // it actually does right now, for the slideshow implementation). 444 aFunctor( xRootNode ); 445 446 447 // output to result sequence 448 // ---------------------------------------------------------------------- 449 450 uno::Sequence< animations::TargetProperties > aRes( aShapeHash.size() ); 451 452 ::std::size_t nCurrIndex(0); 453 XShapeHash::const_iterator aCurr( aShapeHash.begin() ); 454 const XShapeHash::const_iterator aEnd ( aShapeHash.end() ); 455 while( aCurr != aEnd ) 456 { 457 animations::TargetProperties& rCurrProps( aRes[ nCurrIndex++ ] ); 458 459 if( aCurr->first.mnParagraphIndex == -1 ) 460 { 461 rCurrProps.Target = uno::makeAny( aCurr->first.mxRef ); 462 } 463 else 464 { 465 rCurrProps.Target = uno::makeAny( 466 presentation::ParagraphTarget( 467 aCurr->first.mxRef, 468 aCurr->first.mnParagraphIndex ) ); 469 } 470 471 rCurrProps.Properties = ::comphelper::containerToSequence( aCurr->second ); 472 473 ++aCurr; 474 } 475 476 return aRes; 477 } 478 479 // XServiceInfo 480 ::rtl::OUString SAL_CALL TargetPropertiesCreator::getImplementationName() throw( uno::RuntimeException ) 481 { 482 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); 483 } 484 485 sal_Bool SAL_CALL TargetPropertiesCreator::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) 486 { 487 return ServiceName.equalsIgnoreAsciiCaseAscii( SERVICE_NAME ); 488 } 489 490 uno::Sequence< ::rtl::OUString > SAL_CALL TargetPropertiesCreator::getSupportedServiceNames() throw( uno::RuntimeException ) 491 { 492 uno::Sequence< ::rtl::OUString > aRet(1); 493 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); 494 495 return aRet; 496 } 497 498 // XServiceName 499 ::rtl::OUString SAL_CALL TargetPropertiesCreator::getServiceName( ) throw (uno::RuntimeException) 500 { 501 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICE_NAME ) ); 502 } 503 504 } // namespace animcore 505