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:
createInstance(const uno::Reference<uno::XComponentContext> & xContext)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 
createInstance_TargetPropertiesCreator(const uno::Reference<uno::XComponentContext> & rSMgr)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 
getImplementationName_TargetPropertiesCreator()102     ::rtl::OUString getImplementationName_TargetPropertiesCreator()
103     {
104         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) );
105     }
106 
getSupportedServiceNames_TargetPropertiesCreator(void)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
operator ==animcore::__anon7875b3a30111::ShapeHashKey140             bool operator==( const ShapeHashKey& rRHS ) const
141             {
142                 return mxRef == rRHS.mxRef && mnParagraphIndex == rRHS.mnParagraphIndex;
143             }
144         };
145 
146         // A hash functor for ShapeHashKey objects
147         struct ShapeKeyHasher {
operator ()animcore::__anon7875b3a30111::ShapeKeyHasher148 		::std::size_t operator()( const ShapeHashKey& rKey ) const
149 		{
150 		// TODO(P2): Maybe a better hash function would be to
151 		// spread mnParagraphIndex to 32 bit: a0b0c0d0e0... Hakmem
152 		// should have a formula.
153 		//
154 		// Yes it has:
155 		// x = (x & 0x0000FF00) << 8) | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
156 		// x = (x & 0x00F000F0) << 4) | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
157 		// x = (x & 0x0C0C0C0C) << 2) | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
158 		// x = (x & 0x22222222) << 1) | (x >> 1) & 0x22222222 | x & 0x99999999;
159 		//
160 		// Costs about 17 cycles on a RISC machine with infinite
161 		// instruction level parallelism (~42 basic
162 		// instructions). Thus I truly doubt this pays off...
163 		return reinterpret_cast< ::std::size_t >(rKey.mxRef.get()) ^ (rKey.mnParagraphIndex << 16L);
164 	    }
165 	};
166 
167         // A hash map which maps a XShape to the corresponding vector of initial properties
168         typedef ::std::hash_map< ShapeHashKey, VectorOfNamedValues, ShapeKeyHasher > XShapeHash;
169 
170         class NodeFunctor
171         {
172         public:
NodeFunctor(XShapeHash & rShapeHash)173             explicit NodeFunctor( XShapeHash& rShapeHash ) :
174                 mrShapeHash( rShapeHash ),
175                 mxTargetShape(),
176                 mnParagraphIndex( -1 )
177             {
178             }
179 
NodeFunctor(XShapeHash & rShapeHash,const uno::Reference<drawing::XShape> & rTargetShape,sal_Int16 nParagraphIndex)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 
operator ()(const uno::Reference<animations::XAnimationNode> & xNode) const189             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 
TargetPropertiesCreator(const uno::Reference<uno::XComponentContext> &)409     TargetPropertiesCreator::TargetPropertiesCreator( const uno::Reference< uno::XComponentContext >&  ) :
410         TargetPropertiesCreator_Base( m_aMutex )
411     {
412     }
413 
~TargetPropertiesCreator()414     TargetPropertiesCreator::~TargetPropertiesCreator()
415     {
416     }
417 
disposing()418 	void SAL_CALL TargetPropertiesCreator::disposing()
419 	{
420         ::osl::MutexGuard aGuard( m_aMutex );
421 	}
422 
423     // XTargetPropertiesCreator
createInitialTargetProperties(const uno::Reference<animations::XAnimationNode> & xRootNode)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 
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
getImplementationName()479     ::rtl::OUString SAL_CALL TargetPropertiesCreator::getImplementationName() throw( uno::RuntimeException )
480     {
481         return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) );
482     }
483 
supportsService(const::rtl::OUString & ServiceName)484     sal_Bool SAL_CALL TargetPropertiesCreator::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException )
485     {
486         return ServiceName.equalsIgnoreAsciiCaseAscii( SERVICE_NAME );
487     }
488 
getSupportedServiceNames()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
getServiceName()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