1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21 #include <config_features.h>
22 #include <comphelper/diagnose_ex.hxx>
23
24 #include <cppuhelper/basemutex.hxx>
25 #include <cppuhelper/compbase.hxx>
26 #include <cppuhelper/interfacecontainer.h>
27 #include <cppuhelper/supportsservice.hxx>
28
29 #include <comphelper/interfacecontainer3.hxx>
30 #include <comphelper/scopeguard.hxx>
31 #include <comphelper/storagehelper.hxx>
32 #include <cppcanvas/polypolygon.hxx>
33 #include <osl/thread.hxx>
34
35 #include <tools/debug.hxx>
36
37 #include <basegfx/point/b2dpoint.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <basegfx/utils/canvastools.hxx>
41 #include <basegfx/matrix/b2dhommatrix.hxx>
42 #include <basegfx/matrix/b2dhommatrixtools.hxx>
43
44 #include <sal/log.hxx>
45
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <com/sun/star/util/XUpdatable.hpp>
48 #include <com/sun/star/awt/SystemPointer.hpp>
49 #include <com/sun/star/presentation/XSlideShow.hpp>
50 #include <com/sun/star/presentation/XSlideShowNavigationListener.hpp>
51 #include <com/sun/star/lang/NoSupportException.hpp>
52 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
53 #include <com/sun/star/lang/XServiceInfo.hpp>
54 #include <com/sun/star/drawing/LineCap.hpp>
55 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
56 #include <com/sun/star/drawing/PointSequence.hpp>
57 #include <com/sun/star/drawing/XLayer.hpp>
58 #include <com/sun/star/drawing/XLayerSupplier.hpp>
59 #include <com/sun/star/drawing/XLayerManager.hpp>
60 #include <com/sun/star/container/XNameAccess.hpp>
61 #include <com/sun/star/document/XStorageBasedDocument.hpp>
62
63 #include <com/sun/star/uno/Reference.hxx>
64 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
65
66 #include <unoviewcontainer.hxx>
67 #include <transitionfactory.hxx>
68 #include <eventmultiplexer.hxx>
69 #include <usereventqueue.hxx>
70 #include <eventqueue.hxx>
71 #include <cursormanager.hxx>
72 #include <mediafilemanager.hxx>
73 #include <slideshowcontext.hxx>
74 #include <activitiesqueue.hxx>
75 #include <activitiesfactory.hxx>
76 #include <interruptabledelayevent.hxx>
77 #include <slide.hxx>
78 #include <shapemaps.hxx>
79 #include <slideview.hxx>
80 #include <tools.hxx>
81 #include <unoview.hxx>
82 #include <vcl/virdev.hxx>
83 #include "rehearsetimingsactivity.hxx"
84 #include "waitsymbol.hxx"
85 #include "effectrewinder.hxx"
86 #include <framerate.hxx>
87 #include "pointersymbol.hxx"
88 #include "slideoverlaybutton.hxx"
89
90 #include <map>
91 #include <thread>
92 #include <utility>
93 #include <vector>
94 #include <algorithm>
95
96 using namespace com::sun::star;
97 using namespace ::slideshow::internal;
98
99 namespace box2d::utils { class box2DWorld;
100 typedef ::std::shared_ptr< box2DWorld > Box2DWorldSharedPtr; }
101
102 namespace {
103
104 /** During animations the update() method tells its caller to call it as
105 soon as possible. This gives us more time to render the next frame and
106 still maintain a steady frame rate. This class is responsible for
107 synchronizing the display of new frames and thus keeping the frame rate
108 steady.
109 */
110 class FrameSynchronization
111 {
112 public:
113 /** Create new object with a predefined duration between two frames.
114 @param nFrameDuration
115 The preferred duration between the display of two frames in
116 seconds.
117 */
118 explicit FrameSynchronization (const double nFrameDuration);
119
120 /** Set the current time as the time at which the current frame is
121 displayed. From this the target time of the next frame is derived.
122 */
123 void MarkCurrentFrame();
124
125 /** When there is time left until the next frame is due then wait.
126 Otherwise return without delay.
127 */
128 void Synchronize();
129
130 /** Activate frame synchronization when an animation is active and
131 frames are to be displayed in a steady rate. While active
132 Synchronize() will wait until the frame duration time has passed.
133 */
134 void Activate();
135
136 /** Deactivate frame synchronization when no animation is active and the
137 time between frames depends on user actions and other external
138 sources. While deactivated Synchronize() will return without delay.
139 */
140 void Deactivate();
141
142 private:
143 /** The timer that is used for synchronization is independent from the
144 one used by SlideShowImpl: it is not paused or modified by
145 animations.
146 */
147 canvastools::ElapsedTime maTimer;
148 /** Time between the display of frames. Enforced only when mbIsActive
149 is <TRUE/>.
150 */
151 const double mnFrameDuration;
152 /** Time (of maTimer) when the next frame shall be displayed.
153 Synchronize() will wait until this time.
154 */
155 double mnNextFrameTargetTime;
156 /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise
157 it returns immediately.
158 */
159 bool mbIsActive;
160 };
161
162 /******************************************************************************
163
164 SlideShowImpl
165
166 This class encapsulates the slideshow presentation viewer.
167
168 With an instance of this class, it is possible to statically
169 and dynamically show a presentation, as defined by the
170 constructor-provided draw model (represented by a sequence
171 of css::drawing::XDrawPage objects).
172
173 It is possible to show the presentation on multiple views
174 simultaneously (e.g. for a multi-monitor setup). Since this
175 class also relies on user interaction, the corresponding
176 XSlideShowView interface provides means to register some UI
177 event listeners (mostly borrowed from awt::XWindow interface).
178
179 Since currently (mid 2004), OOo isn't very well suited to
180 multi-threaded rendering, this class relies on <em>very
181 frequent</em> external update() calls, which will render the
182 next frame of animations. This works as follows: after the
183 displaySlide() has been successfully called (which setup and
184 starts an actual slide show), the update() method must be
185 called until it returns false.
186 Effectively, this puts the burden of providing
187 concurrency to the clients of this class, which, as noted
188 above, is currently unavoidable with the current state of
189 affairs (I've actually tried threading here, but failed
190 miserably when using the VCL canvas as the render backend -
191 deadlocked).
192
193 ******************************************************************************/
194
195 typedef cppu::WeakComponentImplHelper<css::lang::XServiceInfo, presentation::XSlideShow> SlideShowImplBase;
196
197 typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
198
199 /// Maps XDrawPage for annotations persistence
200 typedef ::std::map< css::uno::Reference<
201 css::drawing::XDrawPage>,
202 PolyPolygonVector> PolygonMap;
203
204 class SlideShowImpl : private cppu::BaseMutex,
205 public CursorManager,
206 public MediaFileManager,
207 public SlideShowImplBase
208 {
209 public:
210 explicit SlideShowImpl(
211 uno::Reference<uno::XComponentContext> xContext );
212
213 /** Notify that the transition phase of the current slide
214 has ended.
215
216 The life of a slide has three phases: the transition
217 phase, when the previous slide vanishes, and the
218 current slide becomes visible, the shape animation
219 phase, when shape effects are running, and the phase
220 after the last shape animation has ended, but before
221 the next slide transition starts.
222
223 This method notifies the end of the first phase.
224
225 @param bPaintSlide
226 When true, Slide::show() is passed a true as well, denoting
227 explicit paint of slide content. Pass false here, if e.g. a
228 slide transition has already rendered the initial slide image.
229 */
230 void notifySlideTransitionEnded( bool bPaintSlide );
231
232 /** Notify that the shape animation phase of the current slide
233 has ended.
234
235 The life of a slide has three phases: the transition
236 phase, when the previous slide vanishes, and the
237 current slide becomes visible, the shape animation
238 phase, when shape effects are running, and the phase
239 after the last shape animation has ended, but before
240 the next slide transition starts.
241
242 This method notifies the end of the second phase.
243 */
244 void notifySlideAnimationsEnded();
245
246 /** Notify that the slide has ended.
247
248 The life of a slide has three phases: the transition
249 phase, when the previous slide vanishes, and the
250 current slide becomes visible, the shape animation
251 phase, when shape effects are running, and the phase
252 after the last shape animation has ended, but before
253 the next slide transition starts.
254
255 This method notifies the end of the third phase.
256 */
257 void notifySlideEnded (const bool bReverse);
258
259 /** Notification from eventmultiplexer that a hyperlink
260 has been clicked.
261 */
262 bool notifyHyperLinkClicked( OUString const& hyperLink );
263
264 /** Notification from eventmultiplexer that an animation event has occurred.
265 This will be forwarded to all registered XSlideShowListener
266 */
267 bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
268
269 /** Obtain a MediaTempFile for the specified url. */
270 virtual std::shared_ptr<avmedia::MediaTempFile> getMediaTempFile(const OUString& aUrl) override;
271
272 private:
273 // XServiceInfo
274 virtual OUString SAL_CALL getImplementationName() override;
275 virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
276 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
277
278 // XSlideShow:
279 virtual sal_Bool SAL_CALL nextEffect() override;
280 virtual sal_Bool SAL_CALL previousEffect() override;
281 virtual sal_Bool SAL_CALL startShapeActivity(
282 uno::Reference<drawing::XShape> const& xShape ) override;
283 virtual sal_Bool SAL_CALL stopShapeActivity(
284 uno::Reference<drawing::XShape> const& xShape ) override;
285 virtual sal_Bool SAL_CALL pause( sal_Bool bPauseShow ) override;
286 virtual uno::Reference<drawing::XDrawPage> SAL_CALL getCurrentSlide() override;
287 virtual void SAL_CALL displaySlide(
288 uno::Reference<drawing::XDrawPage> const& xSlide,
289 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
290 uno::Reference<animations::XAnimationNode> const& xRootNode,
291 uno::Sequence<beans::PropertyValue> const& rProperties ) override;
292 virtual void SAL_CALL registerUserPaintPolygons( const css::uno::Reference< css::lang::XMultiServiceFactory >& xDocFactory ) override;
293 virtual sal_Bool SAL_CALL setProperty(
294 beans::PropertyValue const& rProperty ) override;
295 virtual sal_Bool SAL_CALL addView(
296 uno::Reference<presentation::XSlideShowView> const& xView ) override;
297 virtual sal_Bool SAL_CALL removeView(
298 uno::Reference<presentation::XSlideShowView> const& xView ) override;
299 virtual sal_Bool SAL_CALL update( double & nNextTimeout ) override;
300 virtual void SAL_CALL addSlideShowListener(
301 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
302 virtual void SAL_CALL removeSlideShowListener(
303 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
304 virtual void SAL_CALL addShapeEventListener(
305 uno::Reference<presentation::XShapeEventListener> const& xListener,
306 uno::Reference<drawing::XShape> const& xShape ) override;
307 virtual void SAL_CALL removeShapeEventListener(
308 uno::Reference<presentation::XShapeEventListener> const& xListener,
309 uno::Reference<drawing::XShape> const& xShape ) override;
310 virtual void SAL_CALL setShapeCursor(
311 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape ) override;
312
313 // CursorManager
314
315
316 virtual bool requestCursor( sal_Int16 nCursorShape ) override;
317 virtual void resetCursor() override;
318
319 /** This is somewhat similar to displaySlide when called for the current
320 slide. It has been simplified to take advantage of that no slide
321 change takes place. Furthermore it does not show the slide
322 transition.
323 */
324 void redisplayCurrentSlide();
325
326 protected:
327 // WeakComponentImplHelperBase
328 virtual void SAL_CALL disposing() override;
329
isDisposed() const330 bool isDisposed() const
331 {
332 return (rBHelper.bDisposed || rBHelper.bInDispose);
333 }
334
335 private:
336 struct SeparateListenerImpl; friend struct SeparateListenerImpl;
337 class PrefetchPropertiesFunc; friend class PrefetchPropertiesFunc;
338
339 /// Stop currently running show.
340 void stopShow();
341
342 ///Find a polygons vector in maPolygons (map)
343 PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
344
345 /// Creates a new slide.
346 SlideSharedPtr makeSlide(
347 uno::Reference<drawing::XDrawPage> const& xDrawPage,
348 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
349 uno::Reference<animations::XAnimationNode> const& xRootNode );
350
351 /// Checks whether the given slide/animation node matches mpPrefetchSlide
matches(SlideSharedPtr const & pSlide,uno::Reference<drawing::XDrawPage> const & xSlide,uno::Reference<animations::XAnimationNode> const & xNode)352 static bool matches(
353 SlideSharedPtr const& pSlide,
354 uno::Reference<drawing::XDrawPage> const& xSlide,
355 uno::Reference<animations::XAnimationNode> const& xNode )
356 {
357 if (pSlide)
358 return (pSlide->getXDrawPage() == xSlide &&
359 pSlide->getXAnimationNode() == xNode);
360 else
361 return (!xSlide.is() && !xNode.is());
362 }
363
364 /// Resets the current slide transition sound object with a new one:
365 SoundPlayerSharedPtr resetSlideTransitionSound(
366 uno::Any const& url, bool bLoopSound );
367
368 /// stops the current slide transition sound
369 void stopSlideTransitionSound();
370
371 /** Prepare a slide transition
372
373 This method registers all necessary events and
374 activities for a slide transition.
375
376 @return the slide change activity, or NULL for no transition effect
377 */
378 ActivitySharedPtr createSlideTransition(
379 const uno::Reference< drawing::XDrawPage >& xDrawPage,
380 const SlideSharedPtr& rLeavingSlide,
381 const SlideSharedPtr& rEnteringSlide,
382 const EventSharedPtr& rTransitionEndEvent );
383
384 /** Request/release the wait symbol. The wait symbol is displayed when
385 there are more requests then releases. Locking the wait symbol
386 helps to avoid intermediate repaints.
387
388 Do not call this method directly. Use WaitSymbolLock instead.
389 */
390 void requestWaitSymbol();
391 void releaseWaitSymbol();
392
393 class WaitSymbolLock {public:
WaitSymbolLock(SlideShowImpl & rSlideShowImpl)394 explicit WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
395 { mrSlideShowImpl.requestWaitSymbol(); }
~WaitSymbolLock()396 ~WaitSymbolLock()
397 { mrSlideShowImpl.releaseWaitSymbol(); }
398 private: SlideShowImpl& mrSlideShowImpl;
399 };
400
401 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
402 sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
403
404 /** This method is called asynchronously to finish the rewinding of an
405 effect to the previous slide that was initiated earlier.
406 */
407 void rewindEffectToPreviousSlide();
408
409 /// all registered views
410 UnoViewContainer maViewContainer;
411
412 /// all registered slide show listeners
413 comphelper::OInterfaceContainerHelper3<presentation::XSlideShowListener> maListenerContainer;
414
415 /// map of vectors, containing all registered listeners for a shape
416 ShapeEventListenerMap maShapeEventListeners;
417 /// map of sal_Int16 values, specifying the mouse cursor for every shape
418 ShapeCursorMap maShapeCursors;
419
420 //map of vector of Polygons, containing polygons drawn on each slide.
421 PolygonMap maPolygons;
422
423 std::optional<RGBColor> maUserPaintColor;
424
425 double maUserPaintStrokeWidth;
426
427 //changed for the eraser project
428 std::optional<bool> maEraseAllInk;
429 std::optional<sal_Int32> maEraseInk;
430 //end changed
431
432 std::shared_ptr<canvastools::ElapsedTime> mpPresTimer;
433 ScreenUpdater maScreenUpdater;
434 EventQueue maEventQueue;
435 EventMultiplexer maEventMultiplexer;
436 ActivitiesQueue maActivitiesQueue;
437 UserEventQueue maUserEventQueue;
438 SubsettableShapeManagerSharedPtr mpDummyPtr;
439 box2d::utils::Box2DWorldSharedPtr mpBox2DDummyPtr;
440
441 std::shared_ptr<SeparateListenerImpl> mpListener;
442
443 std::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
444 std::shared_ptr<WaitSymbol> mpWaitSymbol;
445
446 // navigation buttons
447 std::shared_ptr<SlideOverlayButton> mpNavigationPrev;
448 std::shared_ptr<SlideOverlayButton> mpNavigationMenu;
449 std::shared_ptr<SlideOverlayButton> mpNavigationNext;
450
451 std::shared_ptr<PointerSymbol> mpPointerSymbol;
452
453 /// the current slide transition sound object:
454 SoundPlayerSharedPtr mpCurrentSlideTransitionSound;
455
456 uno::Reference<uno::XComponentContext> mxComponentContext;
457 uno::Reference<
458 presentation::XTransitionFactory> mxOptionalTransitionFactory;
459
460 /// the previously running slide
461 SlideSharedPtr mpPreviousSlide;
462 /// the currently running slide
463 SlideSharedPtr mpCurrentSlide;
464 /// the already prefetched slide: best candidate for upcoming slide
465 SlideSharedPtr mpPrefetchSlide;
466 /// slide to be prefetched: best candidate for upcoming slide
467 uno::Reference<drawing::XDrawPage> mxPrefetchSlide;
468 /// save the XDrawPagesSupplier to retrieve polygons
469 uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier;
470 /// Used by MediaFileManager, for media files with package url.
471 uno::Reference<document::XStorageBasedDocument> mxSBD;
472 /// slide animation to be prefetched:
473 uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
474
475 sal_Int16 mnCurrentCursor;
476
477 sal_Int32 mnWaitSymbolRequestCount;
478 bool mbAutomaticAdvancementMode;
479 bool mbImageAnimationsAllowed;
480 bool mbNoSlideTransitions;
481 bool mbMouseVisible;
482 bool mbForceManualAdvance;
483 bool mbShowPaused;
484 bool mbSlideShowIdle;
485 bool mbDisableAnimationZOrder;
486 bool mbMovingForward;
487
488 EffectRewinder maEffectRewinder;
489 FrameSynchronization maFrameSynchronization;
490 };
491
492 /** Separate event listener for animation, view and hyperlink events.
493
494 This handler is registered for slide animation end, view and
495 hyperlink events at the global EventMultiplexer, and forwards
496 notifications to the SlideShowImpl
497 */
498 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
499 public ViewRepaintHandler,
500 public HyperlinkHandler,
501 public AnimationEventHandler
502 {
503 SlideShowImpl& mrShow;
504 ScreenUpdater& mrScreenUpdater;
505 EventQueue& mrEventQueue;
506
SeparateListenerImpl__anonc55c4f800111::SlideShowImpl::SeparateListenerImpl507 SeparateListenerImpl( SlideShowImpl& rShow,
508 ScreenUpdater& rScreenUpdater,
509 EventQueue& rEventQueue ) :
510 mrShow( rShow ),
511 mrScreenUpdater( rScreenUpdater ),
512 mrEventQueue( rEventQueue )
513 {}
514
515 SeparateListenerImpl( const SeparateListenerImpl& ) = delete;
516 SeparateListenerImpl& operator=( const SeparateListenerImpl& ) = delete;
517
518 // EventHandler
handleEvent__anonc55c4f800111::SlideShowImpl::SeparateListenerImpl519 virtual bool handleEvent() override
520 {
521 // DON't call notifySlideAnimationsEnded()
522 // directly, but queue an event. handleEvent()
523 // might be called from e.g.
524 // showNext(), and notifySlideAnimationsEnded() must not be called
525 // in recursion. Note that the event is scheduled for the next
526 // frame so that its expensive execution does not come in between
527 // sprite hiding and shape redraw (at the end of the animation of a
528 // shape), which would cause a flicker.
529 mrEventQueue.addEventForNextRound(
530 makeEvent( [this] () { this->mrShow.notifySlideAnimationsEnded(); },
531 u"SlideShowImpl::notifySlideAnimationsEnded"_ustr));
532 return true;
533 }
534
535 // ViewRepaintHandler
viewClobbered__anonc55c4f800111::SlideShowImpl::SeparateListenerImpl536 virtual void viewClobbered( const UnoViewSharedPtr& rView ) override
537 {
538 // given view needs repaint, request update
539 mrScreenUpdater.notifyUpdate(rView, true);
540 }
541
542 // HyperlinkHandler
handleHyperlink__anonc55c4f800111::SlideShowImpl::SeparateListenerImpl543 virtual bool handleHyperlink( OUString const& rLink ) override
544 {
545 return mrShow.notifyHyperLinkClicked(rLink);
546 }
547
548 // AnimationEventHandler
handleAnimationEvent__anonc55c4f800111::SlideShowImpl::SeparateListenerImpl549 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) override
550 {
551 return mrShow.handleAnimationEvent(rNode);
552 }
553 };
554
SlideShowImpl(uno::Reference<uno::XComponentContext> xContext)555 SlideShowImpl::SlideShowImpl(
556 uno::Reference<uno::XComponentContext> xContext )
557 : SlideShowImplBase(m_aMutex),
558 maViewContainer(),
559 maListenerContainer( m_aMutex ),
560 maShapeEventListeners(),
561 maShapeCursors(),
562 maUserPaintColor(),
563 maUserPaintStrokeWidth(4.0),
564 mpPresTimer( std::make_shared<canvastools::ElapsedTime>() ),
565 maScreenUpdater(maViewContainer),
566 maEventQueue( mpPresTimer ),
567 maEventMultiplexer( maEventQueue,
568 maViewContainer ),
569 maActivitiesQueue( mpPresTimer ),
570 maUserEventQueue( maEventMultiplexer,
571 maEventQueue,
572 *this ),
573 mpDummyPtr(),
574 mpBox2DDummyPtr(),
575 mpListener(),
576 mpRehearseTimingsActivity(),
577 mpWaitSymbol(),
578 mpPointerSymbol(),
579 mpCurrentSlideTransitionSound(),
580 mxComponentContext(std::move( xContext )),
581 mxOptionalTransitionFactory(),
582 mpCurrentSlide(),
583 mpPrefetchSlide(),
584 mxPrefetchSlide(),
585 mxDrawPagesSupplier(),
586 mxSBD(),
587 mxPrefetchAnimationNode(),
588 mnCurrentCursor(awt::SystemPointer::ARROW),
589 mnWaitSymbolRequestCount(0),
590 mbAutomaticAdvancementMode(false),
591 mbImageAnimationsAllowed( true ),
592 mbNoSlideTransitions( false ),
593 mbMouseVisible( true ),
594 mbForceManualAdvance( false ),
595 mbShowPaused( false ),
596 mbSlideShowIdle( true ),
597 mbDisableAnimationZOrder( false ),
598 mbMovingForward( true ),
599 maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue),
600 maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond)
601
602 {
603 // keep care not constructing any UNO references to this inside ctor,
604 // shift that code to create()!
605
606 uno::Reference<lang::XMultiComponentFactory> xFactory(
607 mxComponentContext->getServiceManager() );
608
609 if( xFactory.is() )
610 {
611 try
612 {
613 // #i82460# try to retrieve special transition factory
614 mxOptionalTransitionFactory.set(
615 xFactory->createInstanceWithContext(
616 u"com.sun.star.presentation.TransitionFactory"_ustr,
617 mxComponentContext ),
618 uno::UNO_QUERY );
619 }
620 catch (loader::CannotActivateFactoryException const&)
621 {
622 }
623 }
624
625 mpListener = std::make_shared<SeparateListenerImpl>(
626 *this,
627 maScreenUpdater,
628 maEventQueue );
629 maEventMultiplexer.addSlideAnimationsEndHandler( mpListener );
630 maEventMultiplexer.addViewRepaintHandler( mpListener );
631 maEventMultiplexer.addHyperlinkHandler( mpListener, 0.0 );
632 maEventMultiplexer.addAnimationStartHandler( mpListener );
633 maEventMultiplexer.addAnimationEndHandler( mpListener );
634 }
635
636 // we are about to be disposed (someone call dispose() on us)
disposing()637 void SlideShowImpl::disposing()
638 {
639 osl::MutexGuard const guard( m_aMutex );
640
641 maEffectRewinder.dispose();
642
643 // stop slide transition sound, if any:
644 stopSlideTransitionSound();
645
646 mxComponentContext.clear();
647
648 if( mpCurrentSlideTransitionSound )
649 {
650 mpCurrentSlideTransitionSound->dispose();
651 mpCurrentSlideTransitionSound.reset();
652 }
653
654 mpWaitSymbol.reset();
655 mpPointerSymbol.reset();
656
657 if( mpRehearseTimingsActivity )
658 {
659 mpRehearseTimingsActivity->dispose();
660 mpRehearseTimingsActivity.reset();
661 }
662
663 if( mpListener )
664 {
665 maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
666 maEventMultiplexer.removeViewRepaintHandler(mpListener);
667 maEventMultiplexer.removeHyperlinkHandler(mpListener);
668 maEventMultiplexer.removeAnimationStartHandler( mpListener );
669 maEventMultiplexer.removeAnimationEndHandler( mpListener );
670
671 mpListener.reset();
672 }
673
674 maUserEventQueue.clear();
675 maActivitiesQueue.clear();
676 maEventMultiplexer.clear();
677 maEventQueue.clear();
678 mpPresTimer.reset();
679 maShapeCursors.clear();
680 maShapeEventListeners.clear();
681
682 // send all listeners a disposing() that we are going down:
683 maListenerContainer.disposeAndClear(
684 lang::EventObject( getXWeak() ) );
685
686 maViewContainer.dispose();
687
688 // release slides:
689 mxPrefetchAnimationNode.clear();
690 mxPrefetchSlide.clear();
691 mpPrefetchSlide.reset();
692 mpCurrentSlide.reset();
693 mpPreviousSlide.reset();
694 }
695
getSupportedServiceNames()696 uno::Sequence< OUString > SAL_CALL SlideShowImpl::getSupportedServiceNames()
697 {
698 return { u"com.sun.star.presentation.SlideShow"_ustr };
699 }
700
getImplementationName()701 OUString SAL_CALL SlideShowImpl::getImplementationName()
702 {
703 return u"com.sun.star.comp.presentation.SlideShow"_ustr;
704 }
705
supportsService(const OUString & aServiceName)706 sal_Bool SAL_CALL SlideShowImpl::supportsService(const OUString& aServiceName)
707 {
708 return cppu::supportsService(this, aServiceName);
709 }
710
711 /// stops the current slide transition sound
stopSlideTransitionSound()712 void SlideShowImpl::stopSlideTransitionSound()
713 {
714 if (mpCurrentSlideTransitionSound)
715 {
716 mpCurrentSlideTransitionSound->stopPlayback();
717 mpCurrentSlideTransitionSound->dispose();
718 mpCurrentSlideTransitionSound.reset();
719 }
720 }
721
resetSlideTransitionSound(const uno::Any & rSound,bool bLoopSound)722 SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
723 {
724 bool bStopSound = false;
725 OUString url;
726
727 if( !(rSound >>= bStopSound) )
728 bStopSound = false;
729 rSound >>= url;
730
731 if( !bStopSound && url.isEmpty() )
732 return SoundPlayerSharedPtr();
733
734 stopSlideTransitionSound();
735
736 if (!url.isEmpty())
737 {
738 try
739 {
740 mpCurrentSlideTransitionSound = SoundPlayer::create(
741 maEventMultiplexer, url, mxComponentContext, *this);
742 mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
743 }
744 catch (lang::NoSupportException const&)
745 {
746 // catch possible exceptions from SoundPlayer, since
747 // being not able to playback the sound is not a hard
748 // error here (still, the slide transition should be
749 // shown).
750 }
751 }
752 return mpCurrentSlideTransitionSound;
753 }
754
createSlideTransition(const uno::Reference<drawing::XDrawPage> & xDrawPage,const SlideSharedPtr & rLeavingSlide,const SlideSharedPtr & rEnteringSlide,const EventSharedPtr & rTransitionEndEvent)755 ActivitySharedPtr SlideShowImpl::createSlideTransition(
756 const uno::Reference< drawing::XDrawPage >& xDrawPage,
757 const SlideSharedPtr& rLeavingSlide,
758 const SlideSharedPtr& rEnteringSlide,
759 const EventSharedPtr& rTransitionEndEvent)
760 {
761 ENSURE_OR_THROW( !maViewContainer.empty(),
762 "createSlideTransition(): No views" );
763 ENSURE_OR_THROW( rEnteringSlide,
764 "createSlideTransition(): No entering slide" );
765
766 // return empty transition, if slide transitions
767 // are disabled.
768 if (mbNoSlideTransitions)
769 return ActivitySharedPtr();
770
771 // retrieve slide change parameters from XDrawPage
772 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
773 uno::UNO_QUERY );
774
775 if( !xPropSet.is() )
776 {
777 SAL_INFO("slideshow", "createSlideTransition(): "
778 "Slide has no PropertySet - assuming no transition" );
779 return ActivitySharedPtr();
780 }
781
782 sal_Int16 nTransitionType(0);
783 if( !getPropertyValue( nTransitionType,
784 xPropSet,
785 u"TransitionType"_ustr) )
786 {
787 SAL_INFO("slideshow", "createSlideTransition(): "
788 "Could not extract slide transition type from XDrawPage - assuming no transition" );
789 return ActivitySharedPtr();
790 }
791
792 sal_Int16 nTransitionSubType(0);
793 if( !getPropertyValue( nTransitionSubType,
794 xPropSet,
795 u"TransitionSubtype"_ustr) )
796 {
797 SAL_INFO("slideshow", "createSlideTransition(): "
798 "Could not extract slide transition subtype from XDrawPage - assuming no transition" );
799 return ActivitySharedPtr();
800 }
801
802 bool bTransitionDirection(false);
803 if( !getPropertyValue( bTransitionDirection,
804 xPropSet,
805 u"TransitionDirection"_ustr) )
806 {
807 SAL_INFO("slideshow", "createSlideTransition(): "
808 "Could not extract slide transition direction from XDrawPage - assuming default direction" );
809 }
810
811 sal_Int32 aUnoColor(0);
812 if( !getPropertyValue( aUnoColor,
813 xPropSet,
814 u"TransitionFadeColor"_ustr) )
815 {
816 SAL_INFO("slideshow", "createSlideTransition(): "
817 "Could not extract slide transition fade color from XDrawPage - assuming black" );
818 }
819
820 const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
821
822 uno::Any aSound;
823 bool bLoopSound = false;
824
825 if( !getPropertyValue( aSound, xPropSet, u"Sound"_ustr) )
826 SAL_INFO("slideshow", "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound" );
827
828 if( !getPropertyValue( bLoopSound, xPropSet, u"LoopSound"_ustr ) )
829 SAL_INFO("slideshow", "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound" );
830
831 NumberAnimationSharedPtr pTransition(
832 TransitionFactory::createSlideTransition(
833 rLeavingSlide,
834 rEnteringSlide,
835 maViewContainer,
836 maScreenUpdater,
837 maEventMultiplexer,
838 mxOptionalTransitionFactory,
839 nTransitionType,
840 nTransitionSubType,
841 bTransitionDirection,
842 aTransitionFadeColor,
843 resetSlideTransitionSound( aSound, bLoopSound ) ));
844
845 if( !pTransition )
846 return ActivitySharedPtr(); // no transition effect has been
847 // generated. Normally, that means
848 // that simply no transition is
849 // set on this slide.
850
851 double nTransitionDuration(0.0);
852 if( !getPropertyValue( nTransitionDuration,
853 xPropSet,
854 u"TransitionDuration"_ustr) )
855 {
856 SAL_INFO("slideshow", "createSlideTransition(): "
857 "Could not extract slide transition duration from XDrawPage - assuming no transition" );
858 return ActivitySharedPtr();
859 }
860
861 sal_Int32 nMinFrames(5);
862 if( !getPropertyValue( nMinFrames,
863 xPropSet,
864 u"MinimalFrameNumber"_ustr) )
865 {
866 SAL_INFO("slideshow", "createSlideTransition(): "
867 "No minimal number of frames given - assuming 5" );
868 }
869
870 // prefetch slide transition bitmaps, but postpone it after
871 // displaySlide() has finished - sometimes, view size has not yet
872 // reached final size
873 maEventQueue.addEvent(
874 makeEvent( [pTransition] () {
875 pTransition->prefetch(); },
876 u"Animation::prefetch"_ustr));
877
878 return ActivitySharedPtr(
879 ActivitiesFactory::createSimpleActivity(
880 ActivitiesFactory::CommonParameters(
881 rTransitionEndEvent,
882 maEventQueue,
883 maActivitiesQueue,
884 nTransitionDuration,
885 nMinFrames,
886 false,
887 std::optional<double>(1.0),
888 0.0,
889 0.0,
890 ShapeSharedPtr(),
891 basegfx::B2DVector(rEnteringSlide->getSlideSize().getWidth(), rEnteringSlide->getSlideSize().getHeight()) ),
892 pTransition,
893 true ));
894 }
895
findPolygons(uno::Reference<drawing::XDrawPage> const & xDrawPage)896 PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
897 {
898 // TODO(P2): optimize research in the map.
899 return maPolygons.find(xDrawPage);
900 }
901
makeSlide(uno::Reference<drawing::XDrawPage> const & xDrawPage,uno::Reference<drawing::XDrawPagesSupplier> const & xDrawPages,uno::Reference<animations::XAnimationNode> const & xRootNode)902 SlideSharedPtr SlideShowImpl::makeSlide(
903 uno::Reference<drawing::XDrawPage> const& xDrawPage,
904 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
905 uno::Reference<animations::XAnimationNode> const& xRootNode )
906 {
907 if( !xDrawPage.is() )
908 return SlideSharedPtr();
909
910 //Retrieve polygons for the current slide
911 PolygonMap::iterator aIter = findPolygons(xDrawPage);
912
913 SlideSharedPtr pSlide( createSlide(xDrawPage,
914 xDrawPages,
915 xRootNode,
916 maEventQueue,
917 maEventMultiplexer,
918 maScreenUpdater,
919 maActivitiesQueue,
920 maUserEventQueue,
921 *this,
922 *this,
923 maViewContainer,
924 mxComponentContext,
925 maShapeEventListeners,
926 maShapeCursors,
927 (aIter != maPolygons.end()) ? aIter->second : PolyPolygonVector(),
928 maUserPaintColor ? *maUserPaintColor : RGBColor(),
929 maUserPaintStrokeWidth,
930 !!maUserPaintColor,
931 mbImageAnimationsAllowed,
932 mbDisableAnimationZOrder) );
933
934 // prefetch show content (reducing latency for slide
935 // bitmap and effect start later on)
936 pSlide->prefetch();
937
938 return pSlide;
939 }
940
requestWaitSymbol()941 void SlideShowImpl::requestWaitSymbol()
942 {
943 ++mnWaitSymbolRequestCount;
944 OSL_ASSERT(mnWaitSymbolRequestCount>0);
945
946 if (mnWaitSymbolRequestCount == 1)
947 {
948 if( !mpWaitSymbol )
949 {
950 // fall back to cursor
951 requestCursor(calcActiveCursor(mnCurrentCursor));
952 }
953 else
954 mpWaitSymbol->show();
955 }
956 }
957
releaseWaitSymbol()958 void SlideShowImpl::releaseWaitSymbol()
959 {
960 --mnWaitSymbolRequestCount;
961 OSL_ASSERT(mnWaitSymbolRequestCount>=0);
962
963 if (mnWaitSymbolRequestCount == 0)
964 {
965 if( !mpWaitSymbol )
966 {
967 // fall back to cursor
968 requestCursor(calcActiveCursor(mnCurrentCursor));
969 }
970 else
971 mpWaitSymbol->hide();
972 }
973 }
974
calcActiveCursor(sal_Int16 nCursorShape) const975 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
976 {
977 if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
978 nCursorShape = awt::SystemPointer::WAIT;
979 else if( !mbMouseVisible ) // enforce INVISIBLE
980 nCursorShape = awt::SystemPointer::INVISIBLE;
981 else if( maUserPaintColor &&
982 nCursorShape == awt::SystemPointer::ARROW )
983 nCursorShape = awt::SystemPointer::PEN;
984
985 return nCursorShape;
986 }
987
stopShow()988 void SlideShowImpl::stopShow()
989 {
990 // Force-end running animation
991 // ===========================
992 if (mpCurrentSlide)
993 {
994 mpCurrentSlide->hide();
995 //Register polygons in the map
996 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
997 maPolygons.erase(mpCurrentSlide->getXDrawPage());
998
999 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1000 }
1001
1002 // clear all queues
1003 maEventQueue.clear();
1004 maActivitiesQueue.clear();
1005
1006 // Attention: we MUST clear the user event queue here,
1007 // this is because the current slide might have registered
1008 // shape events (click or enter/leave), which might
1009 // otherwise dangle forever in the queue (because of the
1010 // shared ptr nature). If someone needs to change this:
1011 // somehow unregister those shapes at the user event queue
1012 // on notifySlideEnded().
1013 maUserEventQueue.clear();
1014
1015 // re-enable automatic effect advancement
1016 // (maEventQueue.clear() above might have killed
1017 // maEventMultiplexer's tick events)
1018 if (mbAutomaticAdvancementMode)
1019 {
1020 // toggle automatic mode (enabling just again is
1021 // ignored by EventMultiplexer)
1022 maEventMultiplexer.setAutomaticMode( false );
1023 maEventMultiplexer.setAutomaticMode( true );
1024 }
1025 }
1026
1027 class SlideShowImpl::PrefetchPropertiesFunc
1028 {
1029 public:
PrefetchPropertiesFunc(SlideShowImpl * that_,bool & rbSkipAllMainSequenceEffects,bool & rbSkipSlideTransition)1030 PrefetchPropertiesFunc( SlideShowImpl * that_,
1031 bool& rbSkipAllMainSequenceEffects,
1032 bool& rbSkipSlideTransition)
1033 : mpSlideShowImpl(that_),
1034 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1035 mrbSkipSlideTransition(rbSkipSlideTransition)
1036 {}
1037
operator ()(beans::PropertyValue const & rProperty) const1038 void operator()( beans::PropertyValue const& rProperty ) const {
1039 if (rProperty.Name == "Prefetch" )
1040 {
1041 uno::Sequence<uno::Any> seq;
1042 if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1043 {
1044 seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1045 seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1046 }
1047 }
1048 else if ( rProperty.Name == "SkipAllMainSequenceEffects" )
1049 {
1050 rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1051 }
1052 else if ( rProperty.Name == "SkipSlideTransition" )
1053 {
1054 rProperty.Value >>= mrbSkipSlideTransition;
1055 }
1056 else
1057 {
1058 SAL_WARN( "slideshow", rProperty.Name );
1059 }
1060 }
1061 private:
1062 SlideShowImpl *const mpSlideShowImpl;
1063 bool& mrbSkipAllMainSequenceEffects;
1064 bool& mrbSkipSlideTransition;
1065 };
1066
displaySlide(uno::Reference<drawing::XDrawPage> const & xSlide,uno::Reference<drawing::XDrawPagesSupplier> const & xDrawPages,uno::Reference<animations::XAnimationNode> const & xRootNode,uno::Sequence<beans::PropertyValue> const & rProperties)1067 void SlideShowImpl::displaySlide(
1068 uno::Reference<drawing::XDrawPage> const& xSlide,
1069 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
1070 uno::Reference<animations::XAnimationNode> const& xRootNode,
1071 uno::Sequence<beans::PropertyValue> const& rProperties )
1072 {
1073 osl::MutexGuard const guard( m_aMutex );
1074
1075 if (isDisposed())
1076 return;
1077
1078 maEffectRewinder.setRootAnimationNode(xRootNode);
1079 maEffectRewinder.setCurrentSlide(xSlide);
1080
1081 // precondition: must only be called from the main thread!
1082 DBG_TESTSOLARMUTEX();
1083
1084 mxDrawPagesSupplier = xDrawPages;
1085 mxSBD = uno::Reference<document::XStorageBasedDocument>(mxDrawPagesSupplier, uno::UNO_QUERY);
1086
1087 stopShow(); // MUST call that: results in
1088 // maUserEventQueue.clear(). What's more,
1089 // stopShow()'s currSlide->hide() call is
1090 // now also required, notifySlideEnded()
1091 // relies on that
1092 // unconditionally. Otherwise, genuine
1093 // shape animations (drawing layer and
1094 // GIF) will not be stopped.
1095
1096 bool bSkipAllMainSequenceEffects (false);
1097 bool bSkipSlideTransition (false);
1098 std::for_each( rProperties.begin(),
1099 rProperties.end(),
1100 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1101
1102 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1103 if (maViewContainer.empty())
1104 return;
1105
1106 // this here might take some time
1107 {
1108 WaitSymbolLock aLock (*this);
1109
1110 mpPreviousSlide = mpCurrentSlide;
1111 mpCurrentSlide.reset();
1112
1113 if (matches( mpPrefetchSlide, xSlide, xRootNode ))
1114 {
1115 // prefetched slide matches:
1116 mpCurrentSlide = mpPrefetchSlide;
1117 }
1118 else
1119 mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
1120
1121 OSL_ASSERT( mpCurrentSlide );
1122 if (mpCurrentSlide)
1123 {
1124 basegfx::B2DSize oldSlideSize;
1125 if( mpPreviousSlide )
1126 oldSlideSize = basegfx::B2DSize( mpPreviousSlide->getSlideSize() );
1127
1128 basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
1129
1130 // push new transformation to all views, if size changed
1131 if( !mpPreviousSlide || oldSlideSize != slideSize )
1132 {
1133 for( const auto& pView : maViewContainer )
1134 pView->setViewSize( slideSize );
1135
1136 // explicitly notify view change here,
1137 // because transformation might have changed:
1138 // optimization, this->notifyViewChange() would
1139 // repaint slide which is not necessary.
1140 maEventMultiplexer.notifyViewsChanged();
1141 }
1142
1143 // create slide transition, and add proper end event
1144 // (which then starts the slide effects
1145 // via CURRENT_SLIDE.show())
1146 ActivitySharedPtr pSlideChangeActivity (
1147 createSlideTransition(
1148 mpCurrentSlide->getXDrawPage(),
1149 mpPreviousSlide,
1150 mpCurrentSlide,
1151 makeEvent(
1152 [this] () { this->notifySlideTransitionEnded(false); },
1153 u"SlideShowImpl::notifySlideTransitionEnded"_ustr)));
1154
1155 if (bSkipSlideTransition)
1156 {
1157 // The transition activity was created for the side effects
1158 // (like sound transitions). Because we want to skip the
1159 // actual transition animation we do not need the activity
1160 // anymore.
1161 pSlideChangeActivity.reset();
1162 }
1163
1164 if (pSlideChangeActivity)
1165 {
1166 // factory generated a slide transition - activate it!
1167 maActivitiesQueue.addActivity( pSlideChangeActivity );
1168 }
1169 else
1170 {
1171 // no transition effect on this slide - schedule slide
1172 // effect start event right away.
1173 maEventQueue.addEvent(
1174 makeEvent(
1175 [this] () { this->notifySlideTransitionEnded(true); },
1176 u"SlideShowImpl::notifySlideTransitionEnded"_ustr));
1177 }
1178 }
1179 } // finally
1180
1181 maListenerContainer.forEach(
1182 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1183 {
1184 xListener->slideTransitionStarted();
1185 });
1186
1187 // We are currently rewinding an effect. This lead us from the next
1188 // slide to this one. To complete this we have to play back all main
1189 // sequence effects on this slide.
1190 if (bSkipAllMainSequenceEffects)
1191 maEffectRewinder.skipAllMainSequenceEffects();
1192 }
1193
redisplayCurrentSlide()1194 void SlideShowImpl::redisplayCurrentSlide()
1195 {
1196 osl::MutexGuard const guard( m_aMutex );
1197
1198 if (isDisposed())
1199 return;
1200
1201 // precondition: must only be called from the main thread!
1202 DBG_TESTSOLARMUTEX();
1203 stopShow();
1204
1205 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1206 if (maViewContainer.empty())
1207 return;
1208
1209 // No transition effect on this slide - schedule slide
1210 // effect start event right away.
1211 maEventQueue.addEvent(
1212 makeEvent( [this] () { this->notifySlideTransitionEnded(true); },
1213 u"SlideShowImpl::notifySlideTransitionEnded"_ustr));
1214
1215 maListenerContainer.forEach(
1216 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1217 {
1218 xListener->slideTransitionStarted();
1219 });
1220 }
1221
nextEffect()1222 sal_Bool SlideShowImpl::nextEffect()
1223 {
1224 mbMovingForward = true;
1225 osl::MutexGuard const guard( m_aMutex );
1226
1227 if (isDisposed())
1228 return false;
1229
1230 // precondition: must only be called from the main thread!
1231 DBG_TESTSOLARMUTEX();
1232
1233 if (mbShowPaused)
1234 return true;
1235 else
1236 return maEventMultiplexer.notifyNextEffect();
1237 }
1238
previousEffect()1239 sal_Bool SlideShowImpl::previousEffect()
1240 {
1241 mbMovingForward = false;
1242 osl::MutexGuard const guard( m_aMutex );
1243
1244 if (isDisposed())
1245 return false;
1246
1247 // precondition: must only be called from the main thread!
1248 DBG_TESTSOLARMUTEX();
1249
1250 if (mbShowPaused)
1251 return true;
1252 else
1253 {
1254 return maEffectRewinder.rewind(
1255 maScreenUpdater.createLock(),
1256 [this]() { return this->redisplayCurrentSlide(); },
1257 [this]() { return this->rewindEffectToPreviousSlide(); } );
1258 }
1259 }
1260
rewindEffectToPreviousSlide()1261 void SlideShowImpl::rewindEffectToPreviousSlide()
1262 {
1263 // Show the wait symbol now and prevent it from showing temporary slide
1264 // content while effects are played back.
1265 WaitSymbolLock aLock (*this);
1266
1267 // A previous call to EffectRewinder::Rewind could not rewind the current
1268 // effect because there are no effects on the current slide or none has
1269 // yet been displayed. Go to the previous slide.
1270 notifySlideEnded(true);
1271
1272 // Process pending events once more in order to have the following
1273 // screen update show the last effect. Not sure whether this should be
1274 // necessary.
1275 maEventQueue.forceEmpty();
1276
1277 // We have to call the screen updater before the wait symbol is turned
1278 // off. Otherwise the wait symbol would force the display of an
1279 // intermediate state of the slide (before the effects are replayed.)
1280 maScreenUpdater.commitUpdates();
1281 }
1282
startShapeActivity(uno::Reference<drawing::XShape> const &)1283 sal_Bool SlideShowImpl::startShapeActivity(
1284 uno::Reference<drawing::XShape> const& /*xShape*/ )
1285 {
1286 // precondition: must only be called from the main thread!
1287 DBG_TESTSOLARMUTEX();
1288
1289 // TODO(F3): NYI
1290 OSL_FAIL( "not yet implemented!" );
1291 return false;
1292 }
1293
stopShapeActivity(uno::Reference<drawing::XShape> const &)1294 sal_Bool SlideShowImpl::stopShapeActivity(
1295 uno::Reference<drawing::XShape> const& /*xShape*/ )
1296 {
1297 // precondition: must only be called from the main thread!
1298 DBG_TESTSOLARMUTEX();
1299
1300 // TODO(F3): NYI
1301 OSL_FAIL( "not yet implemented!" );
1302 return false;
1303 }
1304
pause(sal_Bool bPauseShow)1305 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1306 {
1307 osl::MutexGuard const guard( m_aMutex );
1308
1309 if (isDisposed())
1310 return false;
1311
1312 // precondition: must only be called from the main thread!
1313 DBG_TESTSOLARMUTEX();
1314
1315 if (bPauseShow)
1316 mpPresTimer->pauseTimer();
1317 else
1318 mpPresTimer->continueTimer();
1319
1320 maEventMultiplexer.notifyPauseMode(bPauseShow);
1321
1322 mbShowPaused = bPauseShow;
1323 return true;
1324 }
1325
getCurrentSlide()1326 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1327 {
1328 osl::MutexGuard const guard( m_aMutex );
1329
1330 if (isDisposed())
1331 return uno::Reference<drawing::XDrawPage>();
1332
1333 // precondition: must only be called from the main thread!
1334 DBG_TESTSOLARMUTEX();
1335
1336 if (mpCurrentSlide)
1337 return mpCurrentSlide->getXDrawPage();
1338 else
1339 return uno::Reference<drawing::XDrawPage>();
1340 }
1341
addView(uno::Reference<presentation::XSlideShowView> const & xView)1342 sal_Bool SlideShowImpl::addView(
1343 uno::Reference<presentation::XSlideShowView> const& xView )
1344 {
1345 osl::MutexGuard const guard( m_aMutex );
1346
1347 if (isDisposed())
1348 return false;
1349
1350 // precondition: must only be called from the main thread!
1351 DBG_TESTSOLARMUTEX();
1352
1353 // first of all, check if view has a valid canvas
1354 ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
1355 ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(),
1356 "addView(): View does not provide a valid canvas" );
1357
1358 UnoViewSharedPtr const pView( createSlideView(
1359 xView,
1360 maEventQueue,
1361 maEventMultiplexer ));
1362 if (!maViewContainer.addView( pView ))
1363 return false; // view already added
1364
1365 // initialize view content
1366 // =======================
1367
1368 if (mpCurrentSlide)
1369 {
1370 // set view transformation
1371 const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
1372 pView->setViewSize( basegfx::B2DSize(slideSize) );
1373 }
1374
1375 // clear view area (since it's newly added,
1376 // we need a clean slate)
1377 pView->clearAll();
1378
1379 // broadcast newly added view
1380 maEventMultiplexer.notifyViewAdded( pView );
1381
1382 // set current mouse ptr
1383 pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1384
1385 return true;
1386 }
1387
removeView(uno::Reference<presentation::XSlideShowView> const & xView)1388 sal_Bool SlideShowImpl::removeView(
1389 uno::Reference<presentation::XSlideShowView> const& xView )
1390 {
1391 osl::MutexGuard const guard( m_aMutex );
1392
1393 // precondition: must only be called from the main thread!
1394 DBG_TESTSOLARMUTEX();
1395
1396 ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
1397
1398 UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
1399 if( !pView )
1400 return false; // view was not added in the first place
1401
1402 // remove view from EventMultiplexer (mouse events etc.)
1403 maEventMultiplexer.notifyViewRemoved( pView );
1404
1405 pView->_dispose();
1406
1407 return true;
1408 }
1409
1410 drawing::PointSequenceSequence
lcl_createPointSequenceSequenceFromB2DPolygon(const basegfx::B2DPolygon & rPoly)1411 lcl_createPointSequenceSequenceFromB2DPolygon(const basegfx::B2DPolygon& rPoly)
1412 {
1413 drawing::PointSequenceSequence aRetval;
1414 //Create only one sequence for one pen drawing.
1415 aRetval.realloc(1);
1416 // Retrieve the sequence of points from aRetval
1417 drawing::PointSequence* pOuterSequence = aRetval.getArray();
1418 // Create points in this sequence from rPoly
1419 pOuterSequence->realloc(rPoly.count());
1420 // Get these points which are in an array
1421 awt::Point* pInnerSequence = pOuterSequence->getArray();
1422 for( sal_uInt32 n = 0; n < rPoly.count(); n++ )
1423 {
1424 //Create a point from the polygon
1425 *pInnerSequence++ = awt::Point(basegfx::fround(rPoly.getB2DPoint(n).getX()),
1426 basegfx::fround(rPoly.getB2DPoint(n).getY()));
1427 }
1428 return aRetval;
1429 }
1430
lcl_setPropertiesToShape(const drawing::PointSequenceSequence & rPoints,const cppcanvas::PolyPolygonSharedPtr pCanvasPolyPoly,const uno::Reference<drawing::XShape> & rPolyShape)1431 void lcl_setPropertiesToShape(const drawing::PointSequenceSequence& rPoints,
1432 const cppcanvas::PolyPolygonSharedPtr pCanvasPolyPoly,
1433 const uno::Reference< drawing::XShape >& rPolyShape)
1434 {
1435 uno::Reference< beans::XPropertySet > aXPropSet(rPolyShape, uno::UNO_QUERY);
1436 //Give the built PointSequenceSequence.
1437 uno::Any aParam;
1438 aParam <<= rPoints;
1439 aXPropSet->setPropertyValue(u"PolyPolygon"_ustr, aParam);
1440
1441 //LineStyle : SOLID by default
1442 drawing::LineStyle eLS;
1443 eLS = drawing::LineStyle_SOLID;
1444 aXPropSet->setPropertyValue(u"LineStyle"_ustr, uno::Any(eLS));
1445
1446 //LineCap : ROUND by default, same as in show mode
1447 drawing::LineCap eLC;
1448 eLC = drawing::LineCap_ROUND;
1449 aXPropSet->setPropertyValue(u"LineCap"_ustr, uno::Any(eLC));
1450
1451 //LineColor
1452 sal_uInt32 nLineColor = 0;
1453 if (pCanvasPolyPoly)
1454 nLineColor = pCanvasPolyPoly->getRGBALineColor();
1455 //Transform polygon color from RRGGBBAA to AARRGGBB
1456 aXPropSet->setPropertyValue(u"LineColor"_ustr, uno::Any(RGBAColor2UnoColor(nLineColor)));
1457
1458 //LineWidth
1459 double fLineWidth = 0;
1460 if (pCanvasPolyPoly)
1461 fLineWidth = pCanvasPolyPoly->getStrokeWidth();
1462 aXPropSet->setPropertyValue(u"LineWidth"_ustr, uno::Any(static_cast<sal_Int32>(fLineWidth)));
1463 }
1464
registerUserPaintPolygons(const uno::Reference<lang::XMultiServiceFactory> & xDocFactory)1465 void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory )
1466 {
1467 //Retrieve Polygons if user ends presentation by context menu
1468 if (mpCurrentSlide)
1469 {
1470 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1471 maPolygons.erase(mpCurrentSlide->getXDrawPage());
1472
1473 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1474 }
1475
1476 //Creating the layer for shapes drawn during slideshow
1477 // query for the XLayerManager
1478 uno::Reference< drawing::XLayerSupplier > xLayerSupplier(xDocFactory, uno::UNO_QUERY);
1479 uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager();
1480 uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY);
1481
1482 // create layer
1483 uno::Reference< drawing::XLayer > xDrawnInSlideshow;
1484 uno::Any aPropLayer;
1485 OUString sLayerName = u"DrawnInSlideshow"_ustr;
1486 if (xNameAccess->hasByName(sLayerName))
1487 {
1488 xNameAccess->getByName(sLayerName) >>= xDrawnInSlideshow;
1489 }
1490 else
1491 {
1492 xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
1493 aPropLayer <<= sLayerName;
1494 xDrawnInSlideshow->setPropertyValue(u"Name"_ustr, aPropLayer);
1495 }
1496
1497 // ODF defaults from ctor of SdrLayer are not automatically set on the here
1498 // created XLayer. Need to be done explicitly here.
1499 aPropLayer <<= true;
1500 xDrawnInSlideshow->setPropertyValue(u"IsVisible"_ustr, aPropLayer);
1501 xDrawnInSlideshow->setPropertyValue(u"IsPrintable"_ustr, aPropLayer);
1502 aPropLayer <<= false;
1503 xDrawnInSlideshow->setPropertyValue(u"IsLocked"_ustr, aPropLayer);
1504
1505 //Register polygons for each slide
1506 // The polygons are simplified using the Ramer-Douglas-Peucker algorithm.
1507 // This is the therefore needed tolerance. Ideally the value should be user defined.
1508 // For now a suitable value is found experimental.
1509 constexpr double fTolerance(12);
1510 for (const auto& rPoly : maPolygons)
1511 {
1512 PolyPolygonVector aPolygons = rPoly.second;
1513 if (aPolygons.empty())
1514 continue;
1515 //Get shapes for the slide
1516 css::uno::Reference<css::drawing::XShapes> Shapes = rPoly.first;
1517
1518 //Retrieve polygons for one slide
1519 // #tdf112687 A pen drawing in slideshow is actually a chain of individual line shapes, where
1520 // the end point of one line shape equals the start point of the next line shape.
1521 // We collect these points into one B2DPolygon and use that to generate the shape on the
1522 // slide.
1523 ::basegfx::B2DPolygon aDrawingPoints;
1524 cppcanvas::PolyPolygonSharedPtr pFirstPolyPoly = aPolygons.front(); // for style properties
1525 for (const auto& pPolyPoly : aPolygons)
1526 {
1527 if (pPolyPoly->getIsFromPreviousSlideshow())
1528 continue;
1529 // Actually, each item in aPolygons has two points, but wrapped in a cppcanvas::PopyPolygon.
1530 ::basegfx::B2DPolyPolygon b2DPolyPoly
1531 = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
1532 pPolyPoly->getUNOPolyPolygon());
1533
1534 //Normally there is only one polygon
1535 for (sal_uInt32 i = 0; i < b2DPolyPoly.count(); i++)
1536 {
1537 const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
1538
1539 if (aPoly.count() > 1) // otherwise skip it, count should be 2
1540 {
1541 const auto ptCount = aDrawingPoints.count();
1542 if (ptCount == 0)
1543 {
1544 aDrawingPoints.append(aPoly);
1545 pFirstPolyPoly = pPolyPoly;
1546 continue;
1547 }
1548 basegfx::B2DPoint aLast
1549 = aDrawingPoints.getB2DPoint(ptCount - 1);
1550 if (aPoly.getB2DPoint(0).equal(aLast))
1551 {
1552 aDrawingPoints.append(aPoly, 1);
1553 continue;
1554 }
1555
1556 // Put what we have collected to the slide and then start a new pen drawing object
1557 //create the PolyLineShape. The points will be in its PolyPolygon property.
1558 uno::Reference<uno::XInterface> polyshape(
1559 xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
1560 uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY);
1561 //Add the shape to the slide
1562 Shapes->add(rPolyShape);
1563 //Construct a sequence of points sequence
1564 aDrawingPoints
1565 = basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance);
1566 const drawing::PointSequenceSequence aRetval
1567 = lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints);
1568 //Fill the properties
1569 lcl_setPropertiesToShape(aRetval, pFirstPolyPoly, rPolyShape);
1570 // make polygons special
1571 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1572 // Start next pen drawing object
1573 aDrawingPoints.clear();
1574 aDrawingPoints.append(aPoly);
1575 pFirstPolyPoly = pPolyPoly;
1576 }
1577 }
1578 }
1579 // Bring remaining points to slide
1580 if (aDrawingPoints.count() > 1)
1581 {
1582 //create the PolyLineShape. The points will be in its PolyPolygon property.
1583 uno::Reference<uno::XInterface> polyshape(
1584 xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
1585 uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY);
1586 //Add the shape to the slide
1587 Shapes->add(rPolyShape);
1588 //Construct a sequence of points sequence
1589 aDrawingPoints = basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance);
1590 drawing::PointSequenceSequence aRetval
1591 = lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints);
1592 //Fill the properties
1593 lcl_setPropertiesToShape(aRetval, aPolygons.back(), rPolyShape);
1594 // make polygons special
1595 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1596 }
1597 }
1598 }
1599
setProperty(beans::PropertyValue const & rProperty)1600 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1601 {
1602 osl::MutexGuard const guard( m_aMutex );
1603
1604 if (isDisposed())
1605 return false;
1606
1607 // precondition: must only be called from the main thread!
1608 DBG_TESTSOLARMUTEX();
1609
1610 // tdf#160669 IASS: if hint is about PrefetchSlide, flush it to avoid errors
1611 if ( rProperty.Name == "HintSlideChanged" )
1612 {
1613 uno::Reference< drawing::XDrawPage > xDrawPage;
1614 if (rProperty.Value >>= xDrawPage)
1615 {
1616 if (xDrawPage == mxPrefetchSlide)
1617 {
1618 mxPrefetchSlide.clear();
1619 mpPrefetchSlide.reset();
1620 }
1621 }
1622 }
1623
1624 if ( rProperty.Name == "AutomaticAdvancement" )
1625 {
1626 double nTimeout(0.0);
1627 mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
1628 if (mbAutomaticAdvancementMode)
1629 {
1630 maEventMultiplexer.setAutomaticTimeout( nTimeout );
1631 }
1632 maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
1633 return true;
1634 }
1635
1636 if ( rProperty.Name == "UserPaintColor" )
1637 {
1638 sal_Int32 nColor(0);
1639 if (rProperty.Value >>= nColor)
1640 {
1641 OSL_ENSURE( mbMouseVisible,
1642 "setProperty(): User paint overrides invisible mouse" );
1643
1644 // enable user paint
1645 maUserPaintColor = unoColor2RGBColor(nColor);
1646 if( mpCurrentSlide && !mpCurrentSlide->isPaintOverlayActive() )
1647 mpCurrentSlide->enablePaintOverlay();
1648
1649 maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
1650 }
1651 else
1652 {
1653 // disable user paint
1654 maUserPaintColor.reset();
1655 maEventMultiplexer.notifyUserPaintDisabled();
1656 }
1657
1658 resetCursor();
1659
1660 return true;
1661 }
1662
1663 //adding support for erasing features in UserPaintOverlay
1664 if ( rProperty.Name == "EraseAllInk" )
1665 {
1666 bool bEraseAllInk(false);
1667 if (rProperty.Value >>= bEraseAllInk)
1668 {
1669 OSL_ENSURE( mbMouseVisible,
1670 "setProperty(): User paint overrides invisible mouse" );
1671
1672 // enable user paint
1673 maEraseAllInk = bEraseAllInk;
1674 maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
1675 }
1676
1677 return true;
1678 }
1679
1680 if ( rProperty.Name == "SwitchPenMode" )
1681 {
1682 bool bSwitchPenMode(false);
1683 if (rProperty.Value >>= bSwitchPenMode)
1684 {
1685 OSL_ENSURE( mbMouseVisible,
1686 "setProperty(): User paint overrides invisible mouse" );
1687
1688 if(bSwitchPenMode){
1689 // Switch to Pen Mode
1690 maEventMultiplexer.notifySwitchPenMode();
1691 }
1692 }
1693 return true;
1694 }
1695
1696 if ( rProperty.Name == "SwitchEraserMode" )
1697 {
1698 bool bSwitchEraserMode(false);
1699 if (rProperty.Value >>= bSwitchEraserMode)
1700 {
1701 OSL_ENSURE( mbMouseVisible,
1702 "setProperty(): User paint overrides invisible mouse" );
1703 if(bSwitchEraserMode){
1704 // switch to Eraser mode
1705 maEventMultiplexer.notifySwitchEraserMode();
1706 }
1707 }
1708
1709 return true;
1710 }
1711
1712 if ( rProperty.Name == "EraseInk" )
1713 {
1714 sal_Int32 nEraseInk(100);
1715 if (rProperty.Value >>= nEraseInk)
1716 {
1717 OSL_ENSURE( mbMouseVisible,
1718 "setProperty(): User paint overrides invisible mouse" );
1719
1720 // enable user paint
1721 maEraseInk = nEraseInk;
1722 maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
1723 }
1724
1725 return true;
1726 }
1727
1728 // new Property for pen's width
1729 if ( rProperty.Name == "UserPaintStrokeWidth" )
1730 {
1731 double nWidth(4.0);
1732 if (rProperty.Value >>= nWidth)
1733 {
1734 OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
1735 // enable user paint stroke width
1736 maUserPaintStrokeWidth = nWidth;
1737 maEventMultiplexer.notifyUserPaintStrokeWidth( maUserPaintStrokeWidth );
1738 }
1739
1740 return true;
1741 }
1742
1743 if ( rProperty.Name == "AdvanceOnClick" )
1744 {
1745 bool bAdvanceOnClick = false;
1746 if (! (rProperty.Value >>= bAdvanceOnClick))
1747 return false;
1748 maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1749 return true;
1750 }
1751
1752 if ( rProperty.Name == "DisableAnimationZOrder" )
1753 {
1754 bool bDisableAnimationZOrder = false;
1755 if (! (rProperty.Value >>= bDisableAnimationZOrder))
1756 return false;
1757 mbDisableAnimationZOrder = bDisableAnimationZOrder;
1758 return true;
1759 }
1760
1761 if ( rProperty.Name == "ImageAnimationsAllowed" )
1762 {
1763 if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1764 return false;
1765
1766 // TODO(F3): Forward to slides!
1767 return true;
1768 }
1769
1770 if ( rProperty.Name == "MouseVisible" )
1771 {
1772 if (! (rProperty.Value >>= mbMouseVisible))
1773 return false;
1774
1775 requestCursor(mnCurrentCursor);
1776
1777 return true;
1778 }
1779
1780 if ( rProperty.Name == "ForceManualAdvance" )
1781 {
1782 return (rProperty.Value >>= mbForceManualAdvance);
1783 }
1784
1785 if ( rProperty.Name == "RehearseTimings" )
1786 {
1787 bool bRehearseTimings = false;
1788 if (! (rProperty.Value >>= bRehearseTimings))
1789 return false;
1790
1791 if (bRehearseTimings)
1792 {
1793 // TODO(Q3): Move to slide
1794 mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1795 SlideShowContext(
1796 mpDummyPtr,
1797 maEventQueue,
1798 maEventMultiplexer,
1799 maScreenUpdater,
1800 maActivitiesQueue,
1801 maUserEventQueue,
1802 *this,
1803 *this,
1804 maViewContainer,
1805 mxComponentContext,
1806 mpBox2DDummyPtr ) );
1807 }
1808 else if (mpRehearseTimingsActivity)
1809 {
1810 // removes timer from all views:
1811 mpRehearseTimingsActivity->dispose();
1812 mpRehearseTimingsActivity.reset();
1813 }
1814 return true;
1815 }
1816
1817 if ( rProperty.Name == "WaitSymbolBitmap" )
1818 {
1819 uno::Reference<rendering::XBitmap> xBitmap;
1820 if (! (rProperty.Value >>= xBitmap))
1821 return false;
1822
1823 mpWaitSymbol = WaitSymbol::create( xBitmap,
1824 maScreenUpdater,
1825 maEventMultiplexer,
1826 maViewContainer );
1827
1828 return true;
1829 }
1830
1831 if (rProperty.Name == "NavigationSlidePrev")
1832 {
1833 uno::Reference<rendering::XBitmap> xBitmap;
1834 if (!(rProperty.Value >>= xBitmap))
1835 return false;
1836
1837 mpNavigationPrev = SlideOverlayButton::create(
1838 xBitmap, { 20, 10 }, [this](basegfx::B2DPoint) { notifySlideEnded(true); },
1839 maScreenUpdater, maEventMultiplexer, maViewContainer);
1840
1841 return true;
1842 }
1843
1844 if (rProperty.Name == "NavigationSlideMenu")
1845 {
1846 uno::Reference<rendering::XBitmap> xBitmap;
1847 if (!(rProperty.Value >>= xBitmap))
1848 return false;
1849
1850 mpNavigationMenu = SlideOverlayButton::create(
1851 xBitmap, { xBitmap->getSize().Width + 48, 10 },
1852 [this](basegfx::B2DPoint pos) {
1853 maListenerContainer.forEach(
1854 [pos](const uno::Reference<presentation::XSlideShowListener>& xListener) {
1855 uno::Reference<presentation::XSlideShowNavigationListener> xNavListener(
1856 xListener, uno::UNO_QUERY);
1857 if (xNavListener.is())
1858 xNavListener->contextMenuShow(
1859 css::awt::Point(static_cast<sal_Int32>(floor(pos.getX())),
1860 static_cast<sal_Int32>(floor(pos.getY()))));
1861 });
1862 },
1863 maScreenUpdater, maEventMultiplexer, maViewContainer);
1864
1865 return true;
1866 }
1867
1868 if (rProperty.Name == "NavigationSlideNext")
1869 {
1870 uno::Reference<rendering::XBitmap> xBitmap;
1871 if (!(rProperty.Value >>= xBitmap))
1872 return false;
1873
1874 mpNavigationNext = SlideOverlayButton::create(
1875 xBitmap, { 2 * xBitmap->getSize().Width + 76, 10 }, [this](basegfx::B2DPoint) { notifySlideEnded(false); },
1876 maScreenUpdater, maEventMultiplexer, maViewContainer);
1877
1878 return true;
1879 }
1880
1881 if ( rProperty.Name == "PointerSymbolBitmap" )
1882 {
1883 uno::Reference<rendering::XBitmap> xBitmap;
1884 if (! (rProperty.Value >>= xBitmap))
1885 return false;
1886
1887 mpPointerSymbol = PointerSymbol::create( xBitmap,
1888 maScreenUpdater,
1889 maEventMultiplexer,
1890 maViewContainer );
1891
1892 return true;
1893 }
1894
1895 if ( rProperty.Name == "PointerVisible" )
1896 {
1897 bool visible;
1898 if (!(rProperty.Value >>= visible))
1899 return false;
1900
1901 if (!mbShowPaused)
1902 mpPointerSymbol->setVisible(visible);
1903
1904 return true;
1905 }
1906
1907 if ( rProperty.Name == "PointerPosition")
1908 {
1909 css::geometry::RealPoint2D pos;
1910 if (! (rProperty.Value >>= pos))
1911 return false;
1912
1913 if (!mbShowPaused)
1914 mpPointerSymbol->viewsChanged(pos);
1915
1916 return true;
1917 }
1918
1919 if (rProperty.Name == "NoSlideTransitions" )
1920 {
1921 return (rProperty.Value >>= mbNoSlideTransitions);
1922 }
1923
1924 if ( rProperty.Name == "IsSoundEnabled" )
1925 {
1926 uno::Sequence<uno::Any> aValues;
1927 uno::Reference<presentation::XSlideShowView> xView;
1928 bool bValue (false);
1929 if ((rProperty.Value >>= aValues)
1930 && aValues.getLength()==2
1931 && (aValues[0] >>= xView)
1932 && (aValues[1] >>= bValue))
1933 {
1934 // Look up the view.
1935 auto iView = std::find_if(maViewContainer.begin(), maViewContainer.end(),
1936 [&xView](const UnoViewSharedPtr& rxView) { return rxView && rxView->getUnoView() == xView; });
1937 if (iView != maViewContainer.end())
1938 {
1939 // Store the flag at the view so that media shapes have
1940 // access to it.
1941 (*iView)->setIsSoundEnabled(bValue);
1942 return true;
1943 }
1944 }
1945 }
1946
1947 return false;
1948 }
1949
addSlideShowListener(uno::Reference<presentation::XSlideShowListener> const & xListener)1950 void SlideShowImpl::addSlideShowListener(
1951 uno::Reference<presentation::XSlideShowListener> const& xListener )
1952 {
1953 osl::MutexGuard const guard( m_aMutex );
1954
1955 if (isDisposed())
1956 return;
1957
1958 // container syncs with passed mutex ref
1959 maListenerContainer.addInterface(xListener);
1960 }
1961
removeSlideShowListener(uno::Reference<presentation::XSlideShowListener> const & xListener)1962 void SlideShowImpl::removeSlideShowListener(
1963 uno::Reference<presentation::XSlideShowListener> const& xListener )
1964 {
1965 osl::MutexGuard const guard( m_aMutex );
1966
1967 // container syncs with passed mutex ref
1968 maListenerContainer.removeInterface(xListener);
1969 }
1970
addShapeEventListener(uno::Reference<presentation::XShapeEventListener> const & xListener,uno::Reference<drawing::XShape> const & xShape)1971 void SlideShowImpl::addShapeEventListener(
1972 uno::Reference<presentation::XShapeEventListener> const& xListener,
1973 uno::Reference<drawing::XShape> const& xShape )
1974 {
1975 osl::MutexGuard const guard( m_aMutex );
1976
1977 if (isDisposed())
1978 return;
1979
1980 // precondition: must only be called from the main thread!
1981 DBG_TESTSOLARMUTEX();
1982
1983 ShapeEventListenerMap::iterator aIter;
1984 if( (aIter=maShapeEventListeners.find( xShape )) ==
1985 maShapeEventListeners.end() )
1986 {
1987 // no entry for this shape -> create one
1988 aIter = maShapeEventListeners.emplace(
1989 xShape,
1990 std::make_shared<comphelper::OInterfaceContainerHelper3<css::presentation::XShapeEventListener>>(
1991 m_aMutex)).first;
1992 }
1993
1994 // add new listener to broadcaster
1995 if( aIter->second )
1996 aIter->second->addInterface( xListener );
1997
1998 maEventMultiplexer.notifyShapeListenerAdded(xShape);
1999 }
2000
removeShapeEventListener(uno::Reference<presentation::XShapeEventListener> const & xListener,uno::Reference<drawing::XShape> const & xShape)2001 void SlideShowImpl::removeShapeEventListener(
2002 uno::Reference<presentation::XShapeEventListener> const& xListener,
2003 uno::Reference<drawing::XShape> const& xShape )
2004 {
2005 osl::MutexGuard const guard( m_aMutex );
2006
2007 // precondition: must only be called from the main thread!
2008 DBG_TESTSOLARMUTEX();
2009
2010 ShapeEventListenerMap::iterator aIter;
2011 if( (aIter = maShapeEventListeners.find( xShape )) !=
2012 maShapeEventListeners.end() )
2013 {
2014 // entry for this shape found -> remove listener from
2015 // helper object
2016 ENSURE_OR_THROW(
2017 aIter->second,
2018 "SlideShowImpl::removeShapeEventListener(): "
2019 "listener map contains NULL broadcast helper" );
2020
2021 aIter->second->removeInterface( xListener );
2022 }
2023
2024 maEventMultiplexer.notifyShapeListenerRemoved(xShape);
2025 }
2026
setShapeCursor(uno::Reference<drawing::XShape> const & xShape,sal_Int16 nPointerShape)2027 void SlideShowImpl::setShapeCursor(
2028 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
2029 {
2030 osl::MutexGuard const guard( m_aMutex );
2031
2032 if (isDisposed())
2033 return;
2034
2035 // precondition: must only be called from the main thread!
2036 DBG_TESTSOLARMUTEX();
2037
2038 ShapeCursorMap::iterator aIter;
2039 if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
2040 {
2041 // no entry for this shape -> create one
2042 if( nPointerShape != awt::SystemPointer::ARROW )
2043 {
2044 // add new entry, unless shape shall display
2045 // normal pointer arrow -> no need to handle that
2046 // case
2047 maShapeCursors.emplace(xShape, nPointerShape);
2048 }
2049 }
2050 else if( nPointerShape == awt::SystemPointer::ARROW )
2051 {
2052 // shape shall display normal cursor -> can disable
2053 // the cursor and clear the entry
2054 maShapeCursors.erase( xShape );
2055 }
2056 else
2057 {
2058 // existing entry found, update with new cursor ID
2059 aIter->second = nPointerShape;
2060 }
2061 }
2062
requestCursor(sal_Int16 nCursorShape)2063 bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape )
2064 {
2065 mnCurrentCursor = nCursorShape;
2066
2067 const sal_Int16 nActualCursor = calcActiveCursor(mnCurrentCursor);
2068
2069 // change all views to the requested cursor ID
2070 for( const auto& pView : maViewContainer )
2071 pView->setCursorShape( nActualCursor );
2072
2073 return nActualCursor==nCursorShape;
2074 }
2075
resetCursor()2076 void SlideShowImpl::resetCursor()
2077 {
2078 mnCurrentCursor = awt::SystemPointer::ARROW;
2079
2080 const sal_Int16 nActualCursor = calcActiveCursor( mnCurrentCursor );
2081 // change all views to the default cursor ID
2082 for( const auto& pView : maViewContainer )
2083 pView->setCursorShape( nActualCursor );
2084 }
2085
update(double & nNextTimeout)2086 sal_Bool SlideShowImpl::update( double & nNextTimeout )
2087 {
2088 osl::MutexGuard const guard( m_aMutex );
2089
2090 if (isDisposed())
2091 return false;
2092
2093 // precondition: update() must only be called from the
2094 // main thread!
2095 DBG_TESTSOLARMUTEX();
2096
2097 if( mbShowPaused )
2098 {
2099 // commit frame (might be repaints pending)
2100 maScreenUpdater.commitUpdates();
2101
2102 return false;
2103 }
2104 else
2105 {
2106 // TODO(F2): re-evaluate whether that timer lagging makes
2107 // sense.
2108
2109 // hold timer, while processing the queues:
2110 // 1. when there is more than one active activity this ensures the
2111 // same time for all activities and events
2112 // 2. processing of events may lead to creation of further events
2113 // that have zero delay. While the timer is stopped these events
2114 // are processed in the same run.
2115 {
2116 //Get a shared-ptr that outlives the scope-guard which will
2117 //ensure that the pointed-to-item exists in the case of a
2118 //::dispose clearing mpPresTimer
2119 std::shared_ptr<canvastools::ElapsedTime> xTimer(mpPresTimer);
2120 comphelper::ScopeGuard scopeGuard(
2121 [&xTimer]() { return xTimer->releaseTimer(); } );
2122 xTimer->holdTimer();
2123
2124 // process queues
2125 maEventQueue.process();
2126
2127 // #i118671# the call above may execute a macro bound to an object. In
2128 // that case this macro may have destroyed this local slideshow so that it
2129 // is disposed (see bugdoc at task). In that case, detect this and exit
2130 // gently from this slideshow. Do not forget to disable the scoped
2131 // call to mpPresTimer, this will be deleted if we are disposed.
2132 if (isDisposed())
2133 {
2134 scopeGuard.dismiss();
2135 return false;
2136 }
2137
2138 maActivitiesQueue.process();
2139
2140 // commit frame to screen
2141 maFrameSynchronization.Synchronize();
2142 maScreenUpdater.commitUpdates();
2143
2144 // TODO(Q3): remove need to call dequeued() from
2145 // activities. feels like a wart.
2146
2147 // Rationale for ActivitiesQueue::processDequeued(): when
2148 // an activity ends, it usually pushed the end state to
2149 // the animated shape in question, and ends the animation
2150 // (which, in turn, will usually disable shape sprite
2151 // mode). Disabling shape sprite mode causes shape
2152 // repaint, which, depending on slide content, takes
2153 // considerably more time than sprite updates. Thus, the
2154 // last animation step tends to look delayed. To
2155 // camouflage this, reaching end position and disabling
2156 // sprite mode is split into two (normal Activity::end(),
2157 // and Activity::dequeued()). Now, the reason to call
2158 // commitUpdates() twice here is caused by the unrelated
2159 // fact that during wait cursor display/hide, the screen
2160 // is updated, and shows hidden sprites, but, in case of
2161 // leaving the second commitUpdates() call out and punting
2162 // that to the next round, no updated static slide
2163 // content. In short, the last shape animation of a slide
2164 // tends to blink at its end.
2165
2166 // process dequeued activities _after_ commit to screen
2167 maActivitiesQueue.processDequeued();
2168
2169 // commit frame to screen
2170 maScreenUpdater.commitUpdates();
2171 }
2172 // Time held until here
2173
2174 const bool bActivitiesLeft = ! maActivitiesQueue.isEmpty();
2175 const bool bTimerEventsLeft = ! maEventQueue.isEmpty();
2176 const bool bRet = (bActivitiesLeft || bTimerEventsLeft);
2177
2178 if (bRet)
2179 {
2180 // calc nNextTimeout value:
2181 if (bActivitiesLeft)
2182 {
2183 // Activity queue is not empty. Tell caller that we would
2184 // like to render another frame.
2185
2186 // Return a zero time-out to signal our caller to call us
2187 // back as soon as possible. The actual timing, waiting the
2188 // appropriate amount of time between frames, is then done
2189 // by the maFrameSynchronization object.
2190 nNextTimeout = 0;
2191 maFrameSynchronization.Activate();
2192 }
2193 else
2194 {
2195 // timer events left:
2196 // difference from current time (nota bene:
2197 // time no longer held here!) to the next event in
2198 // the event queue.
2199
2200 // #i61190# Retrieve next timeout only _after_
2201 // processing activity queue
2202
2203 // ensure positive value:
2204 nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() );
2205
2206 // There is no active animation so the frame rate does not
2207 // need to be synchronized.
2208 maFrameSynchronization.Deactivate();
2209 }
2210
2211 mbSlideShowIdle = false;
2212 }
2213
2214 #if defined(DBG_UTIL)
2215 // when slideshow is idle, issue an XUpdatable::update() call
2216 // exactly once after a previous animation sequence finished -
2217 // this might trigger screen dumps on some canvas
2218 // implementations
2219 if( !mbSlideShowIdle &&
2220 (!bRet ||
2221 nNextTimeout > 1.0) )
2222 {
2223 for( const auto& pView : maViewContainer )
2224 {
2225 try
2226 {
2227 uno::Reference< presentation::XSlideShowView > xView( pView->getUnoView(),
2228 uno::UNO_SET_THROW );
2229 uno::Reference<util::XUpdatable> const xUpdatable(
2230 xView->getCanvas(), uno::UNO_QUERY);
2231 if (xUpdatable.is()) // not supported in PresenterCanvas
2232 {
2233 xUpdatable->update();
2234 }
2235 }
2236 catch( uno::RuntimeException& )
2237 {
2238 throw;
2239 }
2240 catch( uno::Exception& )
2241 {
2242 TOOLS_WARN_EXCEPTION( "slideshow", "" );
2243 }
2244 }
2245
2246 mbSlideShowIdle = true;
2247 }
2248 #endif
2249
2250 return bRet;
2251 }
2252 }
2253
notifySlideTransitionEnded(bool bPaintSlide)2254 void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide )
2255 {
2256 osl::MutexGuard const guard( m_aMutex );
2257
2258 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2259 OSL_ENSURE( mpCurrentSlide,
2260 "notifySlideTransitionEnded(): Invalid current slide" );
2261 if (mpCurrentSlide)
2262 {
2263 mpCurrentSlide->update_settings( !!maUserPaintColor, maUserPaintColor ? *maUserPaintColor : RGBColor(), maUserPaintStrokeWidth );
2264
2265 // first init show, to give the animations
2266 // the chance to register SlideStartEvents
2267 const bool bBackgroundLayerRendered( !bPaintSlide );
2268 mpCurrentSlide->show( bBackgroundLayerRendered );
2269 maEventMultiplexer.notifySlideStartEvent();
2270 }
2271 }
2272
queryAutomaticSlideTransition(uno::Reference<drawing::XDrawPage> const & xDrawPage,double & nAutomaticNextSlideTimeout,bool & bHasAutomaticNextSlide)2273 void queryAutomaticSlideTransition( uno::Reference<drawing::XDrawPage> const& xDrawPage,
2274 double& nAutomaticNextSlideTimeout,
2275 bool& bHasAutomaticNextSlide )
2276 {
2277 // retrieve slide change parameters from XDrawPage
2278 // ===============================================
2279
2280 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
2281 uno::UNO_QUERY );
2282
2283 sal_Int32 nChange(0);
2284 if( !xPropSet.is() ||
2285 !getPropertyValue( nChange,
2286 xPropSet,
2287 u"Change"_ustr) )
2288 {
2289 SAL_INFO("slideshow",
2290 "queryAutomaticSlideTransition(): "
2291 "Could not extract slide change mode from XDrawPage - assuming <none>" );
2292 }
2293
2294 bHasAutomaticNextSlide = nChange == 1;
2295
2296 if( !xPropSet.is() ||
2297 !getPropertyValue( nAutomaticNextSlideTimeout,
2298 xPropSet,
2299 u"HighResDuration"_ustr) )
2300 {
2301 SAL_INFO("slideshow",
2302 "queryAutomaticSlideTransition(): "
2303 "Could not extract slide transition timeout from "
2304 "XDrawPage - assuming 1 sec" );
2305 }
2306 }
2307
notifySlideAnimationsEnded()2308 void SlideShowImpl::notifySlideAnimationsEnded()
2309 {
2310 osl::MutexGuard const guard( m_aMutex );
2311
2312 //Draw polygons above animations
2313 mpCurrentSlide->drawPolygons();
2314
2315 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2316
2317 // This struct will receive the (interruptable) event,
2318 // that triggers the notifySlideEnded() method.
2319 InterruptableEventPair aNotificationEvents;
2320
2321 if( maEventMultiplexer.getAutomaticMode() )
2322 {
2323 OSL_ENSURE( ! mpRehearseTimingsActivity,
2324 "unexpected: RehearseTimings mode!" );
2325
2326 // schedule a slide end event, with automatic mode's
2327 // delay
2328 aNotificationEvents = makeInterruptableDelay(
2329 [this]() { return this->notifySlideEnded( false ); },
2330 maEventMultiplexer.getAutomaticTimeout() );
2331 }
2332 else
2333 {
2334 OSL_ENSURE( mpCurrentSlide,
2335 "notifySlideAnimationsEnded(): Invalid current slide!" );
2336
2337 bool bHasAutomaticNextSlide=false;
2338 double nAutomaticNextSlideTimeout=0.0;
2339 queryAutomaticSlideTransition(mpCurrentSlide->getXDrawPage(),
2340 nAutomaticNextSlideTimeout,
2341 bHasAutomaticNextSlide);
2342
2343 // check whether slide transition should happen
2344 // 'automatically'. If yes, simply schedule the
2345 // specified timeout.
2346 // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
2347 // override any individual slide setting, to always
2348 // step slides manually.
2349 if( !mbForceManualAdvance &&
2350 !mpRehearseTimingsActivity &&
2351 bHasAutomaticNextSlide &&
2352 mbMovingForward )
2353 {
2354 aNotificationEvents = makeInterruptableDelay(
2355 [this]() { return this->notifySlideEnded( false ); },
2356 nAutomaticNextSlideTimeout);
2357
2358 // TODO(F2): Provide a mechanism to let the user override
2359 // this automatic timeout via next()
2360 }
2361 else
2362 {
2363 if (mpRehearseTimingsActivity)
2364 mpRehearseTimingsActivity->start();
2365
2366 // generate click event. Thus, the user must
2367 // trigger the actual end of a slide. No need to
2368 // generate interruptable event here, there's no
2369 // timeout involved.
2370 aNotificationEvents.mpImmediateEvent =
2371 makeEvent( [this] () { this->notifySlideEnded(false); },
2372 u"SlideShowImpl::notifySlideEnded"_ustr);
2373 }
2374 }
2375
2376 // register events on the queues. To make automatic slide
2377 // changes interruptable, register the interruption event
2378 // as a nextEffectEvent target. Note that the timeout
2379 // event is optional (e.g. manual slide changes don't
2380 // generate a timeout)
2381 maUserEventQueue.registerNextEffectEvent(
2382 aNotificationEvents.mpImmediateEvent );
2383
2384 if( aNotificationEvents.mpTimeoutEvent )
2385 maEventQueue.addEvent( aNotificationEvents.mpTimeoutEvent );
2386
2387 // current slide's main sequence is over. Now should be
2388 // the time to prefetch the next slide (if any), and
2389 // prepare the initial slide bitmap (speeds up slide
2390 // change setup time a lot). Show the wait cursor, this
2391 // indeed might take some seconds.
2392 {
2393 WaitSymbolLock aLock (*this);
2394
2395 if (! matches( mpPrefetchSlide,
2396 mxPrefetchSlide, mxPrefetchAnimationNode ))
2397 {
2398 mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
2399 mxPrefetchAnimationNode );
2400 }
2401 if (mpPrefetchSlide)
2402 {
2403 // ignore return value, this is just to populate
2404 // Slide's internal bitmap buffer, such that the time
2405 // needed to generate the slide bitmap is not spent
2406 // when the slide change is requested.
2407 mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
2408 }
2409 } // finally
2410
2411 maListenerContainer.forEach(
2412 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
2413 {
2414 xListener->slideAnimationsEnded();
2415 });
2416 }
2417
notifySlideEnded(const bool bReverse)2418 void SlideShowImpl::notifySlideEnded (const bool bReverse)
2419 {
2420 osl::MutexGuard const guard( m_aMutex );
2421
2422 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2423
2424 if (mpRehearseTimingsActivity && !bReverse)
2425 {
2426 const double time = mpRehearseTimingsActivity->stop();
2427 if (mpRehearseTimingsActivity->hasBeenClicked())
2428 {
2429 // save time at current drawpage:
2430 uno::Reference<beans::XPropertySet> xPropSet(
2431 mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
2432 OSL_ASSERT( xPropSet.is() );
2433 if (xPropSet.is())
2434 {
2435 xPropSet->setPropertyValue(
2436 u"Change"_ustr,
2437 uno::Any( static_cast<sal_Int32>(1) ) );
2438 xPropSet->setPropertyValue(
2439 u"Duration"_ustr,
2440 uno::Any( static_cast<sal_Int32>(time) ) );
2441 }
2442 }
2443 }
2444
2445 if (bReverse)
2446 maEventMultiplexer.notifySlideEndEvent();
2447
2448 stopShow(); // MUST call that: results in
2449 // maUserEventQueue.clear(). What's more,
2450 // stopShow()'s currSlide->hide() call is
2451 // now also required, notifySlideEnded()
2452 // relies on that
2453 // unconditionally. Otherwise, genuine
2454 // shape animations (drawing layer and
2455 // GIF) will not be stopped.
2456
2457 maListenerContainer.forEach(
2458 [&bReverse]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2459 { return xListener->slideEnded( bReverse ); } );
2460 }
2461
notifyHyperLinkClicked(OUString const & hyperLink)2462 bool SlideShowImpl::notifyHyperLinkClicked( OUString const& hyperLink )
2463 {
2464 osl::MutexGuard const guard( m_aMutex );
2465
2466 maListenerContainer.forEach(
2467 [&hyperLink]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2468 { return xListener->hyperLinkClicked( hyperLink ); } );
2469 return true;
2470 }
2471
2472 /** Notification from eventmultiplexer that an animation event has occurred.
2473 This will be forwarded to all registered XSlideShoeListener
2474 */
handleAnimationEvent(const AnimationNodeSharedPtr & rNode)2475 bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
2476 {
2477 osl::MutexGuard const guard( m_aMutex );
2478
2479 uno::Reference<animations::XAnimationNode> xNode( rNode->getXAnimationNode() );
2480
2481 switch( rNode->getState() )
2482 {
2483 case AnimationNode::ACTIVE:
2484 maListenerContainer.forEach(
2485 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2486 { return xListener->beginEvent( xNode ); } );
2487 break;
2488
2489 case AnimationNode::FROZEN:
2490 case AnimationNode::ENDED:
2491 maListenerContainer.forEach(
2492 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2493 { return xListener->endEvent( xNode ); } );
2494 if(mpCurrentSlide->isPaintOverlayActive())
2495 mpCurrentSlide->drawPolygons();
2496 break;
2497 default:
2498 break;
2499 }
2500
2501 return true;
2502 }
2503
getMediaTempFile(const OUString & aUrl)2504 std::shared_ptr<avmedia::MediaTempFile> SlideShowImpl::getMediaTempFile(const OUString& aUrl)
2505 {
2506 std::shared_ptr<avmedia::MediaTempFile> aRet;
2507
2508 #if !HAVE_FEATURE_AVMEDIA
2509 (void)aUrl;
2510 #else
2511 if (!mxSBD.is())
2512 return aRet;
2513
2514 comphelper::LifecycleProxy aProxy;
2515 uno::Reference<io::XStream> xStream =
2516 comphelper::OStorageHelper::GetStreamAtPackageURL(mxSBD->getDocumentStorage(), aUrl,
2517 css::embed::ElementModes::READ, aProxy);
2518
2519 uno::Reference<io::XInputStream> xInStream = xStream->getInputStream();
2520 if (xInStream.is())
2521 {
2522 sal_Int32 nLastDot = aUrl.lastIndexOf('.');
2523 sal_Int32 nLastSlash = aUrl.lastIndexOf('/');
2524 OUString sDesiredExtension;
2525 if (nLastDot > nLastSlash && nLastDot+1 < aUrl.getLength())
2526 sDesiredExtension = aUrl.copy(nLastDot);
2527
2528 OUString sTempUrl;
2529 if (::avmedia::CreateMediaTempFile(xInStream, sTempUrl, sDesiredExtension))
2530 aRet = std::make_shared<avmedia::MediaTempFile>(sTempUrl);
2531
2532 xInStream->closeInput();
2533 }
2534 #endif
2535
2536 return aRet;
2537 }
2538
2539 //===== FrameSynchronization ==================================================
2540
FrameSynchronization(const double nFrameDuration)2541 FrameSynchronization::FrameSynchronization (const double nFrameDuration)
2542 : maTimer(),
2543 mnFrameDuration(nFrameDuration),
2544 mnNextFrameTargetTime(0),
2545 mbIsActive(false)
2546 {
2547 MarkCurrentFrame();
2548 }
2549
MarkCurrentFrame()2550 void FrameSynchronization::MarkCurrentFrame()
2551 {
2552 mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2553 }
2554
Synchronize()2555 void FrameSynchronization::Synchronize()
2556 {
2557 if (mbIsActive)
2558 {
2559 // Do busy waiting for now.
2560 for(;;)
2561 {
2562 double remainingTime = mnNextFrameTargetTime - maTimer.getElapsedTime();
2563 if(remainingTime <= 0)
2564 break;
2565 // Try to sleep most of it.
2566 int remainingMilliseconds = remainingTime * 1000;
2567 if(remainingMilliseconds > 2)
2568 std::this_thread::sleep_for(std::chrono::milliseconds(remainingMilliseconds - 2));
2569 }
2570 }
2571
2572 MarkCurrentFrame();
2573 }
2574
Activate()2575 void FrameSynchronization::Activate()
2576 {
2577 mbIsActive = true;
2578 }
2579
Deactivate()2580 void FrameSynchronization::Deactivate()
2581 {
2582 mbIsActive = false;
2583 }
2584
2585 } // anon namespace
2586
2587 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
slideshow_SlideShowImpl_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2588 slideshow_SlideShowImpl_get_implementation(
2589 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
2590 {
2591 return cppu::acquire(new SlideShowImpl(context));
2592 }
2593 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2594