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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_slideshow.hxx"
26 
27 #include <canvas/debug.hxx>
28 #include <tools/diagnose_ex.h>
29 #include <canvas/verbosetrace.hxx>
30 
31 #include "drawshapesubsetting.hxx"
32 #include "subsettableshapemanager.hxx"
33 #include "eventqueue.hxx"
34 #include "eventmultiplexer.hxx"
35 #include "intrinsicanimationactivity.hxx"
36 #include "intrinsicanimationeventhandler.hxx"
37 
38 #include <boost/noncopyable.hpp>
39 #include <boost/enable_shared_from_this.hpp>
40 #include <boost/weak_ptr.hpp>
41 
42 namespace slideshow
43 {
44     namespace internal
45     {
46         /** Activity for intrinsic shape animations
47 
48         	This is an Activity interface implementation for intrinsic
49         	shape animations. Intrinsic shape animations are
50         	animations directly within a shape, e.g. drawing layer
51         	animations, or GIF animations.
52          */
53         class IntrinsicAnimationActivity : public Activity,
54                                            public boost::enable_shared_from_this<IntrinsicAnimationActivity>,
55                                            private boost::noncopyable
56         {
57         public:
58             /** Create an IntrinsicAnimationActivity.
59 
60             	@param rContext
61                 Common slideshow objects
62 
63                 @param rDrawShape
64                 Shape to control the intrinsic animation for
65 
66                 @param rWakeupEvent
67                 Externally generated wakeup event, to set this
68                 activity to sleep during inter-frame intervals. Must
69                 come frome the outside, since wakeup event and this
70                 object have mutual references to each other.
71 
72                 @param rTimeouts
73                 Vector of timeout values, to wait before the next
74                 frame is shown.
75              */
76             IntrinsicAnimationActivity( const SlideShowContext& 		rContext,
77                                         const DrawShapeSharedPtr&		rDrawShape,
78                                         const WakeupEventSharedPtr&		rWakeupEvent,
79                                         const ::std::vector<double>&	rTimeouts,
80                                         ::std::size_t                   nNumLoops,
81                                         CycleMode                       eCycleMode );
82 
83             virtual void dispose();
84             virtual double calcTimeLag() const;
85             virtual bool perform();
86             virtual bool isActive() const;
87             virtual void dequeued();
88             virtual void end();
89 
90             bool enableAnimations();
91 
92         private:
93             SlideShowContext                        maContext;
94             boost::weak_ptr<DrawShape>              mpDrawShape;
95             WakeupEventSharedPtr                    mpWakeupEvent;
96             IntrinsicAnimationEventHandlerSharedPtr mpListener;
97             ::std::vector<double>                   maTimeouts;
98             CycleMode                               meCycleMode;
99             ::std::size_t                           mnCurrIndex;
100             ::std::size_t                           mnNumLoops;
101             ::std::size_t                           mnLoopCount;
102             bool                                    mbIsActive;
103         };
104 
105         //////////////////////////////////////////////////////////////////////
106 
107         class IntrinsicAnimationListener : public IntrinsicAnimationEventHandler,
108                                            private boost::noncopyable
109         {
110         public:
111             explicit IntrinsicAnimationListener( IntrinsicAnimationActivity& rActivity ) :
112                 mrActivity( rActivity )
113             {}
114 
115         private:
116 
117             virtual bool enableAnimations() { return mrActivity.enableAnimations(); }
118             virtual bool disableAnimations() { mrActivity.end(); return true; }
119 
120             IntrinsicAnimationActivity& mrActivity;
121         };
122 
123         //////////////////////////////////////////////////////////////////////
124 
125         IntrinsicAnimationActivity::IntrinsicAnimationActivity( const SlideShowContext& 		rContext,
126                                                                 const DrawShapeSharedPtr&		rDrawShape,
127                                                                 const WakeupEventSharedPtr&		rWakeupEvent,
128                                                                 const ::std::vector<double>&	rTimeouts,
129                                                                 ::std::size_t                   nNumLoops,
130                                                                 CycleMode                       eCycleMode ) :
131             maContext( rContext ),
132             mpDrawShape( rDrawShape ),
133             mpWakeupEvent( rWakeupEvent ),
134             mpListener( new IntrinsicAnimationListener(*this) ),
135             maTimeouts( rTimeouts ),
136             meCycleMode( eCycleMode ),
137             mnCurrIndex(0),
138             mnNumLoops(nNumLoops),
139             mnLoopCount(0),
140             mbIsActive(false)
141         {
142             ENSURE_OR_THROW( rContext.mpSubsettableShapeManager,
143                               "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid shape manager" );
144             ENSURE_OR_THROW( rDrawShape,
145                               "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid draw shape" );
146             ENSURE_OR_THROW( rWakeupEvent,
147                               "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Invalid wakeup event" );
148             ENSURE_OR_THROW( !rTimeouts.empty(),
149                               "IntrinsicAnimationActivity::IntrinsicAnimationActivity(): Empty timeout vector" );
150 
151             maContext.mpSubsettableShapeManager->addIntrinsicAnimationHandler(
152                 mpListener );
153         }
154 
155         void IntrinsicAnimationActivity::dispose()
156         {
157             end();
158 
159             if( mpWakeupEvent )
160                 mpWakeupEvent->dispose();
161 
162             maContext.dispose();
163             mpDrawShape.reset();
164             mpWakeupEvent.reset();
165             maTimeouts.clear();
166             mnCurrIndex = 0;
167 
168             maContext.mpSubsettableShapeManager->removeIntrinsicAnimationHandler(
169                 mpListener );
170         }
171 
172         double IntrinsicAnimationActivity::calcTimeLag() const
173         {
174             return 0.0;
175         }
176 
177         bool IntrinsicAnimationActivity::perform()
178         {
179             if( !isActive() )
180                 return false;
181 
182             DrawShapeSharedPtr pDrawShape( mpDrawShape.lock() );
183             if( !pDrawShape || !mpWakeupEvent )
184             {
185                 // event or draw shape vanished, no sense living on ->
186                 // commit suicide.
187                 dispose();
188                 return false;
189             }
190 
191             // mnNumLoops == 0 means infinite looping
192             if( mnNumLoops != 0 &&
193                 mnLoopCount >= mnNumLoops )
194             {
195                 // #i55294# After finishing the loops, display the first frame
196                 pDrawShape->setIntrinsicAnimationFrame( 0 );
197                 maContext.mpSubsettableShapeManager->notifyShapeUpdate( pDrawShape );
198 
199                 end();
200 
201                 return false;
202             }
203 
204             ::std::size_t       nNewIndex = 0;
205             const ::std::size_t nNumFrames(maTimeouts.size());
206             switch( meCycleMode )
207             {
208                 case CYCLE_LOOP:
209                 {
210                     pDrawShape->setIntrinsicAnimationFrame( mnCurrIndex );
211 
212                     mpWakeupEvent->start();
213                     mpWakeupEvent->setNextTimeout( maTimeouts[mnCurrIndex] );
214 
215                     mnLoopCount += (mnCurrIndex + 1) / nNumFrames;
216                     nNewIndex = (mnCurrIndex + 1) % nNumFrames;
217                     break;
218                 }
219 
220                 case CYCLE_PINGPONGLOOP:
221                 {
222                     ::std::size_t nTrueIndex( mnCurrIndex < nNumFrames ?
223                                               mnCurrIndex :
224                                               2*nNumFrames - mnCurrIndex - 1 );
225                     pDrawShape->setIntrinsicAnimationFrame( nTrueIndex );
226 
227                     mpWakeupEvent->start();
228                     mpWakeupEvent->setNextTimeout( maTimeouts[nTrueIndex] );
229 
230                     mnLoopCount += (mnCurrIndex + 1) / (2*nNumFrames);
231                     nNewIndex = (mnCurrIndex + 1) % 2*nNumFrames;
232                     break;
233                 }
234             }
235 
236             maContext.mrEventQueue.addEvent( mpWakeupEvent );
237             maContext.mpSubsettableShapeManager->notifyShapeUpdate( pDrawShape );
238             mnCurrIndex = nNewIndex;
239 
240             return false; // don't reinsert, WakeupEvent will perform
241                           // that after the given timeout
242         }
243 
244         bool IntrinsicAnimationActivity::isActive() const
245         {
246             return mbIsActive;
247         }
248 
249         void IntrinsicAnimationActivity::dequeued()
250         {
251             // not used here
252         }
253 
254         void IntrinsicAnimationActivity::end()
255         {
256             // there is no dedicated end state, just become inactive:
257             mbIsActive = false;
258         }
259 
260         bool IntrinsicAnimationActivity::enableAnimations()
261         {
262             mbIsActive = true;
263             return maContext.mrActivitiesQueue.addActivity(
264                 shared_from_this() );
265         }
266 
267         //////////////////////////////////////////////////////////////////////
268 
269         ActivitySharedPtr createIntrinsicAnimationActivity(
270             const SlideShowContext& 		rContext,
271             const DrawShapeSharedPtr&		rDrawShape,
272             const WakeupEventSharedPtr&		rWakeupEvent,
273             const ::std::vector<double>&	rTimeouts,
274             ::std::size_t                   nNumLoops,
275             CycleMode                       eCycleMode )
276         {
277             return ActivitySharedPtr(
278                 new IntrinsicAnimationActivity(rContext,
279                                                rDrawShape,
280                                                rWakeupEvent,
281                                                rTimeouts,
282                                                nNumLoops,
283                                                eCycleMode) );
284         }
285     }
286 }
287