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