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