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