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_slideshow.hxx"
25 
26 #include "effectrewinder.hxx"
27 #include "eventqueue.hxx"
28 #include "usereventqueue.hxx"
29 #include "mouseeventhandler.hxx"
30 #include "animationnodes/basecontainernode.hxx"
31 #include "delayevent.hxx"
32 
33 #include <com/sun/star/awt/MouseEvent.hpp>
34 #include <com/sun/star/animations/Event.hpp>
35 #include <com/sun/star/animations/EventTrigger.hpp>
36 #include <com/sun/star/container/XEnumerationAccess.hpp>
37 #include <boost/function.hpp>
38 #include <boost/bind.hpp>
39 #include <boost/enable_shared_from_this.hpp>
40 
41 using ::com::sun::star::uno::Reference;
42 using namespace ::com::sun::star;
43 
44 namespace slideshow { namespace internal {
45 
46 
47 namespace {
48 
49 class RewinderEventHandler : public EventHandler
50 {
51 public:
52     typedef ::boost::function<bool(void)> Action;
RewinderEventHandler(const Action & rAction)53     RewinderEventHandler (const Action& rAction) : maAction(rAction) {}
~RewinderEventHandler(void)54     virtual ~RewinderEventHandler (void) {}
55 private:
56     const Action maAction;
handleEvent(void)57     virtual bool handleEvent (void) { return maAction(); }
58 };
59 
60 
61 
62 class RewinderAnimationEventHandler : public AnimationEventHandler
63 {
64 public:
65     typedef ::boost::function<bool(const AnimationNodeSharedPtr& rpNode)> Action;
RewinderAnimationEventHandler(const Action & rAction)66     RewinderAnimationEventHandler (const Action& rAction) : maAction(rAction) {}
~RewinderAnimationEventHandler(void)67     virtual ~RewinderAnimationEventHandler (void) {}
68 private:
69     const Action maAction;
handleAnimationEvent(const AnimationNodeSharedPtr & rpNode)70     virtual bool handleAnimationEvent (const AnimationNodeSharedPtr& rpNode)
71         { return maAction(rpNode); }
72 };
73 
74 
75 
76 } // end of anonymous namespace
77 
78 
79 //----- EffectRewinder --------------------------------------------------------------
80 
EffectRewinder(EventMultiplexer & rEventMultiplexer,EventQueue & rEventQueue,UserEventQueue & rUserEventQueue)81 EffectRewinder::EffectRewinder (
82     EventMultiplexer& rEventMultiplexer,
83     EventQueue& rEventQueue,
84     UserEventQueue& rUserEventQueue)
85     : mrEventMultiplexer(rEventMultiplexer),
86       mrEventQueue(rEventQueue),
87       mrUserEventQueue(rUserEventQueue),
88       mpSlideStartHandler(),
89       mpSlideEndHandler(),
90       mpAnimationStartHandler(),
91       mnMainSequenceEffectCount(0),
92       mpAsynchronousRewindEvent(),
93       mxCurrentAnimationRootNode(),
94       mbNonUserTriggeredMainSequenceEffectSeen(false)
95 {
96     initialize();
97 }
98 
99 
100 
101 
initialize(void)102 void EffectRewinder::initialize (void)
103 {
104     // Add some event handlers so that we are informed when
105     // a) an animation is started (we then check whether that belongs to a
106     // main sequence effect and if so, increase the respective counter),
107     // b,c) a slide was started or ended (in which case the effect counter
108     // is reset.
109 
110     mpAnimationStartHandler.reset(
111         new RewinderAnimationEventHandler(
112             ::boost::bind(&EffectRewinder::notifyAnimationStart, this, _1)));
113     mrEventMultiplexer.addAnimationStartHandler(mpAnimationStartHandler);
114 
115     mpSlideStartHandler.reset(
116         new RewinderEventHandler(
117             ::boost::bind(&EffectRewinder::resetEffectCount, this)));
118     mrEventMultiplexer.addSlideStartHandler(mpSlideStartHandler);
119 
120     mpSlideEndHandler.reset(
121         new RewinderEventHandler(
122             ::boost::bind(&EffectRewinder::resetEffectCount, this)));
123     mrEventMultiplexer.addSlideEndHandler(mpSlideEndHandler);
124 }
125 
126 
127 
128 
~EffectRewinder(void)129 EffectRewinder::~EffectRewinder (void)
130 {
131     dispose();
132 }
133 
134 
135 
136 
dispose(void)137 void EffectRewinder::dispose (void)
138 {
139     if (mpAsynchronousRewindEvent)
140     {
141         mpAsynchronousRewindEvent->dispose();
142         mpAsynchronousRewindEvent.reset();
143     }
144 
145     if (mpAnimationStartHandler)
146     {
147         mrEventMultiplexer.removeAnimationStartHandler(mpAnimationStartHandler);
148         mpAnimationStartHandler.reset();
149     }
150 
151     if (mpSlideStartHandler)
152     {
153         mrEventMultiplexer.removeSlideStartHandler(mpSlideStartHandler);
154         mpSlideStartHandler.reset();
155     }
156 
157     if (mpSlideEndHandler)
158     {
159         mrEventMultiplexer.removeSlideEndHandler(mpSlideEndHandler);
160         mpSlideEndHandler.reset();
161     }
162 }
163 
164 
165 
166 
setRootAnimationNode(const uno::Reference<animations::XAnimationNode> & xRootNode)167 void EffectRewinder::setRootAnimationNode (
168     const uno::Reference<animations::XAnimationNode>& xRootNode)
169 {
170     mxCurrentAnimationRootNode = xRootNode;
171 }
172 
173 
174 
175 
rewind(const::boost::shared_ptr<ScreenUpdater::UpdateLock> & rpPaintLock,const::boost::function<void (void)> & rSlideRewindFunctor,const::boost::function<void (void)> & rPreviousSlideFunctor)176 bool EffectRewinder::rewind (
177     const ::boost::shared_ptr<ScreenUpdater::UpdateLock>& rpPaintLock,
178     const ::boost::function<void(void)>& rSlideRewindFunctor,
179     const ::boost::function<void(void)>& rPreviousSlideFunctor)
180 {
181     mpPaintLock = rpPaintLock;
182 
183     // Do not allow nested rewinds.
184     if (mpAsynchronousRewindEvent)
185     {
186         OSL_ASSERT( ! mpAsynchronousRewindEvent);
187         return false;
188     }
189 
190     // Abort (and skip over the rest of) any currently active animation.
191     mrUserEventQueue.callSkipEffectEventHandler();
192     mrEventQueue.forceEmpty();
193 
194     const int nSkipCount (mnMainSequenceEffectCount - 1);
195     if (nSkipCount < 0)
196     {
197         if ( ! rPreviousSlideFunctor)
198         {
199             OSL_ASSERT(rPreviousSlideFunctor);
200             return false;
201         }
202 
203         // No main sequence effects to rewind on the current slide.
204         // Go back to the previous slide.
205         mpAsynchronousRewindEvent = makeEvent(
206             ::boost::bind(
207                 &EffectRewinder::asynchronousRewindToPreviousSlide,
208                 this,
209                 rPreviousSlideFunctor),
210             "EffectRewinder::asynchronousRewindToPreviousSlide");
211     }
212     else
213     {
214         // The actual rewinding is done asynchronously so that we can safely
215         // call other methods.
216         mpAsynchronousRewindEvent = makeEvent(
217             ::boost::bind(
218                 &EffectRewinder::asynchronousRewind,
219                 this,
220                 nSkipCount,
221                 true,
222                 rSlideRewindFunctor),
223             "EffectRewinder::asynchronousRewind");
224     }
225 
226     if (mpAsynchronousRewindEvent)
227         mrEventQueue.addEvent(mpAsynchronousRewindEvent);
228 
229     return mpAsynchronousRewindEvent.get()!=NULL;
230 }
231 
232 
233 
234 
skipAllMainSequenceEffects(void)235 void EffectRewinder::skipAllMainSequenceEffects (void)
236 {
237     // Do not allow nested rewinds.
238     if (mpAsynchronousRewindEvent)
239     {
240         OSL_ASSERT(!mpAsynchronousRewindEvent);
241         return;
242     }
243 
244     const int nTotalMainSequenceEffectCount (countMainSequenceEffects());
245     mpAsynchronousRewindEvent = makeEvent(
246         ::boost::bind(
247             &EffectRewinder::asynchronousRewind,
248             this,
249             nTotalMainSequenceEffectCount,
250             false,
251             ::boost::function<void(void)>()),
252         "EffectRewinder::asynchronousRewind");
253     mrEventQueue.addEvent(mpAsynchronousRewindEvent);
254 }
255 
256 
257 
258 
countMainSequenceEffects(void)259 sal_Int32 EffectRewinder::countMainSequenceEffects (void)
260 {
261     // Determine the number of main sequence effects.
262     sal_Int32 nMainSequenceNodeCount (0);
263 
264     ::std::queue<uno::Reference<animations::XAnimationNode> > aNodeQueue;
265     aNodeQueue.push(mxCurrentAnimationRootNode);
266     while ( ! aNodeQueue.empty())
267     {
268         const uno::Reference<animations::XAnimationNode> xNode (aNodeQueue.front());
269         aNodeQueue.pop();
270 
271         // Does the current node belong to the main sequence?
272         if (xNode.is())
273         {
274             animations::Event aEvent;
275             if (xNode->getBegin() >>= aEvent)
276                 if (aEvent.Trigger == animations::EventTrigger::ON_NEXT)
277                     ++nMainSequenceNodeCount;
278         }
279 
280         // If the current node is a container then prepare its children for investigation.
281         uno::Reference<container::XEnumerationAccess> xEnumerationAccess (xNode, uno::UNO_QUERY);
282         if (xEnumerationAccess.is())
283         {
284             uno::Reference<container::XEnumeration> xEnumeration (
285                 xEnumerationAccess->createEnumeration());
286             if (xEnumeration.is())
287                 while (xEnumeration->hasMoreElements())
288                 {
289                     aNodeQueue.push(
290                         uno::Reference<animations::XAnimationNode>(
291                             xEnumeration->nextElement(), uno::UNO_QUERY));
292                 }
293         }
294     }
295 
296     return nMainSequenceNodeCount;
297 
298     //    // Skip all main sequence nodes.
299     //    SkipSomeMainSequenceEffects(nMainSequenceNodeCount);
300 }
301 
302 
303 
304 
skipSomeMainSequenceEffects(sal_Int32 nSkipCount)305 void EffectRewinder::skipSomeMainSequenceEffects (sal_Int32 nSkipCount)
306 {
307     while (--nSkipCount >= 0)
308         skipSingleMainSequenceEffects();
309 }
310 
311 
312 
313 
skipSingleMainSequenceEffects(void)314 void EffectRewinder::skipSingleMainSequenceEffects (void)
315 {
316     // This basically just starts the next effect and then skips over its
317     // animation.
318     mrEventMultiplexer.notifyNextEffect();
319     mrEventQueue.forceEmpty();
320     mrUserEventQueue.callSkipEffectEventHandler();
321     mrEventQueue.forceEmpty();
322 }
323 
324 
325 
326 
resetEffectCount(void)327 bool EffectRewinder::resetEffectCount (void)
328 {
329     mnMainSequenceEffectCount = 0;
330     return false;
331 }
332 
333 
334 
335 
notifyAnimationStart(const AnimationNodeSharedPtr & rpNode)336 bool EffectRewinder::notifyAnimationStart (const AnimationNodeSharedPtr& rpNode)
337 {
338     // This notification is only relevant for us when the rpNode belongs to
339     // the main sequence.
340     BaseNodeSharedPtr pBaseNode (::boost::dynamic_pointer_cast<BaseNode>(rpNode));
341     if ( ! pBaseNode)
342         return false;
343 
344     BaseContainerNodeSharedPtr pParent (pBaseNode->getParentNode());
345     if ( ! (pParent && pParent->isMainSequenceRootNode()))
346         return false;
347 
348     // This notification is only relevant for us when the effect is user
349     // triggered.
350     bool bIsUserTriggered (false);
351 
352     Reference<animations::XAnimationNode> xNode (rpNode->getXAnimationNode());
353     if (xNode.is())
354     {
355         animations::Event aEvent;
356         if ((xNode->getBegin() >>= aEvent))
357             bIsUserTriggered = (aEvent.Trigger == animations::EventTrigger::ON_NEXT);
358     }
359 
360     if (bIsUserTriggered)
361         ++mnMainSequenceEffectCount;
362     else
363         mbNonUserTriggeredMainSequenceEffectSeen = true;
364 
365     return false;
366 }
367 
368 
369 
370 
asynchronousRewind(sal_Int32 nEffectCount,const bool bRedisplayCurrentSlide,const boost::function<void (void)> & rSlideRewindFunctor)371 void EffectRewinder::asynchronousRewind (
372     sal_Int32 nEffectCount,
373     const bool bRedisplayCurrentSlide,
374     const boost::function<void(void)>& rSlideRewindFunctor)
375 {
376     OSL_ASSERT(mpAsynchronousRewindEvent);
377 
378     if (bRedisplayCurrentSlide)
379     {
380         mpPaintLock->Activate();
381         // Re-display the current slide.
382         if (rSlideRewindFunctor)
383             rSlideRewindFunctor();
384         mpAsynchronousRewindEvent = makeEvent(
385             ::boost::bind(
386                 &EffectRewinder::asynchronousRewind,
387                 this,
388                 nEffectCount,
389                 false,
390                 rSlideRewindFunctor),
391             "EffectRewinder::asynchronousRewind");
392         mrEventQueue.addEvent(mpAsynchronousRewindEvent);
393     }
394     else
395     {
396         // Process initial events and skip any animations that are started
397         // when the slide is shown.
398         mbNonUserTriggeredMainSequenceEffectSeen = false;
399         mrEventQueue.forceEmpty();
400         if (mbNonUserTriggeredMainSequenceEffectSeen)
401         {
402             mrUserEventQueue.callSkipEffectEventHandler();
403             mrEventQueue.forceEmpty();
404         }
405 
406         while (--nEffectCount >= 0)
407             skipSingleMainSequenceEffects();
408 
409         mpAsynchronousRewindEvent.reset();
410         mpPaintLock.reset();
411     }
412 }
413 
414 
415 
416 
asynchronousRewindToPreviousSlide(const::boost::function<void (void)> & rSlideRewindFunctor)417 void EffectRewinder::asynchronousRewindToPreviousSlide (
418     const ::boost::function<void(void)>& rSlideRewindFunctor)
419 {
420     OSL_ASSERT(mpAsynchronousRewindEvent);
421 
422     mpAsynchronousRewindEvent.reset();
423     rSlideRewindFunctor();
424 }
425 
426 
427 
428 
429 } } // end of namespace ::slideshow::internal
430