1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <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>
78 #include <shapemaps.hxx>
79 #include <slideview.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"
96 using namespace com::sun::star
;
97 using namespace ::slideshow::internal
;
99 namespace box2d::utils
{ class box2DWorld
;
100 typedef ::std::shared_ptr
< box2DWorld
> Box2DWorldSharedPtr
; }
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
110 class FrameSynchronization
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
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.
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.
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.
143 /** The timer that is used for synchronization is independent from the
144 one used by SlideShowImpl: it is not paused or modified by
147 canvas::tools::ElapsedTime maTimer
;
148 /** Time between the display of frames. Enforced only when mbIsActive
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.
162 /******************************************************************************
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 -
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
210 explicit SlideShowImpl(
211 uno::Reference
<uno::XComponentContext
> xContext
);
213 /** Notify that the transition phase of the current slide
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.
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
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
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
;
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
;
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
;
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
324 void redisplayCurrentSlide();
327 // WeakComponentImplHelperBase
328 virtual void SAL_CALL
disposing() override
;
330 bool isDisposed() const
332 return (rBHelper
.bDisposed
|| rBHelper
.bInDispose
);
336 struct SeparateListenerImpl
; friend struct SeparateListenerImpl
;
337 class PrefetchPropertiesFunc
; friend class PrefetchPropertiesFunc
;
339 /// Stop currently running show.
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
353 SlideSharedPtr
const& pSlide
,
354 uno::Reference
<drawing::XDrawPage
> const& xSlide
,
355 uno::Reference
<animations::XAnimationNode
> const& xNode
)
358 return (pSlide
->getXDrawPage() == xSlide
&&
359 pSlide
->getXAnimationNode() == xNode
);
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(); }
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
;
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
;
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
;
482 bool mbForceManualAdvance
;
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
) :
511 mrScreenUpdater( rScreenUpdater
),
512 mrEventQueue( rEventQueue
)
515 SeparateListenerImpl( const SeparateListenerImpl
& ) = delete;
516 SeparateListenerImpl
& operator=( const SeparateListenerImpl
& ) = delete;
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
));
535 // ViewRepaintHandler
536 virtual void viewClobbered( const UnoViewSharedPtr
& rView
) override
538 // given view needs repaint, request update
539 mrScreenUpdater
.notifyUpdate(rView
, true);
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
),
559 maListenerContainer( m_aMutex
),
560 maShapeEventListeners(),
563 maUserPaintStrokeWidth(4.0),
564 mpPresTimer( std::make_shared
<canvas::tools::ElapsedTime
>() ),
565 maScreenUpdater(maViewContainer
),
566 maEventQueue( mpPresTimer
),
567 maEventMultiplexer( maEventQueue
,
569 maActivitiesQueue( mpPresTimer
),
570 maUserEventQueue( maEventMultiplexer
,
576 mpRehearseTimingsActivity(),
579 mpCurrentSlideTransitionSound(),
580 mxComponentContext(std::move( xContext
)),
581 mxOptionalTransitionFactory(),
585 mxDrawPagesSupplier(),
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() );
613 // #i82460# try to retrieve special transition factory
614 mxOptionalTransitionFactory
.set(
615 xFactory
->createInstanceWithContext(
616 u
"com.sun.star.presentation.TransitionFactory"_ustr
,
617 mxComponentContext
),
620 catch (loader::CannotActivateFactoryException
const&)
625 mpListener
= std::make_shared
<SeparateListenerImpl
>(
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();
665 maEventMultiplexer
.removeSlideAnimationsEndHandler(mpListener
);
666 maEventMultiplexer
.removeViewRepaintHandler(mpListener
);
667 maEventMultiplexer
.removeHyperlinkHandler(mpListener
);
668 maEventMultiplexer
.removeAnimationStartHandler( mpListener
);
669 maEventMultiplexer
.removeAnimationEndHandler( mpListener
);
674 maUserEventQueue
.clear();
675 maActivitiesQueue
.clear();
676 maEventMultiplexer
.clear();
677 maEventQueue
.clear();
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();
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;
727 if( !(rSound
>>= bStopSound
) )
731 if( !bStopSound
&& url
.isEmpty() )
732 return SoundPlayerSharedPtr();
734 stopSlideTransitionSound();
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
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
768 if (mbNoSlideTransitions
)
769 return ActivitySharedPtr();
771 // retrieve slide change parameters from XDrawPage
772 uno::Reference
< beans::XPropertySet
> xPropSet( xDrawPage
,
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
,
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
,
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
,
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
,
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
));
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(
838 mxOptionalTransitionFactory
,
841 bTransitionDirection
,
842 aTransitionFadeColor
,
843 resetSlideTransitionSound( aSound
, bLoopSound
) ));
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
,
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
,
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(
887 std::optional
<double>(1.0),
891 basegfx::B2DVector(rEnteringSlide
->getSlideSize().getWidth(), rEnteringSlide
->getSlideSize().getHeight()) ),
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
,
925 maShapeEventListeners
,
927 (aIter
!= maPolygons
.end()) ? aIter
->second
: PolyPolygonVector(),
928 maUserPaintColor
? *maUserPaintColor
: RGBColor(),
929 maUserPaintStrokeWidth
,
931 mbImageAnimationsAllowed
,
932 mbDisableAnimationZOrder
) );
934 // prefetch show content (reducing latency for slide
935 // bitmap and effect start later on)
941 void SlideShowImpl::requestWaitSymbol()
943 ++mnWaitSymbolRequestCount
;
944 OSL_ASSERT(mnWaitSymbolRequestCount
>0);
946 if (mnWaitSymbolRequestCount
== 1)
950 // fall back to cursor
951 requestCursor(calcActiveCursor(mnCurrentCursor
));
954 mpWaitSymbol
->show();
958 void SlideShowImpl::releaseWaitSymbol()
960 --mnWaitSymbolRequestCount
;
961 OSL_ASSERT(mnWaitSymbolRequestCount
>=0);
963 if (mnWaitSymbolRequestCount
== 0)
967 // fall back to cursor
968 requestCursor(calcActiveCursor(mnCurrentCursor
));
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
;
988 void SlideShowImpl::stopShow()
990 // Force-end running animation
991 // ===========================
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()));
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
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
;
1058 SAL_WARN( "slideshow", rProperty
.Name
);
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
);
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()
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(),
1100 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects
, bSkipSlideTransition
) );
1102 OSL_ENSURE( !maViewContainer
.empty(), "### no views!" );
1103 if (maViewContainer
.empty())
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
;
1119 mpCurrentSlide
= makeSlide( xSlide
, xDrawPages
, xRootNode
);
1121 OSL_ASSERT( 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(),
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
1161 pSlideChangeActivity
.reset();
1164 if (pSlideChangeActivity
)
1166 // factory generated a slide transition - activate it!
1167 maActivitiesQueue
.addActivity( pSlideChangeActivity
);
1171 // no transition effect on this slide - schedule slide
1172 // effect start event right away.
1173 maEventQueue
.addEvent(
1175 [this] () { this->notifySlideTransitionEnded(true); },
1176 u
"SlideShowImpl::notifySlideTransitionEnded"_ustr
));
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
);
1201 // precondition: must only be called from the main thread!
1202 DBG_TESTSOLARMUTEX();
1205 OSL_ENSURE( !maViewContainer
.empty(), "### no views!" );
1206 if (maViewContainer
.empty())
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
);
1230 // precondition: must only be called from the main thread!
1231 DBG_TESTSOLARMUTEX();
1236 return maEventMultiplexer
.notifyNextEffect();
1239 sal_Bool
SlideShowImpl::previousEffect()
1241 mbMovingForward
= false;
1242 osl::MutexGuard
const guard( m_aMutex
);
1247 // precondition: must only be called from the main thread!
1248 DBG_TESTSOLARMUTEX();
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
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();
1290 OSL_FAIL( "not yet implemented!" );
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();
1301 OSL_FAIL( "not yet implemented!" );
1305 sal_Bool
SlideShowImpl::pause( sal_Bool bPauseShow
)
1307 osl::MutexGuard
const guard( m_aMutex
);
1312 // precondition: must only be called from the main thread!
1313 DBG_TESTSOLARMUTEX();
1316 mpPresTimer
->pauseTimer();
1318 mpPresTimer
->continueTimer();
1320 maEventMultiplexer
.notifyPauseMode(bPauseShow
);
1322 mbShowPaused
= bPauseShow
;
1326 uno::Reference
<drawing::XDrawPage
> SlideShowImpl::getCurrentSlide()
1328 osl::MutexGuard
const guard( m_aMutex
);
1331 return uno::Reference
<drawing::XDrawPage
>();
1333 // precondition: must only be called from the main thread!
1334 DBG_TESTSOLARMUTEX();
1337 return mpCurrentSlide
->getXDrawPage();
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
);
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(
1361 maEventMultiplexer
));
1362 if (!maViewContainer
.addView( pView
))
1363 return false; // view already added
1365 // initialize view content
1366 // =======================
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)
1379 // broadcast newly added view
1380 maEventMultiplexer
.notifyViewAdded( pView
);
1382 // set current mouse ptr
1383 pView
->setCursorShape( calcActiveCursor(mnCurrentCursor
) );
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
) );
1400 return false; // view was not added in the first place
1402 // remove view from EventMultiplexer (mouse events etc.)
1403 maEventMultiplexer
.notifyViewRemoved( pView
);
1410 drawing::PointSequenceSequence
1411 lcl_createPointSequenceSequenceFromB2DPolygon(const basegfx::B2DPolygon
& rPoly
)
1413 drawing::PointSequenceSequence aRetval
;
1414 //Create only one sequence for one pen drawing.
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()));
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.
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
));
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
)));
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
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
);
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
;
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())
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
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();
1542 aDrawingPoints
.append(aPoly
);
1543 pFirstPolyPoly
= pPolyPoly
;
1546 basegfx::B2DPoint aLast
1547 = aDrawingPoints
.getB2DPoint(ptCount
- 1);
1548 if (aPoly
.getB2DPoint(0).equal(aLast
))
1550 aDrawingPoints
.append(aPoly
, 1);
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
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
);
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
);
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
);
1651 // disable user paint
1652 maUserPaintColor
.reset();
1653 maEventMultiplexer
.notifyUserPaintDisabled();
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
);
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" );
1687 // Switch to Pen Mode
1688 maEventMultiplexer
.notifySwitchPenMode();
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();
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
);
1726 // new Property for pen's width
1727 if ( rProperty
.Name
== "UserPaintStrokeWidth" )
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
);
1741 if ( rProperty
.Name
== "AdvanceOnClick" )
1743 bool bAdvanceOnClick
= false;
1744 if (! (rProperty
.Value
>>= bAdvanceOnClick
))
1746 maUserEventQueue
.setAdvanceOnClick( bAdvanceOnClick
);
1750 if ( rProperty
.Name
== "DisableAnimationZOrder" )
1752 bool bDisableAnimationZOrder
= false;
1753 if (! (rProperty
.Value
>>= bDisableAnimationZOrder
))
1755 mbDisableAnimationZOrder
= bDisableAnimationZOrder
;
1759 if ( rProperty
.Name
== "ImageAnimationsAllowed" )
1761 if (! (rProperty
.Value
>>= mbImageAnimationsAllowed
))
1764 // TODO(F3): Forward to slides!
1768 if ( rProperty
.Name
== "MouseVisible" )
1770 if (! (rProperty
.Value
>>= mbMouseVisible
))
1773 requestCursor(mnCurrentCursor
);
1778 if ( rProperty
.Name
== "ForceManualAdvance" )
1780 return (rProperty
.Value
>>= mbForceManualAdvance
);
1783 if ( rProperty
.Name
== "RehearseTimings" )
1785 bool bRehearseTimings
= false;
1786 if (! (rProperty
.Value
>>= bRehearseTimings
))
1789 if (bRehearseTimings
)
1791 // TODO(Q3): Move to slide
1792 mpRehearseTimingsActivity
= RehearseTimingsActivity::create(
1804 mpBox2DDummyPtr
) );
1806 else if (mpRehearseTimingsActivity
)
1808 // removes timer from all views:
1809 mpRehearseTimingsActivity
->dispose();
1810 mpRehearseTimingsActivity
.reset();
1815 if ( rProperty
.Name
== "WaitSymbolBitmap" )
1817 uno::Reference
<rendering::XBitmap
> xBitmap
;
1818 if (! (rProperty
.Value
>>= xBitmap
))
1821 mpWaitSymbol
= WaitSymbol::create( xBitmap
,
1829 if (rProperty
.Name
== "NavigationSlidePrev")
1831 uno::Reference
<rendering::XBitmap
> xBitmap
;
1832 if (!(rProperty
.Value
>>= xBitmap
))
1835 mpNavigationPrev
= SlideOverlayButton::create(
1836 xBitmap
, { 20, 10 }, [this](basegfx::B2DPoint
) { notifySlideEnded(true); },
1837 maScreenUpdater
, maEventMultiplexer
, maViewContainer
);
1842 if (rProperty
.Name
== "NavigationSlideMenu")
1844 uno::Reference
<rendering::XBitmap
> xBitmap
;
1845 if (!(rProperty
.Value
>>= xBitmap
))
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
);
1866 if (rProperty
.Name
== "NavigationSlideNext")
1868 uno::Reference
<rendering::XBitmap
> xBitmap
;
1869 if (!(rProperty
.Value
>>= xBitmap
))
1872 mpNavigationNext
= SlideOverlayButton::create(
1873 xBitmap
, { 2 * xBitmap
->getSize().Width
+ 76, 10 }, [this](basegfx::B2DPoint
) { notifySlideEnded(false); },
1874 maScreenUpdater
, maEventMultiplexer
, maViewContainer
);
1879 if ( rProperty
.Name
== "PointerSymbolBitmap" )
1881 uno::Reference
<rendering::XBitmap
> xBitmap
;
1882 if (! (rProperty
.Value
>>= xBitmap
))
1885 mpPointerSymbol
= PointerSymbol::create( xBitmap
,
1893 if ( rProperty
.Name
== "PointerVisible" )
1896 if (!(rProperty
.Value
>>= visible
))
1900 mpPointerSymbol
->setVisible(visible
);
1905 if ( rProperty
.Name
== "PointerPosition")
1907 css::geometry::RealPoint2D pos
;
1908 if (! (rProperty
.Value
>>= pos
))
1912 mpPointerSymbol
->viewsChanged(pos
);
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
1939 (*iView
)->setIsSoundEnabled(bValue
);
1948 void SlideShowImpl::addSlideShowListener(
1949 uno::Reference
<presentation::XSlideShowListener
> const& xListener
)
1951 osl::MutexGuard
const guard( m_aMutex
);
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
);
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(
1988 std::make_shared
<comphelper::OInterfaceContainerHelper3
<css::presentation::XShapeEventListener
>>(
1992 // add new listener to broadcaster
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
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
);
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
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
);
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
);
2091 // precondition: update() must only be called from the
2093 DBG_TESTSOLARMUTEX();
2097 // commit frame (might be repaints pending)
2098 maScreenUpdater
.commitUpdates();
2104 // TODO(F2): re-evaluate whether that timer lagging makes
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();
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.
2132 scopeGuard
.dismiss();
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
);
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.
2189 maFrameSynchronization
.Activate();
2193 // timer events left:
2194 // difference from current time (nota bene:
2195 // time no longer held here!) to the next event in
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
2217 if( !mbSlideShowIdle
&&
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
& )
2238 catch( uno::Exception
& )
2240 TOOLS_WARN_EXCEPTION( "slideshow", "" );
2244 mbSlideShowIdle
= true;
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" );
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
,
2281 sal_Int32
nChange(0);
2282 if( !xPropSet
.is() ||
2283 !getPropertyValue( nChange
,
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
,
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
2326 aNotificationEvents
= makeInterruptableDelay(
2327 [this]() { return this->notifySlideEnded( false ); },
2328 maEventMultiplexer
.getAutomaticTimeout() );
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
&&
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()
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() );
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() );
2433 xPropSet
->setPropertyValue(
2435 uno::Any( static_cast<sal_Int32
>(1) ) );
2436 xPropSet
->setPropertyValue(
2438 uno::Any( static_cast<sal_Int32
>(time
) ) );
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()
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
); } );
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
); } );
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();
2502 std::shared_ptr
<avmedia::MediaTempFile
> SlideShowImpl::getMediaTempFile(const OUString
& aUrl
)
2504 std::shared_ptr
<avmedia::MediaTempFile
> aRet
;
2506 #if !HAVE_FEATURE_AVMEDIA
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();
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
);
2527 if (::avmedia::CreateMediaTempFile(xInStream
, sTempUrl
, sDesiredExtension
))
2528 aRet
= std::make_shared
<avmedia::MediaTempFile
>(sTempUrl
);
2530 xInStream
->closeInput();
2537 //===== FrameSynchronization ==================================================
2539 FrameSynchronization::FrameSynchronization (const double nFrameDuration
)
2541 mnFrameDuration(nFrameDuration
),
2542 mnNextFrameTargetTime(0),
2548 void FrameSynchronization::MarkCurrentFrame()
2550 mnNextFrameTargetTime
= maTimer
.getElapsedTime() + mnFrameDuration
;
2553 void FrameSynchronization::Synchronize()
2557 // Do busy waiting for now.
2560 double remainingTime
= mnNextFrameTargetTime
- maTimer
.getElapsedTime();
2561 if(remainingTime
<= 0)
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));
2573 void FrameSynchronization::Activate()
2578 void FrameSynchronization::Deactivate()
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: */