Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <com/sun/star/uno/XComponentContext.hpp>
21 : #include <com/sun/star/lang/XTypeProvider.hpp>
22 : #include <com/sun/star/animations/XIterateContainer.hpp>
23 : #include <com/sun/star/presentation/ParagraphTarget.hpp>
24 : #include <com/sun/star/registry/XRegistryKey.hpp>
25 : #include <com/sun/star/drawing/XShape.hpp>
26 : #include <com/sun/star/animations/AnimationNodeType.hpp>
27 : #include <com/sun/star/animations/XAnimate.hpp>
28 :
29 : #include <unordered_map>
30 : #include <vector>
31 :
32 : #include "targetpropertiescreator.hxx"
33 : #include "tools.hxx"
34 :
35 : namespace slideshow
36 : {
37 : namespace internal
38 : {
39 : namespace
40 : {
41 : // Vector containing all properties for a given shape
42 : typedef ::std::vector< beans::NamedValue > VectorOfNamedValues;
43 :
44 : /** The hash map key
45 :
46 : This key contains both XShape reference and a paragraph
47 : index, as we somehow have to handle shape and paragraph
48 : targets with the same data structure.
49 : */
50 0 : struct ShapeHashKey
51 : {
52 : /// Shape target
53 : uno::Reference< drawing::XShape > mxRef;
54 :
55 : /** Paragraph index.
56 :
57 : If this is a pure shape target, mnParagraphIndex is
58 : set to -1.
59 : */
60 : sal_Int16 mnParagraphIndex;
61 :
62 : /// Comparison needed for unordered_map
63 0 : bool operator==( const ShapeHashKey& rRHS ) const
64 : {
65 0 : return mxRef == rRHS.mxRef && mnParagraphIndex == rRHS.mnParagraphIndex;
66 : }
67 : };
68 :
69 : // A hash functor for ShapeHashKey objects
70 : struct ShapeKeyHasher
71 : {
72 0 : ::std::size_t operator()( const ShapeHashKey& rKey ) const
73 : {
74 : // TODO(P2): Maybe a better hash function would be to
75 : // spread mnParagraphIndex to 32 bit: a0b0c0d0e0... Hakmem
76 : // should have a formula.
77 :
78 : // Yes it has:
79 : // x = (x & 0x0000FF00) << 8) | (x >> 8) & 0x0000FF00 | x & 0xFF0000FF;
80 : // x = (x & 0x00F000F0) << 4) | (x >> 4) & 0x00F000F0 | x & 0xF00FF00F;
81 : // x = (x & 0x0C0C0C0C) << 2) | (x >> 2) & 0x0C0C0C0C | x & 0xC3C3C3C3;
82 : // x = (x & 0x22222222) << 1) | (x >> 1) & 0x22222222 | x & 0x99999999;
83 :
84 : // Costs about 17 cycles on a RISC machine with infinite
85 : // instruction level parallelism (~42 basic
86 : // instructions). Thus I truly doubt this pays off...
87 0 : return reinterpret_cast< ::std::size_t >(rKey.mxRef.get()) ^ (rKey.mnParagraphIndex << 16L);
88 : }
89 : };
90 :
91 : // A hash map which maps a XShape to the corresponding vector of initial properties
92 : typedef std::unordered_map< ShapeHashKey, VectorOfNamedValues, ShapeKeyHasher > XShapeHash;
93 :
94 :
95 0 : class NodeFunctor
96 : {
97 : public:
98 0 : explicit NodeFunctor( XShapeHash& rShapeHash ) :
99 : mrShapeHash( rShapeHash ),
100 : mxTargetShape(),
101 0 : mnParagraphIndex( -1 )
102 : {
103 0 : }
104 :
105 0 : NodeFunctor( XShapeHash& rShapeHash,
106 : const uno::Reference< drawing::XShape >& rTargetShape,
107 : sal_Int16 nParagraphIndex ) :
108 : mrShapeHash( rShapeHash ),
109 : mxTargetShape( rTargetShape ),
110 0 : mnParagraphIndex( nParagraphIndex )
111 : {
112 0 : }
113 :
114 0 : void operator()( const uno::Reference< animations::XAnimationNode >& xNode ) const
115 : {
116 0 : if( !xNode.is() )
117 : {
118 : OSL_FAIL( "AnimCore: NodeFunctor::operator(): invalid XAnimationNode" );
119 0 : return;
120 : }
121 :
122 0 : uno::Reference< drawing::XShape > xTargetShape( mxTargetShape );
123 0 : sal_Int16 nParagraphIndex( mnParagraphIndex );
124 :
125 0 : switch( xNode->getType() )
126 : {
127 : case animations::AnimationNodeType::ITERATE:
128 : {
129 : // extract target shape from iterate node
130 : // (will override the target for all children)
131 :
132 : uno::Reference< animations::XIterateContainer > xIterNode( xNode,
133 0 : uno::UNO_QUERY );
134 :
135 : // TODO(E1): I'm not too sure what to expect here...
136 0 : if( !xIterNode->getTarget().hasValue() )
137 : {
138 : OSL_FAIL( "animcore: NodeFunctor::operator(): no target on ITERATE node" );
139 0 : return;
140 : }
141 :
142 0 : xTargetShape.set( xIterNode->getTarget(),
143 0 : uno::UNO_QUERY );
144 :
145 0 : if( !xTargetShape.is() )
146 : {
147 0 : ::com::sun::star::presentation::ParagraphTarget aTarget;
148 :
149 : // no shape provided. Maybe a ParagraphTarget?
150 0 : if( !(xIterNode->getTarget() >>= aTarget) )
151 : {
152 : OSL_FAIL( "animcore: NodeFunctor::operator(): could not extract any "
153 : "target information" );
154 0 : return;
155 : }
156 :
157 0 : xTargetShape = aTarget.Shape;
158 0 : nParagraphIndex = aTarget.Paragraph;
159 :
160 0 : if( !xTargetShape.is() )
161 : {
162 : OSL_FAIL( "animcore: NodeFunctor::operator(): invalid shape in ParagraphTarget" );
163 0 : return;
164 0 : }
165 0 : }
166 : }
167 : // FALLTHROUGH intended
168 : case animations::AnimationNodeType::PAR:
169 : // FALLTHROUGH intended
170 : case animations::AnimationNodeType::SEQ:
171 : {
172 : NodeFunctor aFunctor( mrShapeHash,
173 : xTargetShape,
174 0 : nParagraphIndex );
175 0 : if( !for_each_childNode( xNode, aFunctor ) )
176 : {
177 : OSL_FAIL( "AnimCore: NodeFunctor::operator(): child node iteration failed, "
178 : "or extraneous container nodes encountered" );
179 0 : }
180 : }
181 0 : break;
182 :
183 : case animations::AnimationNodeType::CUSTOM:
184 : // FALLTHROUGH intended
185 : case animations::AnimationNodeType::ANIMATE:
186 : // FALLTHROUGH intended
187 : case animations::AnimationNodeType::ANIMATEMOTION:
188 : // FALLTHROUGH intended
189 : case animations::AnimationNodeType::ANIMATECOLOR:
190 : // FALLTHROUGH intended
191 : case animations::AnimationNodeType::ANIMATETRANSFORM:
192 : // FALLTHROUGH intended
193 : case animations::AnimationNodeType::TRANSITIONFILTER:
194 : // FALLTHROUGH intended
195 : case animations::AnimationNodeType::AUDIO:
196 : // FALLTHROUGH intended
197 : /*default:
198 : // ignore this node, no valuable content for now.
199 : break;*/
200 :
201 : case animations::AnimationNodeType::SET:
202 : {
203 : // evaluate set node content
204 : uno::Reference< animations::XAnimate > xAnimateNode( xNode,
205 0 : uno::UNO_QUERY );
206 :
207 0 : if( !xAnimateNode.is() )
208 0 : break; // invalid node
209 :
210 : // determine target shape (if any)
211 0 : ShapeHashKey aTarget;
212 0 : if( xTargetShape.is() )
213 : {
214 : // override target shape with parent-supplied
215 0 : aTarget.mxRef = xTargetShape;
216 0 : aTarget.mnParagraphIndex = nParagraphIndex;
217 : }
218 : else
219 : {
220 : // no parent-supplied target, retrieve
221 : // node target
222 0 : if( (xAnimateNode->getTarget() >>= aTarget.mxRef) )
223 : {
224 : // pure shape target - set paragraph
225 : // index to magic
226 0 : aTarget.mnParagraphIndex = -1;
227 : }
228 : else
229 : {
230 : // not a pure shape target - maybe a
231 : // ParagraphTarget?
232 0 : presentation::ParagraphTarget aUnoTarget;
233 :
234 0 : if( !(xAnimateNode->getTarget() >>= aUnoTarget) )
235 : {
236 : OSL_FAIL( "AnimCore: NodeFunctor::operator(): unknown target type encountered" );
237 0 : break;
238 : }
239 :
240 0 : aTarget.mxRef = aUnoTarget.Shape;
241 0 : aTarget.mnParagraphIndex = aUnoTarget.Paragraph;
242 : }
243 : }
244 :
245 0 : if( !aTarget.mxRef.is() )
246 : {
247 : OSL_FAIL( "AnimCore: NodeFunctor::operator(): Found target, but XShape is NULL" );
248 0 : break; // invalid target XShape
249 : }
250 :
251 : // check whether we already have an entry for
252 : // this target (we only take the first set
253 : // effect for every shape)
254 0 : XShapeHash::const_iterator aIter;
255 0 : if( (aIter=mrShapeHash.find( aTarget )) != mrShapeHash.end() )
256 0 : break; // already an entry in existence for given XShape
257 :
258 : // if this is an appear effect, hide shape
259 : // initially. This is currently the only place
260 : // where a shape effect influences shape
261 : // attributes outside it's effective duration.
262 0 : bool bVisible( false );
263 0 : if( xAnimateNode->getAttributeName().equalsIgnoreAsciiCase("visibility") )
264 : {
265 :
266 0 : uno::Any aAny( xAnimateNode->getTo() );
267 :
268 : // try to extract bool value
269 0 : if( !(aAny >>= bVisible) )
270 : {
271 : // try to extract string
272 0 : OUString aString;
273 0 : if( (aAny >>= aString) )
274 : {
275 : // we also take the strings "true" and "false",
276 : // as well as "on" and "off" here
277 0 : if( aString.equalsIgnoreAsciiCase("true") ||
278 0 : aString.equalsIgnoreAsciiCase("on") )
279 : {
280 0 : bVisible = true;
281 : }
282 0 : if( aString.equalsIgnoreAsciiCase("false") ||
283 0 : aString.equalsIgnoreAsciiCase("off") )
284 : {
285 0 : bVisible = false;
286 : }
287 0 : }
288 0 : }
289 : }
290 : // target is set the 'visible' value,
291 : // so we should record the opposite value
292 : mrShapeHash.insert(
293 : XShapeHash::value_type(
294 : aTarget,
295 : VectorOfNamedValues(
296 : 1,
297 : beans::NamedValue(
298 : //xAnimateNode->getAttributeName(),
299 : OUString("visibility"),
300 0 : uno::makeAny( !bVisible ) ) ) ) );
301 0 : break;
302 : }
303 0 : }
304 : }
305 :
306 : private:
307 : XShapeHash& mrShapeHash;
308 : uno::Reference< drawing::XShape > mxTargetShape;
309 : sal_Int16 mnParagraphIndex;
310 : };
311 : }
312 :
313 0 : uno::Sequence< animations::TargetProperties > SAL_CALL TargetPropertiesCreator::createInitialTargetProperties
314 : (
315 : const uno::Reference< animations::XAnimationNode >& xRootNode
316 : ) //throw (uno::RuntimeException, std::exception)
317 : {
318 : // scan all nodes for visibility changes, and record first
319 : // 'visibility=true' for each shape
320 0 : XShapeHash aShapeHash( 101 );
321 :
322 0 : NodeFunctor aFunctor( aShapeHash );
323 :
324 : // TODO(F1): Maybe limit functor application to main sequence
325 : // alone (CL said something that shape visibility is only
326 : // affected by effects in the main sequence for PPT).
327 :
328 : // OTOH, client code can pass us only the main sequence (which
329 : // it actually does right now, for the slideshow implementation).
330 0 : aFunctor( xRootNode );
331 :
332 : // output to result sequence
333 0 : uno::Sequence< animations::TargetProperties > aRes( aShapeHash.size() );
334 :
335 0 : ::std::size_t nCurrIndex(0);
336 0 : XShapeHash::const_iterator aCurr( aShapeHash.begin() );
337 0 : const XShapeHash::const_iterator aEnd ( aShapeHash.end() );
338 0 : while( aCurr != aEnd )
339 : {
340 0 : animations::TargetProperties& rCurrProps( aRes[ nCurrIndex++ ] );
341 :
342 0 : if( aCurr->first.mnParagraphIndex == -1 )
343 : {
344 0 : rCurrProps.Target = uno::makeAny( aCurr->first.mxRef );
345 : }
346 : else
347 : {
348 0 : rCurrProps.Target = uno::makeAny(
349 : presentation::ParagraphTarget(
350 0 : aCurr->first.mxRef,
351 0 : aCurr->first.mnParagraphIndex ) );
352 : }
353 :
354 0 : rCurrProps.Properties = ::comphelper::containerToSequence( aCurr->second );
355 :
356 0 : ++aCurr;
357 : }
358 :
359 0 : return aRes;
360 : }
361 :
362 : } // namespace internal
363 : } // namespace slideshow
364 :
365 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|