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 "precompiled_sd.hxx"
25 #include "controller/SlsAnimator.hxx"
26 #include "view/SlideSorterView.hxx"
27 #include "View.hxx"
28 #include <boost/bind.hpp>
29
30 namespace sd { namespace slidesorter { namespace controller {
31
32 namespace {
33 static const sal_Int32 gnResolution = 25;
34 }
35 /** Handle one animation function by using a timer for frequent calls to
36 the animations operator().
37 */
38 class Animator::Animation
39 {
40 public:
41 Animation (
42 const Animator::AnimationFunctor& rAnimation,
43 const double nStartOffset,
44 const double nDuration,
45 const double nGlobalTime,
46 const Animator::AnimationId nAnimationId,
47 const Animator::FinishFunctor& rFinishFunctor);
48 ~Animation (void);
49 /** Run next animation step. If animation has reached its end it is
50 expired.
51 */
52 bool Run (const double nGlobalTime);
53
54 /** Typically called when an animation has finished, but also from
55 Animator::Disposed(). The finish functor is called and the
56 animation is marked as expired to prevent another run.
57 */
58 void Expire (void);
59 bool IsExpired (void);
60
61 Animator::AnimationFunctor maAnimation;
62 Animator::FinishFunctor maFinishFunctor;
63 const Animator::AnimationId mnAnimationId;
64 const double mnDuration;
65 const double mnEnd;
66 const double mnGlobalTimeAtStart;
67 bool mbIsExpired;
68 };
69
70
71
72
Animator(SlideSorter & rSlideSorter)73 Animator::Animator (SlideSorter& rSlideSorter)
74 : mrSlideSorter(rSlideSorter),
75 maTimer(),
76 mbIsDisposed(false),
77 maAnimations(),
78 maElapsedTime(),
79 mpDrawLock(),
80 mnNextAnimationId(0)
81 {
82 maTimer.SetTimeout(gnResolution);
83 maTimer.SetTimeoutHdl(LINK(this,Animator,TimeoutHandler));
84 }
85
86
87
88
~Animator(void)89 Animator::~Animator (void)
90 {
91 if ( ! mbIsDisposed)
92 {
93 OSL_ASSERT(mbIsDisposed);
94 Dispose();
95 }
96 }
97
98
99
100
Dispose(void)101 void Animator::Dispose (void)
102 {
103 mbIsDisposed = true;
104
105 AnimationList aCopy (maAnimations);
106 AnimationList::const_iterator iAnimation;
107 for (iAnimation=aCopy.begin(); iAnimation!=aCopy.end(); ++iAnimation)
108 (*iAnimation)->Expire();
109
110 maTimer.Stop();
111 if (mpDrawLock)
112 {
113 mpDrawLock->Dispose();
114 mpDrawLock.reset();
115 }
116 }
117
118
119
120
AddAnimation(const AnimationFunctor & rAnimation,const sal_Int32 nStartOffset,const sal_Int32 nDuration,const FinishFunctor & rFinishFunctor)121 Animator::AnimationId Animator::AddAnimation (
122 const AnimationFunctor& rAnimation,
123 const sal_Int32 nStartOffset,
124 const sal_Int32 nDuration,
125 const FinishFunctor& rFinishFunctor)
126 {
127 // When the animator is already disposed then ignore this call
128 // silently (well, we show an assertion, but do not throw an exception.)
129 OSL_ASSERT( ! mbIsDisposed);
130 if (mbIsDisposed)
131 return -1;
132
133 boost::shared_ptr<Animation> pAnimation (
134 new Animation(
135 rAnimation,
136 nStartOffset / 1000.0,
137 nDuration / 1000.0,
138 maElapsedTime.getElapsedTime(),
139 ++mnNextAnimationId,
140 rFinishFunctor));
141 maAnimations.push_back(pAnimation);
142
143 RequestNextFrame();
144
145 return pAnimation->mnAnimationId;
146 }
147
148
149
150
AddInfiniteAnimation(const AnimationFunctor & rAnimation,const double nDelta)151 Animator::AnimationId Animator::AddInfiniteAnimation (
152 const AnimationFunctor& rAnimation,
153 const double nDelta)
154 {
155 (void)nDelta;
156
157 // When the animator is already disposed then ignore this call
158 // silently (well, we show an assertion, but do not throw an exception.)
159 OSL_ASSERT( ! mbIsDisposed);
160 if (mbIsDisposed)
161 return -1;
162
163 boost::shared_ptr<Animation> pAnimation (
164 new Animation(
165 rAnimation,
166 0,
167 -1,
168 maElapsedTime.getElapsedTime(),
169 mnNextAnimationId++,
170 FinishFunctor()));
171 maAnimations.push_back(pAnimation);
172
173 RequestNextFrame();
174
175 return pAnimation->mnAnimationId;
176 }
177
178
179
180
RemoveAnimation(const Animator::AnimationId nId)181 void Animator::RemoveAnimation (const Animator::AnimationId nId)
182 {
183 OSL_ASSERT( ! mbIsDisposed);
184
185 const AnimationList::iterator iAnimation (::std::find_if(
186 maAnimations.begin(),
187 maAnimations.end(),
188 ::boost::bind(
189 ::std::equal_to<Animator::AnimationId>(),
190 nId,
191 ::boost::bind(&Animation::mnAnimationId, _1))));
192 if (iAnimation != maAnimations.end())
193 {
194 OSL_ASSERT((*iAnimation)->mnAnimationId == nId);
195 (*iAnimation)->Expire();
196 maAnimations.erase(iAnimation);
197 }
198
199 if (maAnimations.empty())
200 {
201 // Reset the animation id when we can.
202 mnNextAnimationId = 0;
203
204 // No more animations => we do not have to suppress painting
205 // anymore.
206 mpDrawLock.reset();
207 }
208 }
209
210
211
212
RemoveAllAnimations(void)213 void Animator::RemoveAllAnimations (void)
214 {
215 ::std::for_each(
216 maAnimations.begin(),
217 maAnimations.end(),
218 ::boost::bind(
219 &Animation::Expire,
220 _1));
221 maAnimations.clear();
222 mnNextAnimationId = 0;
223
224 // No more animations => we do not have to suppress painting
225 // anymore.
226 mpDrawLock.reset();
227 }
228
229
230
231
ProcessAnimations(const double nTime)232 bool Animator::ProcessAnimations (const double nTime)
233 {
234 bool bExpired (false);
235
236 OSL_ASSERT( ! mbIsDisposed);
237 if (mbIsDisposed)
238 return bExpired;
239
240
241 AnimationList aCopy (maAnimations);
242 AnimationList::const_iterator iAnimation;
243 for (iAnimation=aCopy.begin(); iAnimation!=aCopy.end(); ++iAnimation)
244 {
245 bExpired |= (*iAnimation)->Run(nTime);
246 }
247
248 return bExpired;
249 }
250
251
252
253
CleanUpAnimationList(void)254 void Animator::CleanUpAnimationList (void)
255 {
256 OSL_ASSERT( ! mbIsDisposed);
257 if (mbIsDisposed)
258 return;
259
260 AnimationList aActiveAnimations;
261
262 AnimationList::const_iterator iAnimation;
263 for (iAnimation=maAnimations.begin(); iAnimation!=maAnimations.end(); ++iAnimation)
264 {
265 if ( ! (*iAnimation)->IsExpired())
266 aActiveAnimations.push_back(*iAnimation);
267 }
268
269 maAnimations.swap(aActiveAnimations);
270 }
271
272
273
274
RequestNextFrame(const double nFrameStart)275 void Animator::RequestNextFrame (const double nFrameStart)
276 {
277 (void)nFrameStart;
278 if ( ! maTimer.IsActive())
279 {
280 // Prevent redraws except for the ones in TimeoutHandler. While the
281 // Animator is active it will schedule repaints regularly. Repaints
282 // in between would only lead to visual artifacts.
283 mpDrawLock.reset(new view::SlideSorterView::DrawLock(mrSlideSorter));
284 maTimer.Start();
285 }
286 }
287
288
289
290
IMPL_LINK(Animator,TimeoutHandler,Timer *,EMPTYARG)291 IMPL_LINK(Animator, TimeoutHandler, Timer*, EMPTYARG)
292 {
293 if (mbIsDisposed)
294 return 0;
295
296 if (ProcessAnimations(maElapsedTime.getElapsedTime()))
297 CleanUpAnimationList();
298
299 // Unlock the draw lock. This should lead to a repaint.
300 mpDrawLock.reset();
301
302 if (!maAnimations.empty())
303 RequestNextFrame();
304
305 return 0;
306 }
307
308
309
310
311 //===== Animator::Animation ===================================================
312
Animation(const Animator::AnimationFunctor & rAnimation,const double nStartOffset,const double nDuration,const double nGlobalTime,const Animator::AnimationId nId,const Animator::FinishFunctor & rFinishFunctor)313 Animator::Animation::Animation (
314 const Animator::AnimationFunctor& rAnimation,
315 const double nStartOffset,
316 const double nDuration,
317 const double nGlobalTime,
318 const Animator::AnimationId nId,
319 const Animator::FinishFunctor& rFinishFunctor)
320 : maAnimation(rAnimation),
321 maFinishFunctor(rFinishFunctor),
322 mnAnimationId(nId),
323 mnDuration(nDuration),
324 mnEnd(nGlobalTime + nDuration + nStartOffset),
325 mnGlobalTimeAtStart(nGlobalTime + nStartOffset),
326 mbIsExpired(false)
327 {
328 Run(nGlobalTime);
329 }
330
331
332
333
~Animation(void)334 Animator::Animation::~Animation (void)
335 {
336 }
337
338
339
340
Run(const double nGlobalTime)341 bool Animator::Animation::Run (const double nGlobalTime)
342 {
343 if ( ! mbIsExpired)
344 {
345 if (mnDuration > 0)
346 {
347 if (nGlobalTime >= mnEnd)
348 {
349 maAnimation(1.0);
350 Expire();
351 }
352 else if (nGlobalTime >= mnGlobalTimeAtStart)
353 {
354 maAnimation((nGlobalTime - mnGlobalTimeAtStart) / mnDuration);
355 }
356 }
357 else if (mnDuration < 0)
358 {
359 // Animations without end have to be expired by their owner.
360 maAnimation(nGlobalTime);
361 }
362 }
363
364 return mbIsExpired;
365 }
366
367
368
369
Expire(void)370 void Animator::Animation::Expire (void)
371 {
372 if ( ! mbIsExpired)
373 {
374 mbIsExpired = true;
375 if (maFinishFunctor)
376 maFinishFunctor();
377 }
378 }
379
380
381
382
IsExpired(void)383 bool Animator::Animation::IsExpired (void)
384 {
385 return mbIsExpired;
386 }
387
388
389
390
391 } } } // end of namespace ::sd::slidesorter::controller
392