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