1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "effectrewinder.hxx"
22 #include <eventqueue.hxx>
23 #include <usereventqueue.hxx>
24 #include <mouseeventhandler.hxx>
25 #include <basecontainernode.hxx>
26 #include <delayevent.hxx>
28 #include <com/sun/star/awt/MouseEvent.hpp>
29 #include <com/sun/star/animations/Event.hpp>
30 #include <com/sun/star/animations/EventTrigger.hpp>
31 #include <com/sun/star/container/XEnumerationAccess.hpp>
32 #include <com/sun/star/animations/XAnimate.hpp>
34 #include <officecfg/Office/Canvas.hxx>
36 using ::com::sun::star::uno::Reference
;
37 using namespace ::com::sun::star
;
39 namespace slideshow
{ namespace internal
{
44 class RewinderEventHandler
: public EventHandler
47 typedef ::std::function
<bool ()> Action
;
48 explicit RewinderEventHandler (const Action
& rAction
) : maAction(rAction
) {}
51 const Action maAction
;
52 virtual bool handleEvent() override
{ return maAction(); }
56 class RewinderAnimationEventHandler
: public AnimationEventHandler
59 typedef ::std::function
<bool (const AnimationNodeSharedPtr
& rpNode
)> Action
;
60 explicit RewinderAnimationEventHandler (const Action
& rAction
) : maAction(rAction
) {}
63 const Action maAction
;
64 virtual bool handleAnimationEvent (const AnimationNodeSharedPtr
& rpNode
) override
65 { return maAction(rpNode
); }
69 } // end of anonymous namespace
72 //----- EffectRewinder --------------------------------------------------------------
74 EffectRewinder::EffectRewinder (
75 EventMultiplexer
& rEventMultiplexer
,
76 EventQueue
& rEventQueue
,
77 UserEventQueue
& rUserEventQueue
)
78 : mrEventMultiplexer(rEventMultiplexer
),
79 mrEventQueue(rEventQueue
),
80 mrUserEventQueue(rUserEventQueue
),
81 mpSlideStartHandler(),
83 mpAnimationStartHandler(),
84 mnMainSequenceEffectCount(0),
85 mpAsynchronousRewindEvent(),
86 mxCurrentAnimationRootNode(),
87 mbNonUserTriggeredMainSequenceEffectSeen(false)
93 void EffectRewinder::initialize()
95 // Add some event handlers so that we are informed when
96 // a) an animation is started (we then check whether that belongs to a
97 // main sequence effect and if so, increase the respective counter),
98 // b,c) a slide was started or ended (in which case the effect counter
101 mpAnimationStartHandler
.reset(
102 new RewinderAnimationEventHandler(
103 [this]( const AnimationNodeSharedPtr
& pNode
)
104 { return this->notifyAnimationStart( pNode
); } ) );
105 mrEventMultiplexer
.addAnimationStartHandler(mpAnimationStartHandler
);
107 mpSlideStartHandler
.reset(
108 new RewinderEventHandler(
109 [this]() { return this->resetEffectCount(); } ) );
110 mrEventMultiplexer
.addSlideStartHandler(mpSlideStartHandler
);
112 mpSlideEndHandler
.reset(
113 new RewinderEventHandler(
114 [this]() { return this->resetEffectCount(); } ) );
115 mrEventMultiplexer
.addSlideEndHandler(mpSlideEndHandler
);
119 EffectRewinder::~EffectRewinder()
125 void EffectRewinder::dispose()
127 if (mpAsynchronousRewindEvent
)
129 mpAsynchronousRewindEvent
->dispose();
130 mpAsynchronousRewindEvent
.reset();
133 if (mpAnimationStartHandler
)
135 mrEventMultiplexer
.removeAnimationStartHandler(mpAnimationStartHandler
);
136 mpAnimationStartHandler
.reset();
139 if (mpSlideStartHandler
)
141 mrEventMultiplexer
.removeSlideStartHandler(mpSlideStartHandler
);
142 mpSlideStartHandler
.reset();
145 if (mpSlideEndHandler
)
147 mrEventMultiplexer
.removeSlideEndHandler(mpSlideEndHandler
);
148 mpSlideEndHandler
.reset();
153 void EffectRewinder::setRootAnimationNode (
154 const uno::Reference
<animations::XAnimationNode
>& xRootNode
)
156 mxCurrentAnimationRootNode
= xRootNode
;
160 bool EffectRewinder::rewind (
161 const ::std::shared_ptr
<ScreenUpdater::UpdateLock
>& rpPaintLock
,
162 const ::std::function
<void ()>& rSlideRewindFunctor
,
163 const ::std::function
<void ()>& rPreviousSlideFunctor
)
165 mpPaintLock
= rpPaintLock
;
167 // Do not allow nested rewinds.
168 if (mpAsynchronousRewindEvent
)
170 OSL_ASSERT( ! mpAsynchronousRewindEvent
);
174 // Abort (and skip over the rest of) any currently active animation.
175 mrUserEventQueue
.callSkipEffectEventHandler();
177 const int nSkipCount (mnMainSequenceEffectCount
- 1);
180 if ( ! rPreviousSlideFunctor
)
182 OSL_ASSERT(rPreviousSlideFunctor
);
186 // No main sequence effects to rewind on the current slide.
187 // Go back to the previous slide.
188 mpAsynchronousRewindEvent
= makeEvent(
190 &EffectRewinder::asynchronousRewindToPreviousSlide
,
192 rPreviousSlideFunctor
),
193 "EffectRewinder::asynchronousRewindToPreviousSlide");
197 // The actual rewinding is done asynchronously so that we can safely
198 // call other methods.
199 mpAsynchronousRewindEvent
= makeEvent(
201 &EffectRewinder::asynchronousRewind
,
205 rSlideRewindFunctor
),
206 "EffectRewinder::asynchronousRewind");
209 if (mpAsynchronousRewindEvent
)
210 mrEventQueue
.addEvent(mpAsynchronousRewindEvent
);
212 return mpAsynchronousRewindEvent
.get()!=nullptr;
216 void EffectRewinder::skipAllMainSequenceEffects()
218 // Do not allow nested rewinds.
219 if (mpAsynchronousRewindEvent
)
221 OSL_ASSERT(!mpAsynchronousRewindEvent
);
225 const int nTotalMainSequenceEffectCount (countMainSequenceEffects());
226 mpAsynchronousRewindEvent
= makeEvent(
228 &EffectRewinder::asynchronousRewind
,
230 nTotalMainSequenceEffectCount
,
232 ::std::function
<void ()>()),
233 "EffectRewinder::asynchronousRewind");
234 mrEventQueue
.addEvent(mpAsynchronousRewindEvent
);
238 sal_Int32
EffectRewinder::countMainSequenceEffects()
240 // Determine the number of main sequence effects.
241 sal_Int32
nMainSequenceNodeCount (0);
243 ::std::queue
<uno::Reference
<animations::XAnimationNode
> > aNodeQueue
;
244 aNodeQueue
.push(mxCurrentAnimationRootNode
);
245 while ( ! aNodeQueue
.empty())
247 const uno::Reference
<animations::XAnimationNode
> xNode (aNodeQueue
.front());
250 // Does the current node belong to the main sequence?
253 animations::Event aEvent
;
254 if (xNode
->getBegin() >>= aEvent
)
255 if (aEvent
.Trigger
== animations::EventTrigger::ON_NEXT
)
256 ++nMainSequenceNodeCount
;
259 // If the current node is a container then prepare its children for investigation.
260 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess (xNode
, uno::UNO_QUERY
);
261 if (xEnumerationAccess
.is())
263 uno::Reference
<container::XEnumeration
> xEnumeration (
264 xEnumerationAccess
->createEnumeration());
265 if (xEnumeration
.is())
266 while (xEnumeration
->hasMoreElements())
269 uno::Reference
<animations::XAnimationNode
>(
270 xEnumeration
->nextElement(), uno::UNO_QUERY
));
275 return nMainSequenceNodeCount
;
279 void EffectRewinder::skipSingleMainSequenceEffects()
281 // This basically just starts the next effect and then skips over its
283 mrEventMultiplexer
.notifyNextEffect();
284 mrEventQueue
.forceEmpty();
285 mrUserEventQueue
.callSkipEffectEventHandler();
286 mrEventQueue
.forceEmpty();
290 bool EffectRewinder::resetEffectCount()
292 mnMainSequenceEffectCount
= 0;
296 bool EffectRewinder::hasBlockedAnimation( const css::uno::Reference
<css::animations::XAnimationNode
>& xNode
)
298 bool isShapeTarget
= false;;
300 OUString preset_sub_type
;
301 OUString preset_property
;
303 if (xNode
->getUserData().getLength())
305 for(int i
= 0; i
< xNode
->getUserData().getLength(); i
++)
307 if(xNode
->getUserData()[i
].Name
== "preset-id")
308 xNode
->getUserData()[i
].Value
>>= preset_id
;
309 if(xNode
->getUserData()[i
].Name
== "preset-sub-type")
310 xNode
->getUserData()[i
].Value
>>= preset_sub_type
;
311 if(xNode
->getUserData()[i
].Name
== "preset-property")
312 xNode
->getUserData()[i
].Value
>>= preset_property
;
316 uno::Reference
<container::XEnumerationAccess
> xEnumerationAccess (xNode
, uno::UNO_QUERY
);
317 if (xEnumerationAccess
.is())
319 uno::Reference
<container::XEnumeration
> xEnumeration (
320 xEnumerationAccess
->createEnumeration());
321 if (xEnumeration
.is())
322 while (xEnumeration
->hasMoreElements())
324 uno::Reference
<animations::XAnimationNode
> xNext (xEnumeration
->nextElement(), uno::UNO_QUERY
);
325 uno::Reference
<animations::XAnimate
> xAnimate( xNext
, uno::UNO_QUERY
);
329 uno::Reference
< drawing::XShape
> xShape( xAnimate
->getTarget(), uno::UNO_QUERY
);
331 if (xShape
.is() || xAnimate
->getTarget().getValueType() == cppu::UnoType
<void>::get())
338 ((preset_id
== "ooo-entrance-zoom" && preset_sub_type
== "in") || // Entrance Zoom In
339 (preset_id
== "ooo-entrance-swivel" ) || // Entrance Swivel
340 (preset_id
== "ooo-entrance-spiral-in") || // Entrance Spiral-In
341 (preset_id
== "ooo-entrance-stretchy"))) // Entrance Stretchy
347 bool EffectRewinder::notifyAnimationStart (const AnimationNodeSharedPtr
& rpNode
)
349 Reference
<animations::XAnimationNode
> xNode (rpNode
->getXAnimationNode());
352 !officecfg::Office::Canvas::ForceSafeServiceImpl::get() &&
353 hasBlockedAnimation(xNode
) )
354 skipSingleMainSequenceEffects();
356 // This notification is only relevant for us when the rpNode belongs to
357 // the main sequence.
358 BaseNodeSharedPtr
pBaseNode (::std::dynamic_pointer_cast
<BaseNode
>(rpNode
));
362 BaseContainerNodeSharedPtr
pParent (pBaseNode
->getParentNode());
363 if ( ! (pParent
&& pParent
->isMainSequenceRootNode()))
366 // This notification is only relevant for us when the effect is user
368 bool bIsUserTriggered (false);
372 animations::Event aEvent
;
373 if (xNode
->getBegin() >>= aEvent
)
374 bIsUserTriggered
= (aEvent
.Trigger
== animations::EventTrigger::ON_NEXT
);
377 if (bIsUserTriggered
)
378 ++mnMainSequenceEffectCount
;
380 mbNonUserTriggeredMainSequenceEffectSeen
= true;
386 void EffectRewinder::asynchronousRewind (
387 sal_Int32 nEffectCount
,
388 const bool bRedisplayCurrentSlide
,
389 const std::function
<void ()>& rSlideRewindFunctor
)
391 OSL_ASSERT(mpAsynchronousRewindEvent
);
393 if (bRedisplayCurrentSlide
)
395 mpPaintLock
->Activate();
396 // Re-display the current slide.
397 if (rSlideRewindFunctor
)
398 rSlideRewindFunctor();
399 mpAsynchronousRewindEvent
= makeEvent(
401 &EffectRewinder::asynchronousRewind
,
405 rSlideRewindFunctor
),
406 "EffectRewinder::asynchronousRewind");
407 mrEventQueue
.addEvent(mpAsynchronousRewindEvent
);
411 // Process initial events and skip any animations that are started
412 // when the slide is shown.
413 mbNonUserTriggeredMainSequenceEffectSeen
= false;
415 if (mbNonUserTriggeredMainSequenceEffectSeen
)
417 mrUserEventQueue
.callSkipEffectEventHandler();
418 mrEventQueue
.forceEmpty();
421 while (--nEffectCount
>= 0)
422 skipSingleMainSequenceEffects();
424 mpAsynchronousRewindEvent
.reset();
430 void EffectRewinder::asynchronousRewindToPreviousSlide (
431 const ::std::function
<void ()>& rSlideRewindFunctor
)
433 OSL_ASSERT(mpAsynchronousRewindEvent
);
435 mpAsynchronousRewindEvent
.reset();
436 rSlideRewindFunctor();
440 } } // end of namespace ::slideshow::internal
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */