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