use insert function instead of for loop
[LibreOffice.git] / slideshow / source / engine / slideshowimpl.cxx
blob27da8770b43b09242cec39feb66ef8cf96f77979
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <config_features.h>
22 #include <comphelper/diagnose_ex.hxx>
24 #include <cppuhelper/basemutex.hxx>
25 #include <cppuhelper/compbase.hxx>
26 #include <cppuhelper/interfacecontainer.h>
27 #include <cppuhelper/supportsservice.hxx>
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>
35 #include <tools/debug.hxx>
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>
44 #include <sal/log.hxx>
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>
63 #include <com/sun/star/uno/Reference.hxx>
64 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
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"
90 #include <map>
91 #include <thread>
92 #include <utility>
93 #include <vector>
94 #include <algorithm>
96 using namespace com::sun::star;
97 using namespace ::slideshow::internal;
99 namespace box2d::utils { class box2DWorld;
100 typedef ::std::shared_ptr< box2DWorld > Box2DWorldSharedPtr; }
102 namespace {
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.
110 class FrameSynchronization
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.
118 explicit FrameSynchronization (const double nFrameDuration);
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.
123 void MarkCurrentFrame();
125 /** When there is time left until the next frame is due then wait.
126 Otherwise return without delay.
128 void Synchronize();
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.
134 void Activate();
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.
140 void Deactivate();
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.
147 canvas::tools::ElapsedTime maTimer;
148 /** Time between the display of frames. Enforced only when mbIsActive
149 is <TRUE/>.
151 const double mnFrameDuration;
152 /** Time (of maTimer) when the next frame shall be displayed.
153 Synchronize() will wait until this time.
155 double mnNextFrameTargetTime;
156 /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise
157 it returns immediately.
159 bool mbIsActive;
162 /******************************************************************************
164 SlideShowImpl
166 This class encapsulates the slideshow presentation viewer.
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).
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).
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).
193 ******************************************************************************/
195 typedef cppu::WeakComponentImplHelper<css::lang::XServiceInfo, presentation::XSlideShow> SlideShowImplBase;
197 typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
199 /// Maps XDrawPage for annotations persistence
200 typedef ::std::map< css::uno::Reference<
201 css::drawing::XDrawPage>,
202 PolyPolygonVector> PolygonMap;
204 class SlideShowImpl : private cppu::BaseMutex,
205 public CursorManager,
206 public MediaFileManager,
207 public SlideShowImplBase
209 public:
210 explicit SlideShowImpl(
211 uno::Reference<uno::XComponentContext> xContext );
213 /** Notify that the transition phase of the current slide
214 has ended.
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.
223 This method notifies the end of the first phase.
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.
230 void notifySlideTransitionEnded( bool bPaintSlide );
232 /** Notify that the shape animation phase of the current slide
233 has ended.
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.
242 This method notifies the end of the second phase.
244 void notifySlideAnimationsEnded();
246 /** Notify that the slide has ended.
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.
255 This method notifies the end of the third phase.
257 void notifySlideEnded (const bool bReverse);
259 /** Notification from eventmultiplexer that a hyperlink
260 has been clicked.
262 bool notifyHyperLinkClicked( OUString const& hyperLink );
264 /** Notification from eventmultiplexer that an animation event has occurred.
265 This will be forwarded to all registered XSlideShowListener
267 bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
269 /** Obtain a MediaTempFile for the specified url. */
270 virtual std::shared_ptr<avmedia::MediaTempFile> getMediaTempFile(const OUString& aUrl) override;
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;
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;
313 // CursorManager
316 virtual bool requestCursor( sal_Int16 nCursorShape ) override;
317 virtual void resetCursor() override;
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.
324 void redisplayCurrentSlide();
326 protected:
327 // WeakComponentImplHelperBase
328 virtual void SAL_CALL disposing() override;
330 bool isDisposed() const
332 return (rBHelper.bDisposed || rBHelper.bInDispose);
335 private:
336 struct SeparateListenerImpl; friend struct SeparateListenerImpl;
337 class PrefetchPropertiesFunc; friend class PrefetchPropertiesFunc;
339 /// Stop currently running show.
340 void stopShow();
342 ///Find a polygons vector in maPolygons (map)
343 PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
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 );
351 /// Checks whether the given slide/animation node matches mpPrefetchSlide
352 static bool matches(
353 SlideSharedPtr const& pSlide,
354 uno::Reference<drawing::XDrawPage> const& xSlide,
355 uno::Reference<animations::XAnimationNode> const& xNode )
357 if (pSlide)
358 return (pSlide->getXDrawPage() == xSlide &&
359 pSlide->getXAnimationNode() == xNode);
360 else
361 return (!xSlide.is() && !xNode.is());
364 /// Resets the current slide transition sound object with a new one:
365 SoundPlayerSharedPtr resetSlideTransitionSound(
366 uno::Any const& url, bool bLoopSound );
368 /// stops the current slide transition sound
369 void stopSlideTransitionSound();
371 /** Prepare a slide transition
373 This method registers all necessary events and
374 activities for a slide transition.
376 @return the slide change activity, or NULL for no transition effect
378 ActivitySharedPtr createSlideTransition(
379 const uno::Reference< drawing::XDrawPage >& xDrawPage,
380 const SlideSharedPtr& rLeavingSlide,
381 const SlideSharedPtr& rEnteringSlide,
382 const EventSharedPtr& rTransitionEndEvent );
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.
388 Do not call this method directly. Use WaitSymbolLock instead.
390 void requestWaitSymbol();
391 void releaseWaitSymbol();
393 class WaitSymbolLock {public:
394 explicit WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
395 { mrSlideShowImpl.requestWaitSymbol(); }
396 ~WaitSymbolLock()
397 { mrSlideShowImpl.releaseWaitSymbol(); }
398 private: SlideShowImpl& mrSlideShowImpl;
401 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
402 sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
404 /** This method is called asynchronously to finish the rewinding of an
405 effect to the previous slide that was initiated earlier.
407 void rewindEffectToPreviousSlide();
409 /// all registered views
410 UnoViewContainer maViewContainer;
412 /// all registered slide show listeners
413 comphelper::OInterfaceContainerHelper3<presentation::XSlideShowListener> maListenerContainer;
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;
420 //map of vector of Polygons, containing polygons drawn on each slide.
421 PolygonMap maPolygons;
423 std::optional<RGBColor> maUserPaintColor;
425 double maUserPaintStrokeWidth;
427 //changed for the eraser project
428 std::optional<bool> maEraseAllInk;
429 std::optional<sal_Int32> maEraseInk;
430 //end changed
432 std::shared_ptr<canvas::tools::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;
441 std::shared_ptr<SeparateListenerImpl> mpListener;
443 std::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
444 std::shared_ptr<WaitSymbol> mpWaitSymbol;
446 // navigation buttons
447 std::shared_ptr<SlideOverlayButton> mpNavigationPrev;
448 std::shared_ptr<SlideOverlayButton> mpNavigationMenu;
449 std::shared_ptr<SlideOverlayButton> mpNavigationNext;
451 std::shared_ptr<PointerSymbol> mpPointerSymbol;
453 /// the current slide transition sound object:
454 SoundPlayerSharedPtr mpCurrentSlideTransitionSound;
456 uno::Reference<uno::XComponentContext> mxComponentContext;
457 uno::Reference<
458 presentation::XTransitionFactory> mxOptionalTransitionFactory;
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;
475 sal_Int16 mnCurrentCursor;
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;
488 EffectRewinder maEffectRewinder;
489 FrameSynchronization maFrameSynchronization;
492 /** Separate event listener for animation, view and hyperlink events.
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
498 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
499 public ViewRepaintHandler,
500 public HyperlinkHandler,
501 public AnimationEventHandler
503 SlideShowImpl& mrShow;
504 ScreenUpdater& mrScreenUpdater;
505 EventQueue& mrEventQueue;
507 SeparateListenerImpl( SlideShowImpl& rShow,
508 ScreenUpdater& rScreenUpdater,
509 EventQueue& rEventQueue ) :
510 mrShow( rShow ),
511 mrScreenUpdater( rScreenUpdater ),
512 mrEventQueue( rEventQueue )
515 SeparateListenerImpl( const SeparateListenerImpl& ) = delete;
516 SeparateListenerImpl& operator=( const SeparateListenerImpl& ) = delete;
518 // EventHandler
519 virtual bool handleEvent() override
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;
535 // ViewRepaintHandler
536 virtual void viewClobbered( const UnoViewSharedPtr& rView ) override
538 // given view needs repaint, request update
539 mrScreenUpdater.notifyUpdate(rView, true);
542 // HyperlinkHandler
543 virtual bool handleHyperlink( OUString const& rLink ) override
545 return mrShow.notifyHyperLinkClicked(rLink);
548 // AnimationEventHandler
549 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) override
551 return mrShow.handleAnimationEvent(rNode);
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<canvas::tools::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)
603 // keep care not constructing any UNO references to this inside ctor,
604 // shift that code to create()!
606 uno::Reference<lang::XMultiComponentFactory> xFactory(
607 mxComponentContext->getServiceManager() );
609 if( xFactory.is() )
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 );
620 catch (loader::CannotActivateFactoryException const&)
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 );
636 // we are about to be disposed (someone call dispose() on us)
637 void SlideShowImpl::disposing()
639 osl::MutexGuard const guard( m_aMutex );
641 maEffectRewinder.dispose();
643 // stop slide transition sound, if any:
644 stopSlideTransitionSound();
646 mxComponentContext.clear();
648 if( mpCurrentSlideTransitionSound )
650 mpCurrentSlideTransitionSound->dispose();
651 mpCurrentSlideTransitionSound.reset();
654 mpWaitSymbol.reset();
655 mpPointerSymbol.reset();
657 if( mpRehearseTimingsActivity )
659 mpRehearseTimingsActivity->dispose();
660 mpRehearseTimingsActivity.reset();
663 if( mpListener )
665 maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
666 maEventMultiplexer.removeViewRepaintHandler(mpListener);
667 maEventMultiplexer.removeHyperlinkHandler(mpListener);
668 maEventMultiplexer.removeAnimationStartHandler( mpListener );
669 maEventMultiplexer.removeAnimationEndHandler( mpListener );
671 mpListener.reset();
674 maUserEventQueue.clear();
675 maActivitiesQueue.clear();
676 maEventMultiplexer.clear();
677 maEventQueue.clear();
678 mpPresTimer.reset();
679 maShapeCursors.clear();
680 maShapeEventListeners.clear();
682 // send all listeners a disposing() that we are going down:
683 maListenerContainer.disposeAndClear(
684 lang::EventObject( getXWeak() ) );
686 maViewContainer.dispose();
688 // release slides:
689 mxPrefetchAnimationNode.clear();
690 mxPrefetchSlide.clear();
691 mpPrefetchSlide.reset();
692 mpCurrentSlide.reset();
693 mpPreviousSlide.reset();
696 uno::Sequence< OUString > SAL_CALL SlideShowImpl::getSupportedServiceNames()
698 return { u"com.sun.star.presentation.SlideShow"_ustr };
701 OUString SAL_CALL SlideShowImpl::getImplementationName()
703 return u"com.sun.star.comp.presentation.SlideShow"_ustr;
706 sal_Bool SAL_CALL SlideShowImpl::supportsService(const OUString& aServiceName)
708 return cppu::supportsService(this, aServiceName);
711 /// stops the current slide transition sound
712 void SlideShowImpl::stopSlideTransitionSound()
714 if (mpCurrentSlideTransitionSound)
716 mpCurrentSlideTransitionSound->stopPlayback();
717 mpCurrentSlideTransitionSound->dispose();
718 mpCurrentSlideTransitionSound.reset();
722 SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
724 bool bStopSound = false;
725 OUString url;
727 if( !(rSound >>= bStopSound) )
728 bStopSound = false;
729 rSound >>= url;
731 if( !bStopSound && url.isEmpty() )
732 return SoundPlayerSharedPtr();
734 stopSlideTransitionSound();
736 if (!url.isEmpty())
740 mpCurrentSlideTransitionSound = SoundPlayer::create(
741 maEventMultiplexer, url, mxComponentContext, *this);
742 mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
744 catch (lang::NoSupportException const&)
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).
752 return mpCurrentSlideTransitionSound;
755 ActivitySharedPtr SlideShowImpl::createSlideTransition(
756 const uno::Reference< drawing::XDrawPage >& xDrawPage,
757 const SlideSharedPtr& rLeavingSlide,
758 const SlideSharedPtr& rEnteringSlide,
759 const EventSharedPtr& rTransitionEndEvent)
761 ENSURE_OR_THROW( !maViewContainer.empty(),
762 "createSlideTransition(): No views" );
763 ENSURE_OR_THROW( rEnteringSlide,
764 "createSlideTransition(): No entering slide" );
766 // return empty transition, if slide transitions
767 // are disabled.
768 if (mbNoSlideTransitions)
769 return ActivitySharedPtr();
771 // retrieve slide change parameters from XDrawPage
772 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
773 uno::UNO_QUERY );
775 if( !xPropSet.is() )
777 SAL_INFO("slideshow", "createSlideTransition(): "
778 "Slide has no PropertySet - assuming no transition" );
779 return ActivitySharedPtr();
782 sal_Int16 nTransitionType(0);
783 if( !getPropertyValue( nTransitionType,
784 xPropSet,
785 u"TransitionType"_ustr) )
787 SAL_INFO("slideshow", "createSlideTransition(): "
788 "Could not extract slide transition type from XDrawPage - assuming no transition" );
789 return ActivitySharedPtr();
792 sal_Int16 nTransitionSubType(0);
793 if( !getPropertyValue( nTransitionSubType,
794 xPropSet,
795 u"TransitionSubtype"_ustr) )
797 SAL_INFO("slideshow", "createSlideTransition(): "
798 "Could not extract slide transition subtype from XDrawPage - assuming no transition" );
799 return ActivitySharedPtr();
802 bool bTransitionDirection(false);
803 if( !getPropertyValue( bTransitionDirection,
804 xPropSet,
805 u"TransitionDirection"_ustr) )
807 SAL_INFO("slideshow", "createSlideTransition(): "
808 "Could not extract slide transition direction from XDrawPage - assuming default direction" );
811 sal_Int32 aUnoColor(0);
812 if( !getPropertyValue( aUnoColor,
813 xPropSet,
814 u"TransitionFadeColor"_ustr) )
816 SAL_INFO("slideshow", "createSlideTransition(): "
817 "Could not extract slide transition fade color from XDrawPage - assuming black" );
820 const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
822 uno::Any aSound;
823 bool bLoopSound = false;
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" );
828 if( !getPropertyValue( bLoopSound, xPropSet, u"LoopSound"_ustr ) )
829 SAL_INFO("slideshow", "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound" );
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 ) ));
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.
851 double nTransitionDuration(0.0);
852 if( !getPropertyValue( nTransitionDuration,
853 xPropSet,
854 u"TransitionDuration"_ustr) )
856 SAL_INFO("slideshow", "createSlideTransition(): "
857 "Could not extract slide transition duration from XDrawPage - assuming no transition" );
858 return ActivitySharedPtr();
861 sal_Int32 nMinFrames(5);
862 if( !getPropertyValue( nMinFrames,
863 xPropSet,
864 u"MinimalFrameNumber"_ustr) )
866 SAL_INFO("slideshow", "createSlideTransition(): "
867 "No minimal number of frames given - assuming 5" );
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));
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 ));
896 PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
898 // TODO(P2): optimize research in the map.
899 return maPolygons.find(xDrawPage);
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 )
907 if( !xDrawPage.is() )
908 return SlideSharedPtr();
910 //Retrieve polygons for the current slide
911 PolygonMap::iterator aIter = findPolygons(xDrawPage);
913 const 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) );
934 // prefetch show content (reducing latency for slide
935 // bitmap and effect start later on)
936 pSlide->prefetch();
938 return pSlide;
941 void SlideShowImpl::requestWaitSymbol()
943 ++mnWaitSymbolRequestCount;
944 OSL_ASSERT(mnWaitSymbolRequestCount>0);
946 if (mnWaitSymbolRequestCount == 1)
948 if( !mpWaitSymbol )
950 // fall back to cursor
951 requestCursor(calcActiveCursor(mnCurrentCursor));
953 else
954 mpWaitSymbol->show();
958 void SlideShowImpl::releaseWaitSymbol()
960 --mnWaitSymbolRequestCount;
961 OSL_ASSERT(mnWaitSymbolRequestCount>=0);
963 if (mnWaitSymbolRequestCount == 0)
965 if( !mpWaitSymbol )
967 // fall back to cursor
968 requestCursor(calcActiveCursor(mnCurrentCursor));
970 else
971 mpWaitSymbol->hide();
975 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
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;
985 return nCursorShape;
988 void SlideShowImpl::stopShow()
990 // Force-end running animation
991 // ===========================
992 if (mpCurrentSlide)
994 mpCurrentSlide->hide();
995 //Register polygons in the map
996 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
997 maPolygons.erase(mpCurrentSlide->getXDrawPage());
999 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1002 // clear all queues
1003 maEventQueue.clear();
1004 maActivitiesQueue.clear();
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();
1015 // re-enable automatic effect advancement
1016 // (maEventQueue.clear() above might have killed
1017 // maEventMultiplexer's tick events)
1018 if (mbAutomaticAdvancementMode)
1020 // toggle automatic mode (enabling just again is
1021 // ignored by EventMultiplexer)
1022 maEventMultiplexer.setAutomaticMode( false );
1023 maEventMultiplexer.setAutomaticMode( true );
1027 class SlideShowImpl::PrefetchPropertiesFunc
1029 public:
1030 PrefetchPropertiesFunc( SlideShowImpl * that_,
1031 bool& rbSkipAllMainSequenceEffects,
1032 bool& rbSkipSlideTransition)
1033 : mpSlideShowImpl(that_),
1034 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1035 mrbSkipSlideTransition(rbSkipSlideTransition)
1038 void operator()( beans::PropertyValue const& rProperty ) const {
1039 if (rProperty.Name == "Prefetch" )
1041 uno::Sequence<uno::Any> seq;
1042 if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1044 seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1045 seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1048 else if ( rProperty.Name == "SkipAllMainSequenceEffects" )
1050 rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1052 else if ( rProperty.Name == "SkipSlideTransition" )
1054 rProperty.Value >>= mrbSkipSlideTransition;
1056 else
1058 SAL_WARN( "slideshow", rProperty.Name );
1061 private:
1062 SlideShowImpl *const mpSlideShowImpl;
1063 bool& mrbSkipAllMainSequenceEffects;
1064 bool& mrbSkipSlideTransition;
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 )
1073 osl::MutexGuard const guard( m_aMutex );
1075 if (isDisposed())
1076 return;
1078 maEffectRewinder.setRootAnimationNode(xRootNode);
1079 maEffectRewinder.setCurrentSlide(xSlide);
1081 // precondition: must only be called from the main thread!
1082 DBG_TESTSOLARMUTEX();
1084 mxDrawPagesSupplier = xDrawPages;
1085 mxSBD = uno::Reference<document::XStorageBasedDocument>(mxDrawPagesSupplier, uno::UNO_QUERY);
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.
1096 bool bSkipAllMainSequenceEffects (false);
1097 bool bSkipSlideTransition (false);
1098 std::for_each( rProperties.begin(),
1099 rProperties.end(),
1100 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1102 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1103 if (maViewContainer.empty())
1104 return;
1106 // this here might take some time
1108 WaitSymbolLock aLock (*this);
1110 mpPreviousSlide = mpCurrentSlide;
1111 mpCurrentSlide.reset();
1113 if (matches( mpPrefetchSlide, xSlide, xRootNode ))
1115 // prefetched slide matches:
1116 mpCurrentSlide = mpPrefetchSlide;
1118 else
1119 mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
1121 OSL_ASSERT( mpCurrentSlide );
1122 if (mpCurrentSlide)
1124 basegfx::B2DSize oldSlideSize;
1125 if( mpPreviousSlide )
1126 oldSlideSize = basegfx::B2DSize( mpPreviousSlide->getSlideSize() );
1128 basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
1130 // push new transformation to all views, if size changed
1131 if( !mpPreviousSlide || oldSlideSize != slideSize )
1133 for( const auto& pView : maViewContainer )
1134 pView->setViewSize( slideSize );
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();
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)));
1155 if (bSkipSlideTransition)
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();
1164 if (pSlideChangeActivity)
1166 // factory generated a slide transition - activate it!
1167 maActivitiesQueue.addActivity( pSlideChangeActivity );
1169 else
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));
1179 } // finally
1181 maListenerContainer.forEach(
1182 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1184 xListener->slideTransitionStarted();
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();
1194 void SlideShowImpl::redisplayCurrentSlide()
1196 osl::MutexGuard const guard( m_aMutex );
1198 if (isDisposed())
1199 return;
1201 // precondition: must only be called from the main thread!
1202 DBG_TESTSOLARMUTEX();
1203 stopShow();
1205 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1206 if (maViewContainer.empty())
1207 return;
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));
1215 maListenerContainer.forEach(
1216 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1218 xListener->slideTransitionStarted();
1222 sal_Bool SlideShowImpl::nextEffect()
1224 mbMovingForward = true;
1225 osl::MutexGuard const guard( m_aMutex );
1227 if (isDisposed())
1228 return false;
1230 // precondition: must only be called from the main thread!
1231 DBG_TESTSOLARMUTEX();
1233 if (mbShowPaused)
1234 return true;
1235 else
1236 return maEventMultiplexer.notifyNextEffect();
1239 sal_Bool SlideShowImpl::previousEffect()
1241 mbMovingForward = false;
1242 osl::MutexGuard const guard( m_aMutex );
1244 if (isDisposed())
1245 return false;
1247 // precondition: must only be called from the main thread!
1248 DBG_TESTSOLARMUTEX();
1250 if (mbShowPaused)
1251 return true;
1252 else
1254 return maEffectRewinder.rewind(
1255 maScreenUpdater.createLock(),
1256 [this]() { return this->redisplayCurrentSlide(); },
1257 [this]() { return this->rewindEffectToPreviousSlide(); } );
1261 void SlideShowImpl::rewindEffectToPreviousSlide()
1263 // Show the wait symbol now and prevent it from showing temporary slide
1264 // content while effects are played back.
1265 WaitSymbolLock aLock (*this);
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);
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();
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();
1283 sal_Bool SlideShowImpl::startShapeActivity(
1284 uno::Reference<drawing::XShape> const& /*xShape*/ )
1286 // precondition: must only be called from the main thread!
1287 DBG_TESTSOLARMUTEX();
1289 // TODO(F3): NYI
1290 OSL_FAIL( "not yet implemented!" );
1291 return false;
1294 sal_Bool SlideShowImpl::stopShapeActivity(
1295 uno::Reference<drawing::XShape> const& /*xShape*/ )
1297 // precondition: must only be called from the main thread!
1298 DBG_TESTSOLARMUTEX();
1300 // TODO(F3): NYI
1301 OSL_FAIL( "not yet implemented!" );
1302 return false;
1305 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1307 osl::MutexGuard const guard( m_aMutex );
1309 if (isDisposed())
1310 return false;
1312 // precondition: must only be called from the main thread!
1313 DBG_TESTSOLARMUTEX();
1315 if (bPauseShow)
1316 mpPresTimer->pauseTimer();
1317 else
1318 mpPresTimer->continueTimer();
1320 maEventMultiplexer.notifyPauseMode(bPauseShow);
1322 mbShowPaused = bPauseShow;
1323 return true;
1326 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1328 osl::MutexGuard const guard( m_aMutex );
1330 if (isDisposed())
1331 return uno::Reference<drawing::XDrawPage>();
1333 // precondition: must only be called from the main thread!
1334 DBG_TESTSOLARMUTEX();
1336 if (mpCurrentSlide)
1337 return mpCurrentSlide->getXDrawPage();
1338 else
1339 return uno::Reference<drawing::XDrawPage>();
1342 sal_Bool SlideShowImpl::addView(
1343 uno::Reference<presentation::XSlideShowView> const& xView )
1345 osl::MutexGuard const guard( m_aMutex );
1347 if (isDisposed())
1348 return false;
1350 // precondition: must only be called from the main thread!
1351 DBG_TESTSOLARMUTEX();
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" );
1358 UnoViewSharedPtr const pView( createSlideView(
1359 xView,
1360 maEventQueue,
1361 maEventMultiplexer ));
1362 if (!maViewContainer.addView( pView ))
1363 return false; // view already added
1365 // initialize view content
1366 // =======================
1368 if (mpCurrentSlide)
1370 // set view transformation
1371 const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
1372 pView->setViewSize( basegfx::B2DSize(slideSize) );
1375 // clear view area (since it's newly added,
1376 // we need a clean slate)
1377 pView->clearAll();
1379 // broadcast newly added view
1380 maEventMultiplexer.notifyViewAdded( pView );
1382 // set current mouse ptr
1383 pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1385 return true;
1388 sal_Bool SlideShowImpl::removeView(
1389 uno::Reference<presentation::XSlideShowView> const& xView )
1391 osl::MutexGuard const guard( m_aMutex );
1393 // precondition: must only be called from the main thread!
1394 DBG_TESTSOLARMUTEX();
1396 ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
1398 UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
1399 if( !pView )
1400 return false; // view was not added in the first place
1402 // remove view from EventMultiplexer (mouse events etc.)
1403 maEventMultiplexer.notifyViewRemoved( pView );
1405 pView->_dispose();
1407 return true;
1410 drawing::PointSequenceSequence
1411 lcl_createPointSequenceSequenceFromB2DPolygon(const basegfx::B2DPolygon& rPoly)
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++ )
1424 //Create a point from the polygon
1425 *pInnerSequence++ = awt::Point(basegfx::fround(rPoly.getB2DPoint(n).getX()),
1426 basegfx::fround(rPoly.getB2DPoint(n).getY()));
1428 return aRetval;
1431 void lcl_setPropertiesToShape(const drawing::PointSequenceSequence& rPoints,
1432 const cppcanvas::PolyPolygonSharedPtr pCanvasPolyPoly,
1433 uno::Reference< drawing::XShape >& rPolyShape)
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);
1441 //LineStyle : SOLID by default
1442 drawing::LineStyle eLS;
1443 eLS = drawing::LineStyle_SOLID;
1444 aXPropSet->setPropertyValue(u"LineStyle"_ustr, uno::Any(eLS));
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));
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)));
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)));
1465 void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory )
1467 //Retrieve Polygons if user ends presentation by context menu
1468 if (mpCurrentSlide)
1470 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1471 maPolygons.erase(mpCurrentSlide->getXDrawPage());
1473 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
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);
1482 // create layer
1483 uno::Reference< drawing::XLayer > xDrawnInSlideshow;
1484 uno::Any aPropLayer;
1485 OUString sLayerName = u"DrawnInSlideshow"_ustr;
1486 if (xNameAccess->hasByName(sLayerName))
1488 xNameAccess->getByName(sLayerName) >>= xDrawnInSlideshow;
1490 else
1492 xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
1493 aPropLayer <<= sLayerName;
1494 xDrawnInSlideshow->setPropertyValue(u"Name"_ustr, aPropLayer);
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);
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)
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;
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)
1527 // Actually, each item in aPolygons has two points, but wrapped in a cppcanvas::PopyPolygon.
1528 ::basegfx::B2DPolyPolygon b2DPolyPoly
1529 = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(
1530 pPolyPoly->getUNOPolyPolygon());
1532 //Normally there is only one polygon
1533 for (sal_uInt32 i = 0; i < b2DPolyPoly.count(); i++)
1535 const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
1537 if (aPoly.count() > 1) // otherwise skip it, count should be 2
1539 const auto ptCount = aDrawingPoints.count();
1540 if (ptCount == 0)
1542 aDrawingPoints.append(aPoly);
1543 pFirstPolyPoly = pPolyPoly;
1544 continue;
1546 basegfx::B2DPoint aLast
1547 = aDrawingPoints.getB2DPoint(ptCount - 1);
1548 if (aPoly.getB2DPoint(0).equal(aLast))
1550 aDrawingPoints.append(aPoly, 1);
1551 continue;
1554 // Put what we have collected to the slide and then start a new pen drawing object
1555 //create the PolyLineShape. The points will be in its PolyPolygon property.
1556 uno::Reference<uno::XInterface> polyshape(
1557 xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
1558 uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY);
1559 //Add the shape to the slide
1560 Shapes->add(rPolyShape);
1561 //Construct a sequence of points sequence
1562 aDrawingPoints
1563 = basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance);
1564 const drawing::PointSequenceSequence aRetval
1565 = lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints);
1566 //Fill the properties
1567 lcl_setPropertiesToShape(aRetval, pFirstPolyPoly, rPolyShape);
1568 // make polygons special
1569 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1570 // Start next pen drawing object
1571 aDrawingPoints.clear();
1572 aDrawingPoints.append(aPoly);
1573 pFirstPolyPoly = pPolyPoly;
1577 // Bring remaining points to slide
1578 if (aDrawingPoints.count() > 1)
1580 //create the PolyLineShape. The points will be in its PolyPolygon property.
1581 uno::Reference<uno::XInterface> polyshape(
1582 xDocFactory->createInstance(u"com.sun.star.drawing.PolyLineShape"_ustr));
1583 uno::Reference<drawing::XShape> rPolyShape(polyshape, uno::UNO_QUERY);
1584 //Add the shape to the slide
1585 Shapes->add(rPolyShape);
1586 //Construct a sequence of points sequence
1587 aDrawingPoints = basegfx::utils::createSimplifiedPolygon(aDrawingPoints, fTolerance);
1588 drawing::PointSequenceSequence aRetval
1589 = lcl_createPointSequenceSequenceFromB2DPolygon(aDrawingPoints);
1590 //Fill the properties
1591 lcl_setPropertiesToShape(aRetval, aPolygons.back(), rPolyShape);
1592 // make polygons special
1593 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1598 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1600 osl::MutexGuard const guard( m_aMutex );
1602 if (isDisposed())
1603 return false;
1605 // precondition: must only be called from the main thread!
1606 DBG_TESTSOLARMUTEX();
1608 // tdf#160669 IASS: if hint is about PrefetchSlide, flush it to avoid errors
1609 if ( rProperty.Name == "HintSlideChanged" )
1611 uno::Reference< drawing::XDrawPage > xDrawPage;
1612 if (rProperty.Value >>= xDrawPage)
1614 if (xDrawPage == mxPrefetchSlide)
1616 mxPrefetchSlide.clear();
1617 mpPrefetchSlide.reset();
1622 if ( rProperty.Name == "AutomaticAdvancement" )
1624 double nTimeout(0.0);
1625 mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
1626 if (mbAutomaticAdvancementMode)
1628 maEventMultiplexer.setAutomaticTimeout( nTimeout );
1630 maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
1631 return true;
1634 if ( rProperty.Name == "UserPaintColor" )
1636 sal_Int32 nColor(0);
1637 if (rProperty.Value >>= nColor)
1639 OSL_ENSURE( mbMouseVisible,
1640 "setProperty(): User paint overrides invisible mouse" );
1642 // enable user paint
1643 maUserPaintColor = unoColor2RGBColor(nColor);
1644 if( mpCurrentSlide && !mpCurrentSlide->isPaintOverlayActive() )
1645 mpCurrentSlide->enablePaintOverlay();
1647 maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
1649 else
1651 // disable user paint
1652 maUserPaintColor.reset();
1653 maEventMultiplexer.notifyUserPaintDisabled();
1656 resetCursor();
1658 return true;
1661 //adding support for erasing features in UserPaintOverlay
1662 if ( rProperty.Name == "EraseAllInk" )
1664 bool bEraseAllInk(false);
1665 if (rProperty.Value >>= bEraseAllInk)
1667 OSL_ENSURE( mbMouseVisible,
1668 "setProperty(): User paint overrides invisible mouse" );
1670 // enable user paint
1671 maEraseAllInk = bEraseAllInk;
1672 maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
1675 return true;
1678 if ( rProperty.Name == "SwitchPenMode" )
1680 bool bSwitchPenMode(false);
1681 if (rProperty.Value >>= bSwitchPenMode)
1683 OSL_ENSURE( mbMouseVisible,
1684 "setProperty(): User paint overrides invisible mouse" );
1686 if(bSwitchPenMode){
1687 // Switch to Pen Mode
1688 maEventMultiplexer.notifySwitchPenMode();
1691 return true;
1694 if ( rProperty.Name == "SwitchEraserMode" )
1696 bool bSwitchEraserMode(false);
1697 if (rProperty.Value >>= bSwitchEraserMode)
1699 OSL_ENSURE( mbMouseVisible,
1700 "setProperty(): User paint overrides invisible mouse" );
1701 if(bSwitchEraserMode){
1702 // switch to Eraser mode
1703 maEventMultiplexer.notifySwitchEraserMode();
1707 return true;
1710 if ( rProperty.Name == "EraseInk" )
1712 sal_Int32 nEraseInk(100);
1713 if (rProperty.Value >>= nEraseInk)
1715 OSL_ENSURE( mbMouseVisible,
1716 "setProperty(): User paint overrides invisible mouse" );
1718 // enable user paint
1719 maEraseInk = nEraseInk;
1720 maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
1723 return true;
1726 // new Property for pen's width
1727 if ( rProperty.Name == "UserPaintStrokeWidth" )
1729 double nWidth(4.0);
1730 if (rProperty.Value >>= nWidth)
1732 OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
1733 // enable user paint stroke width
1734 maUserPaintStrokeWidth = nWidth;
1735 maEventMultiplexer.notifyUserPaintStrokeWidth( maUserPaintStrokeWidth );
1738 return true;
1741 if ( rProperty.Name == "AdvanceOnClick" )
1743 bool bAdvanceOnClick = false;
1744 if (! (rProperty.Value >>= bAdvanceOnClick))
1745 return false;
1746 maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1747 return true;
1750 if ( rProperty.Name == "DisableAnimationZOrder" )
1752 bool bDisableAnimationZOrder = false;
1753 if (! (rProperty.Value >>= bDisableAnimationZOrder))
1754 return false;
1755 mbDisableAnimationZOrder = bDisableAnimationZOrder;
1756 return true;
1759 if ( rProperty.Name == "ImageAnimationsAllowed" )
1761 if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1762 return false;
1764 // TODO(F3): Forward to slides!
1765 return true;
1768 if ( rProperty.Name == "MouseVisible" )
1770 if (! (rProperty.Value >>= mbMouseVisible))
1771 return false;
1773 requestCursor(mnCurrentCursor);
1775 return true;
1778 if ( rProperty.Name == "ForceManualAdvance" )
1780 return (rProperty.Value >>= mbForceManualAdvance);
1783 if ( rProperty.Name == "RehearseTimings" )
1785 bool bRehearseTimings = false;
1786 if (! (rProperty.Value >>= bRehearseTimings))
1787 return false;
1789 if (bRehearseTimings)
1791 // TODO(Q3): Move to slide
1792 mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1793 SlideShowContext(
1794 mpDummyPtr,
1795 maEventQueue,
1796 maEventMultiplexer,
1797 maScreenUpdater,
1798 maActivitiesQueue,
1799 maUserEventQueue,
1800 *this,
1801 *this,
1802 maViewContainer,
1803 mxComponentContext,
1804 mpBox2DDummyPtr ) );
1806 else if (mpRehearseTimingsActivity)
1808 // removes timer from all views:
1809 mpRehearseTimingsActivity->dispose();
1810 mpRehearseTimingsActivity.reset();
1812 return true;
1815 if ( rProperty.Name == "WaitSymbolBitmap" )
1817 uno::Reference<rendering::XBitmap> xBitmap;
1818 if (! (rProperty.Value >>= xBitmap))
1819 return false;
1821 mpWaitSymbol = WaitSymbol::create( xBitmap,
1822 maScreenUpdater,
1823 maEventMultiplexer,
1824 maViewContainer );
1826 return true;
1829 if (rProperty.Name == "NavigationSlidePrev")
1831 uno::Reference<rendering::XBitmap> xBitmap;
1832 if (!(rProperty.Value >>= xBitmap))
1833 return false;
1835 mpNavigationPrev = SlideOverlayButton::create(
1836 xBitmap, { 20, 10 }, [this](basegfx::B2DPoint) { notifySlideEnded(true); },
1837 maScreenUpdater, maEventMultiplexer, maViewContainer);
1839 return true;
1842 if (rProperty.Name == "NavigationSlideMenu")
1844 uno::Reference<rendering::XBitmap> xBitmap;
1845 if (!(rProperty.Value >>= xBitmap))
1846 return false;
1848 mpNavigationMenu = SlideOverlayButton::create(
1849 xBitmap, { xBitmap->getSize().Width + 48, 10 },
1850 [this](basegfx::B2DPoint pos) {
1851 maListenerContainer.forEach(
1852 [pos](const uno::Reference<presentation::XSlideShowListener>& xListener) {
1853 uno::Reference<presentation::XSlideShowNavigationListener> xNavListener(
1854 xListener, uno::UNO_QUERY);
1855 if (xNavListener.is())
1856 xNavListener->contextMenuShow(
1857 css::awt::Point(static_cast<sal_Int32>(floor(pos.getX())),
1858 static_cast<sal_Int32>(floor(pos.getY()))));
1861 maScreenUpdater, maEventMultiplexer, maViewContainer);
1863 return true;
1866 if (rProperty.Name == "NavigationSlideNext")
1868 uno::Reference<rendering::XBitmap> xBitmap;
1869 if (!(rProperty.Value >>= xBitmap))
1870 return false;
1872 mpNavigationNext = SlideOverlayButton::create(
1873 xBitmap, { 2 * xBitmap->getSize().Width + 76, 10 }, [this](basegfx::B2DPoint) { notifySlideEnded(false); },
1874 maScreenUpdater, maEventMultiplexer, maViewContainer);
1876 return true;
1879 if ( rProperty.Name == "PointerSymbolBitmap" )
1881 uno::Reference<rendering::XBitmap> xBitmap;
1882 if (! (rProperty.Value >>= xBitmap))
1883 return false;
1885 mpPointerSymbol = PointerSymbol::create( xBitmap,
1886 maScreenUpdater,
1887 maEventMultiplexer,
1888 maViewContainer );
1890 return true;
1893 if ( rProperty.Name == "PointerVisible" )
1895 bool visible;
1896 if (!(rProperty.Value >>= visible))
1897 return false;
1899 if (!mbShowPaused)
1900 mpPointerSymbol->setVisible(visible);
1902 return true;
1905 if ( rProperty.Name == "PointerPosition")
1907 css::geometry::RealPoint2D pos;
1908 if (! (rProperty.Value >>= pos))
1909 return false;
1911 if (!mbShowPaused)
1912 mpPointerSymbol->viewsChanged(pos);
1914 return true;
1917 if (rProperty.Name == "NoSlideTransitions" )
1919 return (rProperty.Value >>= mbNoSlideTransitions);
1922 if ( rProperty.Name == "IsSoundEnabled" )
1924 uno::Sequence<uno::Any> aValues;
1925 uno::Reference<presentation::XSlideShowView> xView;
1926 bool bValue (false);
1927 if ((rProperty.Value >>= aValues)
1928 && aValues.getLength()==2
1929 && (aValues[0] >>= xView)
1930 && (aValues[1] >>= bValue))
1932 // Look up the view.
1933 auto iView = std::find_if(maViewContainer.begin(), maViewContainer.end(),
1934 [&xView](const UnoViewSharedPtr& rxView) { return rxView && rxView->getUnoView() == xView; });
1935 if (iView != maViewContainer.end())
1937 // Store the flag at the view so that media shapes have
1938 // access to it.
1939 (*iView)->setIsSoundEnabled(bValue);
1940 return true;
1945 return false;
1948 void SlideShowImpl::addSlideShowListener(
1949 uno::Reference<presentation::XSlideShowListener> const& xListener )
1951 osl::MutexGuard const guard( m_aMutex );
1953 if (isDisposed())
1954 return;
1956 // container syncs with passed mutex ref
1957 maListenerContainer.addInterface(xListener);
1960 void SlideShowImpl::removeSlideShowListener(
1961 uno::Reference<presentation::XSlideShowListener> const& xListener )
1963 osl::MutexGuard const guard( m_aMutex );
1965 // container syncs with passed mutex ref
1966 maListenerContainer.removeInterface(xListener);
1969 void SlideShowImpl::addShapeEventListener(
1970 uno::Reference<presentation::XShapeEventListener> const& xListener,
1971 uno::Reference<drawing::XShape> const& xShape )
1973 osl::MutexGuard const guard( m_aMutex );
1975 if (isDisposed())
1976 return;
1978 // precondition: must only be called from the main thread!
1979 DBG_TESTSOLARMUTEX();
1981 ShapeEventListenerMap::iterator aIter;
1982 if( (aIter=maShapeEventListeners.find( xShape )) ==
1983 maShapeEventListeners.end() )
1985 // no entry for this shape -> create one
1986 aIter = maShapeEventListeners.emplace(
1987 xShape,
1988 std::make_shared<comphelper::OInterfaceContainerHelper3<css::presentation::XShapeEventListener>>(
1989 m_aMutex)).first;
1992 // add new listener to broadcaster
1993 if( aIter->second )
1994 aIter->second->addInterface( xListener );
1996 maEventMultiplexer.notifyShapeListenerAdded(xShape);
1999 void SlideShowImpl::removeShapeEventListener(
2000 uno::Reference<presentation::XShapeEventListener> const& xListener,
2001 uno::Reference<drawing::XShape> const& xShape )
2003 osl::MutexGuard const guard( m_aMutex );
2005 // precondition: must only be called from the main thread!
2006 DBG_TESTSOLARMUTEX();
2008 ShapeEventListenerMap::iterator aIter;
2009 if( (aIter = maShapeEventListeners.find( xShape )) !=
2010 maShapeEventListeners.end() )
2012 // entry for this shape found -> remove listener from
2013 // helper object
2014 ENSURE_OR_THROW(
2015 aIter->second,
2016 "SlideShowImpl::removeShapeEventListener(): "
2017 "listener map contains NULL broadcast helper" );
2019 aIter->second->removeInterface( xListener );
2022 maEventMultiplexer.notifyShapeListenerRemoved(xShape);
2025 void SlideShowImpl::setShapeCursor(
2026 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
2028 osl::MutexGuard const guard( m_aMutex );
2030 if (isDisposed())
2031 return;
2033 // precondition: must only be called from the main thread!
2034 DBG_TESTSOLARMUTEX();
2036 ShapeCursorMap::iterator aIter;
2037 if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
2039 // no entry for this shape -> create one
2040 if( nPointerShape != awt::SystemPointer::ARROW )
2042 // add new entry, unless shape shall display
2043 // normal pointer arrow -> no need to handle that
2044 // case
2045 maShapeCursors.emplace(xShape, nPointerShape);
2048 else if( nPointerShape == awt::SystemPointer::ARROW )
2050 // shape shall display normal cursor -> can disable
2051 // the cursor and clear the entry
2052 maShapeCursors.erase( xShape );
2054 else
2056 // existing entry found, update with new cursor ID
2057 aIter->second = nPointerShape;
2061 bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape )
2063 mnCurrentCursor = nCursorShape;
2065 const sal_Int16 nActualCursor = calcActiveCursor(mnCurrentCursor);
2067 // change all views to the requested cursor ID
2068 for( const auto& pView : maViewContainer )
2069 pView->setCursorShape( nActualCursor );
2071 return nActualCursor==nCursorShape;
2074 void SlideShowImpl::resetCursor()
2076 mnCurrentCursor = awt::SystemPointer::ARROW;
2078 const sal_Int16 nActualCursor = calcActiveCursor( mnCurrentCursor );
2079 // change all views to the default cursor ID
2080 for( const auto& pView : maViewContainer )
2081 pView->setCursorShape( nActualCursor );
2084 sal_Bool SlideShowImpl::update( double & nNextTimeout )
2086 osl::MutexGuard const guard( m_aMutex );
2088 if (isDisposed())
2089 return false;
2091 // precondition: update() must only be called from the
2092 // main thread!
2093 DBG_TESTSOLARMUTEX();
2095 if( mbShowPaused )
2097 // commit frame (might be repaints pending)
2098 maScreenUpdater.commitUpdates();
2100 return false;
2102 else
2104 // TODO(F2): re-evaluate whether that timer lagging makes
2105 // sense.
2107 // hold timer, while processing the queues:
2108 // 1. when there is more than one active activity this ensures the
2109 // same time for all activities and events
2110 // 2. processing of events may lead to creation of further events
2111 // that have zero delay. While the timer is stopped these events
2112 // are processed in the same run.
2114 //Get a shared-ptr that outlives the scope-guard which will
2115 //ensure that the pointed-to-item exists in the case of a
2116 //::dispose clearing mpPresTimer
2117 std::shared_ptr<canvas::tools::ElapsedTime> xTimer(mpPresTimer);
2118 comphelper::ScopeGuard scopeGuard(
2119 [&xTimer]() { return xTimer->releaseTimer(); } );
2120 xTimer->holdTimer();
2122 // process queues
2123 maEventQueue.process();
2125 // #i118671# the call above may execute a macro bound to an object. In
2126 // that case this macro may have destroyed this local slideshow so that it
2127 // is disposed (see bugdoc at task). In that case, detect this and exit
2128 // gently from this slideshow. Do not forget to disable the scoped
2129 // call to mpPresTimer, this will be deleted if we are disposed.
2130 if (isDisposed())
2132 scopeGuard.dismiss();
2133 return false;
2136 maActivitiesQueue.process();
2138 // commit frame to screen
2139 maFrameSynchronization.Synchronize();
2140 maScreenUpdater.commitUpdates();
2142 // TODO(Q3): remove need to call dequeued() from
2143 // activities. feels like a wart.
2145 // Rationale for ActivitiesQueue::processDequeued(): when
2146 // an activity ends, it usually pushed the end state to
2147 // the animated shape in question, and ends the animation
2148 // (which, in turn, will usually disable shape sprite
2149 // mode). Disabling shape sprite mode causes shape
2150 // repaint, which, depending on slide content, takes
2151 // considerably more time than sprite updates. Thus, the
2152 // last animation step tends to look delayed. To
2153 // camouflage this, reaching end position and disabling
2154 // sprite mode is split into two (normal Activity::end(),
2155 // and Activity::dequeued()). Now, the reason to call
2156 // commitUpdates() twice here is caused by the unrelated
2157 // fact that during wait cursor display/hide, the screen
2158 // is updated, and shows hidden sprites, but, in case of
2159 // leaving the second commitUpdates() call out and punting
2160 // that to the next round, no updated static slide
2161 // content. In short, the last shape animation of a slide
2162 // tends to blink at its end.
2164 // process dequeued activities _after_ commit to screen
2165 maActivitiesQueue.processDequeued();
2167 // commit frame to screen
2168 maScreenUpdater.commitUpdates();
2170 // Time held until here
2172 const bool bActivitiesLeft = ! maActivitiesQueue.isEmpty();
2173 const bool bTimerEventsLeft = ! maEventQueue.isEmpty();
2174 const bool bRet = (bActivitiesLeft || bTimerEventsLeft);
2176 if (bRet)
2178 // calc nNextTimeout value:
2179 if (bActivitiesLeft)
2181 // Activity queue is not empty. Tell caller that we would
2182 // like to render another frame.
2184 // Return a zero time-out to signal our caller to call us
2185 // back as soon as possible. The actual timing, waiting the
2186 // appropriate amount of time between frames, is then done
2187 // by the maFrameSynchronization object.
2188 nNextTimeout = 0;
2189 maFrameSynchronization.Activate();
2191 else
2193 // timer events left:
2194 // difference from current time (nota bene:
2195 // time no longer held here!) to the next event in
2196 // the event queue.
2198 // #i61190# Retrieve next timeout only _after_
2199 // processing activity queue
2201 // ensure positive value:
2202 nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() );
2204 // There is no active animation so the frame rate does not
2205 // need to be synchronized.
2206 maFrameSynchronization.Deactivate();
2209 mbSlideShowIdle = false;
2212 #if defined(DBG_UTIL)
2213 // when slideshow is idle, issue an XUpdatable::update() call
2214 // exactly once after a previous animation sequence finished -
2215 // this might trigger screen dumps on some canvas
2216 // implementations
2217 if( !mbSlideShowIdle &&
2218 (!bRet ||
2219 nNextTimeout > 1.0) )
2221 for( const auto& pView : maViewContainer )
2225 uno::Reference< presentation::XSlideShowView > xView( pView->getUnoView(),
2226 uno::UNO_SET_THROW );
2227 uno::Reference<util::XUpdatable> const xUpdatable(
2228 xView->getCanvas(), uno::UNO_QUERY);
2229 if (xUpdatable.is()) // not supported in PresenterCanvas
2231 xUpdatable->update();
2234 catch( uno::RuntimeException& )
2236 throw;
2238 catch( uno::Exception& )
2240 TOOLS_WARN_EXCEPTION( "slideshow", "" );
2244 mbSlideShowIdle = true;
2246 #endif
2248 return bRet;
2252 void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide )
2254 osl::MutexGuard const guard( m_aMutex );
2256 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2257 OSL_ENSURE( mpCurrentSlide,
2258 "notifySlideTransitionEnded(): Invalid current slide" );
2259 if (mpCurrentSlide)
2261 mpCurrentSlide->update_settings( !!maUserPaintColor, maUserPaintColor ? *maUserPaintColor : RGBColor(), maUserPaintStrokeWidth );
2263 // first init show, to give the animations
2264 // the chance to register SlideStartEvents
2265 const bool bBackgroundLayerRendered( !bPaintSlide );
2266 mpCurrentSlide->show( bBackgroundLayerRendered );
2267 maEventMultiplexer.notifySlideStartEvent();
2271 void queryAutomaticSlideTransition( uno::Reference<drawing::XDrawPage> const& xDrawPage,
2272 double& nAutomaticNextSlideTimeout,
2273 bool& bHasAutomaticNextSlide )
2275 // retrieve slide change parameters from XDrawPage
2276 // ===============================================
2278 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
2279 uno::UNO_QUERY );
2281 sal_Int32 nChange(0);
2282 if( !xPropSet.is() ||
2283 !getPropertyValue( nChange,
2284 xPropSet,
2285 u"Change"_ustr) )
2287 SAL_INFO("slideshow",
2288 "queryAutomaticSlideTransition(): "
2289 "Could not extract slide change mode from XDrawPage - assuming <none>" );
2292 bHasAutomaticNextSlide = nChange == 1;
2294 if( !xPropSet.is() ||
2295 !getPropertyValue( nAutomaticNextSlideTimeout,
2296 xPropSet,
2297 u"HighResDuration"_ustr) )
2299 SAL_INFO("slideshow",
2300 "queryAutomaticSlideTransition(): "
2301 "Could not extract slide transition timeout from "
2302 "XDrawPage - assuming 1 sec" );
2306 void SlideShowImpl::notifySlideAnimationsEnded()
2308 osl::MutexGuard const guard( m_aMutex );
2310 //Draw polygons above animations
2311 mpCurrentSlide->drawPolygons();
2313 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2315 // This struct will receive the (interruptable) event,
2316 // that triggers the notifySlideEnded() method.
2317 InterruptableEventPair aNotificationEvents;
2319 if( maEventMultiplexer.getAutomaticMode() )
2321 OSL_ENSURE( ! mpRehearseTimingsActivity,
2322 "unexpected: RehearseTimings mode!" );
2324 // schedule a slide end event, with automatic mode's
2325 // delay
2326 aNotificationEvents = makeInterruptableDelay(
2327 [this]() { return this->notifySlideEnded( false ); },
2328 maEventMultiplexer.getAutomaticTimeout() );
2330 else
2332 OSL_ENSURE( mpCurrentSlide,
2333 "notifySlideAnimationsEnded(): Invalid current slide!" );
2335 bool bHasAutomaticNextSlide=false;
2336 double nAutomaticNextSlideTimeout=0.0;
2337 queryAutomaticSlideTransition(mpCurrentSlide->getXDrawPage(),
2338 nAutomaticNextSlideTimeout,
2339 bHasAutomaticNextSlide);
2341 // check whether slide transition should happen
2342 // 'automatically'. If yes, simply schedule the
2343 // specified timeout.
2344 // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
2345 // override any individual slide setting, to always
2346 // step slides manually.
2347 if( !mbForceManualAdvance &&
2348 !mpRehearseTimingsActivity &&
2349 bHasAutomaticNextSlide &&
2350 mbMovingForward )
2352 aNotificationEvents = makeInterruptableDelay(
2353 [this]() { return this->notifySlideEnded( false ); },
2354 nAutomaticNextSlideTimeout);
2356 // TODO(F2): Provide a mechanism to let the user override
2357 // this automatic timeout via next()
2359 else
2361 if (mpRehearseTimingsActivity)
2362 mpRehearseTimingsActivity->start();
2364 // generate click event. Thus, the user must
2365 // trigger the actual end of a slide. No need to
2366 // generate interruptable event here, there's no
2367 // timeout involved.
2368 aNotificationEvents.mpImmediateEvent =
2369 makeEvent( [this] () { this->notifySlideEnded(false); },
2370 u"SlideShowImpl::notifySlideEnded"_ustr);
2374 // register events on the queues. To make automatic slide
2375 // changes interruptable, register the interruption event
2376 // as a nextEffectEvent target. Note that the timeout
2377 // event is optional (e.g. manual slide changes don't
2378 // generate a timeout)
2379 maUserEventQueue.registerNextEffectEvent(
2380 aNotificationEvents.mpImmediateEvent );
2382 if( aNotificationEvents.mpTimeoutEvent )
2383 maEventQueue.addEvent( aNotificationEvents.mpTimeoutEvent );
2385 // current slide's main sequence is over. Now should be
2386 // the time to prefetch the next slide (if any), and
2387 // prepare the initial slide bitmap (speeds up slide
2388 // change setup time a lot). Show the wait cursor, this
2389 // indeed might take some seconds.
2391 WaitSymbolLock aLock (*this);
2393 if (! matches( mpPrefetchSlide,
2394 mxPrefetchSlide, mxPrefetchAnimationNode ))
2396 mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
2397 mxPrefetchAnimationNode );
2399 if (mpPrefetchSlide)
2401 // ignore return value, this is just to populate
2402 // Slide's internal bitmap buffer, such that the time
2403 // needed to generate the slide bitmap is not spent
2404 // when the slide change is requested.
2405 mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
2407 } // finally
2409 maListenerContainer.forEach(
2410 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
2412 xListener->slideAnimationsEnded();
2416 void SlideShowImpl::notifySlideEnded (const bool bReverse)
2418 osl::MutexGuard const guard( m_aMutex );
2420 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2422 if (mpRehearseTimingsActivity && !bReverse)
2424 const double time = mpRehearseTimingsActivity->stop();
2425 if (mpRehearseTimingsActivity->hasBeenClicked())
2427 // save time at current drawpage:
2428 uno::Reference<beans::XPropertySet> xPropSet(
2429 mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
2430 OSL_ASSERT( xPropSet.is() );
2431 if (xPropSet.is())
2433 xPropSet->setPropertyValue(
2434 u"Change"_ustr,
2435 uno::Any( static_cast<sal_Int32>(1) ) );
2436 xPropSet->setPropertyValue(
2437 u"Duration"_ustr,
2438 uno::Any( static_cast<sal_Int32>(time) ) );
2443 if (bReverse)
2444 maEventMultiplexer.notifySlideEndEvent();
2446 stopShow(); // MUST call that: results in
2447 // maUserEventQueue.clear(). What's more,
2448 // stopShow()'s currSlide->hide() call is
2449 // now also required, notifySlideEnded()
2450 // relies on that
2451 // unconditionally. Otherwise, genuine
2452 // shape animations (drawing layer and
2453 // GIF) will not be stopped.
2455 maListenerContainer.forEach(
2456 [&bReverse]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2457 { return xListener->slideEnded( bReverse ); } );
2460 bool SlideShowImpl::notifyHyperLinkClicked( OUString const& hyperLink )
2462 osl::MutexGuard const guard( m_aMutex );
2464 maListenerContainer.forEach(
2465 [&hyperLink]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2466 { return xListener->hyperLinkClicked( hyperLink ); } );
2467 return true;
2470 /** Notification from eventmultiplexer that an animation event has occurred.
2471 This will be forwarded to all registered XSlideShoeListener
2473 bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
2475 osl::MutexGuard const guard( m_aMutex );
2477 uno::Reference<animations::XAnimationNode> xNode( rNode->getXAnimationNode() );
2479 switch( rNode->getState() )
2481 case AnimationNode::ACTIVE:
2482 maListenerContainer.forEach(
2483 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2484 { return xListener->beginEvent( xNode ); } );
2485 break;
2487 case AnimationNode::FROZEN:
2488 case AnimationNode::ENDED:
2489 maListenerContainer.forEach(
2490 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2491 { return xListener->endEvent( xNode ); } );
2492 if(mpCurrentSlide->isPaintOverlayActive())
2493 mpCurrentSlide->drawPolygons();
2494 break;
2495 default:
2496 break;
2499 return true;
2502 std::shared_ptr<avmedia::MediaTempFile> SlideShowImpl::getMediaTempFile(const OUString& aUrl)
2504 std::shared_ptr<avmedia::MediaTempFile> aRet;
2506 #if !HAVE_FEATURE_AVMEDIA
2507 (void)aUrl;
2508 #else
2509 if (!mxSBD.is())
2510 return aRet;
2512 comphelper::LifecycleProxy aProxy;
2513 uno::Reference<io::XStream> xStream =
2514 comphelper::OStorageHelper::GetStreamAtPackageURL(mxSBD->getDocumentStorage(), aUrl,
2515 css::embed::ElementModes::READ, aProxy);
2517 uno::Reference<io::XInputStream> xInStream = xStream->getInputStream();
2518 if (xInStream.is())
2520 sal_Int32 nLastDot = aUrl.lastIndexOf('.');
2521 sal_Int32 nLastSlash = aUrl.lastIndexOf('/');
2522 OUString sDesiredExtension;
2523 if (nLastDot > nLastSlash && nLastDot+1 < aUrl.getLength())
2524 sDesiredExtension = aUrl.copy(nLastDot);
2526 OUString sTempUrl;
2527 if (::avmedia::CreateMediaTempFile(xInStream, sTempUrl, sDesiredExtension))
2528 aRet = std::make_shared<avmedia::MediaTempFile>(sTempUrl);
2530 xInStream->closeInput();
2532 #endif
2534 return aRet;
2537 //===== FrameSynchronization ==================================================
2539 FrameSynchronization::FrameSynchronization (const double nFrameDuration)
2540 : maTimer(),
2541 mnFrameDuration(nFrameDuration),
2542 mnNextFrameTargetTime(0),
2543 mbIsActive(false)
2545 MarkCurrentFrame();
2548 void FrameSynchronization::MarkCurrentFrame()
2550 mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2553 void FrameSynchronization::Synchronize()
2555 if (mbIsActive)
2557 // Do busy waiting for now.
2558 for(;;)
2560 double remainingTime = mnNextFrameTargetTime - maTimer.getElapsedTime();
2561 if(remainingTime <= 0)
2562 break;
2563 // Try to sleep most of it.
2564 int remainingMilliseconds = remainingTime * 1000;
2565 if(remainingMilliseconds > 2)
2566 std::this_thread::sleep_for(std::chrono::milliseconds(remainingMilliseconds - 2));
2570 MarkCurrentFrame();
2573 void FrameSynchronization::Activate()
2575 mbIsActive = true;
2578 void FrameSynchronization::Deactivate()
2580 mbIsActive = false;
2583 } // anon namespace
2585 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2586 slideshow_SlideShowImpl_get_implementation(
2587 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
2589 return cppu::acquire(new SlideShowImpl(context));
2591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */