nss: upgrade to release 3.73
[LibreOffice.git] / slideshow / source / engine / slideshowimpl.cxx
blob5647170f9c4d18288b736ce111997f990e3cd731
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 <tools/diagnose_ex.h>
23 #include <cppuhelper/basemutex.hxx>
24 #include <cppuhelper/compbase.hxx>
25 #include <cppuhelper/interfacecontainer.h>
26 #include <cppuhelper/supportsservice.hxx>
28 #include <comphelper/scopeguard.hxx>
29 #include <comphelper/storagehelper.hxx>
30 #include <cppcanvas/polypolygon.hxx>
31 #include <osl/thread.hxx>
33 #include <tools/debug.hxx>
35 #include <basegfx/point/b2dpoint.hxx>
36 #include <basegfx/polygon/b2dpolygon.hxx>
37 #include <basegfx/utils/canvastools.hxx>
39 #include <sal/log.hxx>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/util/XUpdatable.hpp>
43 #include <com/sun/star/awt/SystemPointer.hpp>
44 #include <com/sun/star/presentation/XSlideShow.hpp>
45 #include <com/sun/star/presentation/XSlideShowListener.hpp>
46 #include <com/sun/star/lang/NoSupportException.hpp>
47 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
48 #include <com/sun/star/lang/XServiceInfo.hpp>
49 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
50 #include <com/sun/star/drawing/PointSequence.hpp>
51 #include <com/sun/star/drawing/XLayer.hpp>
52 #include <com/sun/star/drawing/XLayerSupplier.hpp>
53 #include <com/sun/star/drawing/XLayerManager.hpp>
54 #include <com/sun/star/container/XNameAccess.hpp>
55 #include <com/sun/star/document/XStorageBasedDocument.hpp>
57 #include <com/sun/star/uno/Reference.hxx>
58 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
60 #include <unoviewcontainer.hxx>
61 #include <transitionfactory.hxx>
62 #include <eventmultiplexer.hxx>
63 #include <usereventqueue.hxx>
64 #include <eventqueue.hxx>
65 #include <cursormanager.hxx>
66 #include <mediafilemanager.hxx>
67 #include <slideshowcontext.hxx>
68 #include <activitiesqueue.hxx>
69 #include <activitiesfactory.hxx>
70 #include <interruptabledelayevent.hxx>
71 #include <slide.hxx>
72 #include <shapemaps.hxx>
73 #include <slideview.hxx>
74 #include <tools.hxx>
75 #include <unoview.hxx>
76 #include "rehearsetimingsactivity.hxx"
77 #include "waitsymbol.hxx"
78 #include "effectrewinder.hxx"
79 #include <framerate.hxx>
80 #include "pointersymbol.hxx"
82 #include <map>
83 #include <vector>
84 #include <algorithm>
86 using namespace com::sun::star;
87 using namespace ::slideshow::internal;
89 namespace box2d::utils { class box2DWorld;
90 typedef ::std::shared_ptr< box2DWorld > Box2DWorldSharedPtr; }
92 namespace {
94 /** During animations the update() method tells its caller to call it as
95 soon as possible. This gives us more time to render the next frame and
96 still maintain a steady frame rate. This class is responsible for
97 synchronizing the display of new frames and thus keeping the frame rate
98 steady.
100 class FrameSynchronization
102 public:
103 /** Create new object with a predefined duration between two frames.
104 @param nFrameDuration
105 The preferred duration between the display of two frames in
106 seconds.
108 explicit FrameSynchronization (const double nFrameDuration);
110 /** Set the current time as the time at which the current frame is
111 displayed. From this the target time of the next frame is derived.
113 void MarkCurrentFrame();
115 /** When there is time left until the next frame is due then wait.
116 Otherwise return without delay.
118 void Synchronize();
120 /** Activate frame synchronization when an animation is active and
121 frames are to be displayed in a steady rate. While active
122 Synchronize() will wait until the frame duration time has passed.
124 void Activate();
126 /** Deactivate frame synchronization when no animation is active and the
127 time between frames depends on user actions and other external
128 sources. While deactivated Synchronize() will return without delay.
130 void Deactivate();
132 private:
133 /** The timer that is used for synchronization is independent from the
134 one used by SlideShowImpl: it is not paused or modified by
135 animations.
137 canvas::tools::ElapsedTime maTimer;
138 /** Time between the display of frames. Enforced only when mbIsActive
139 is <TRUE/>.
141 const double mnFrameDuration;
142 /** Time (of maTimer) when the next frame shall be displayed.
143 Synchronize() will wait until this time.
145 double mnNextFrameTargetTime;
146 /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise
147 it returns immediately.
149 bool mbIsActive;
152 /******************************************************************************
154 SlideShowImpl
156 This class encapsulates the slideshow presentation viewer.
158 With an instance of this class, it is possible to statically
159 and dynamically show a presentation, as defined by the
160 constructor-provided draw model (represented by a sequence
161 of css::drawing::XDrawPage objects).
163 It is possible to show the presentation on multiple views
164 simultaneously (e.g. for a multi-monitor setup). Since this
165 class also relies on user interaction, the corresponding
166 XSlideShowView interface provides means to register some UI
167 event listeners (mostly borrowed from awt::XWindow interface).
169 Since currently (mid 2004), OOo isn't very well suited to
170 multi-threaded rendering, this class relies on <em>very
171 frequent</em> external update() calls, which will render the
172 next frame of animations. This works as follows: after the
173 displaySlide() has been successfully called (which setup and
174 starts an actual slide show), the update() method must be
175 called until it returns false.
176 Effectively, this puts the burden of providing
177 concurrency to the clients of this class, which, as noted
178 above, is currently unavoidable with the current state of
179 affairs (I've actually tried threading here, but failed
180 miserably when using the VCL canvas as the render backend -
181 deadlocked).
183 ******************************************************************************/
185 typedef cppu::WeakComponentImplHelper<css::lang::XServiceInfo, presentation::XSlideShow> SlideShowImplBase;
187 typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
189 /// Maps XDrawPage for annotations persistence
190 typedef ::std::map< css::uno::Reference<
191 css::drawing::XDrawPage>,
192 PolyPolygonVector> PolygonMap;
194 class SlideShowImpl : private cppu::BaseMutex,
195 public CursorManager,
196 public MediaFileManager,
197 public SlideShowImplBase
199 public:
200 explicit SlideShowImpl(
201 uno::Reference<uno::XComponentContext> const& xContext );
203 /** Notify that the transition phase of the current slide
204 has ended.
206 The life of a slide has three phases: the transition
207 phase, when the previous slide vanishes, and the
208 current slide becomes visible, the shape animation
209 phase, when shape effects are running, and the phase
210 after the last shape animation has ended, but before
211 the next slide transition starts.
213 This method notifies the end of the first phase.
215 @param bPaintSlide
216 When true, Slide::show() is passed a true as well, denoting
217 explicit paint of slide content. Pass false here, if e.g. a
218 slide transition has already rendered the initial slide image.
220 void notifySlideTransitionEnded( bool bPaintSlide );
222 /** Notify that the shape animation phase of the current slide
223 has ended.
225 The life of a slide has three phases: the transition
226 phase, when the previous slide vanishes, and the
227 current slide becomes visible, the shape animation
228 phase, when shape effects are running, and the phase
229 after the last shape animation has ended, but before
230 the next slide transition starts.
232 This method notifies the end of the second phase.
234 void notifySlideAnimationsEnded();
236 /** Notify that the slide has ended.
238 The life of a slide has three phases: the transition
239 phase, when the previous slide vanishes, and the
240 current slide becomes visible, the shape animation
241 phase, when shape effects are running, and the phase
242 after the last shape animation has ended, but before
243 the next slide transition starts.
245 This method notifies the end of the third phase.
247 void notifySlideEnded (const bool bReverse);
249 /** Notification from eventmultiplexer that a hyperlink
250 has been clicked.
252 bool notifyHyperLinkClicked( OUString const& hyperLink );
254 /** Notification from eventmultiplexer that an animation event has occurred.
255 This will be forwarded to all registered XSlideShowListener
257 bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
259 /** Obtain a MediaTempFile for the specified url. */
260 virtual std::shared_ptr<avmedia::MediaTempFile> getMediaTempFile(const OUString& aUrl) override;
262 private:
263 // XServiceInfo
264 virtual OUString SAL_CALL getImplementationName() override;
265 virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
266 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override;
268 // XSlideShow:
269 virtual sal_Bool SAL_CALL nextEffect() override;
270 virtual sal_Bool SAL_CALL previousEffect() override;
271 virtual sal_Bool SAL_CALL startShapeActivity(
272 uno::Reference<drawing::XShape> const& xShape ) override;
273 virtual sal_Bool SAL_CALL stopShapeActivity(
274 uno::Reference<drawing::XShape> const& xShape ) override;
275 virtual sal_Bool SAL_CALL pause( sal_Bool bPauseShow ) override;
276 virtual uno::Reference<drawing::XDrawPage> SAL_CALL getCurrentSlide() override;
277 virtual void SAL_CALL displaySlide(
278 uno::Reference<drawing::XDrawPage> const& xSlide,
279 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
280 uno::Reference<animations::XAnimationNode> const& xRootNode,
281 uno::Sequence<beans::PropertyValue> const& rProperties ) override;
282 virtual void SAL_CALL registerUserPaintPolygons( const css::uno::Reference< css::lang::XMultiServiceFactory >& xDocFactory ) override;
283 virtual sal_Bool SAL_CALL setProperty(
284 beans::PropertyValue const& rProperty ) override;
285 virtual sal_Bool SAL_CALL addView(
286 uno::Reference<presentation::XSlideShowView> const& xView ) override;
287 virtual sal_Bool SAL_CALL removeView(
288 uno::Reference<presentation::XSlideShowView> const& xView ) override;
289 virtual sal_Bool SAL_CALL update( double & nNextTimeout ) override;
290 virtual void SAL_CALL addSlideShowListener(
291 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
292 virtual void SAL_CALL removeSlideShowListener(
293 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
294 virtual void SAL_CALL addShapeEventListener(
295 uno::Reference<presentation::XShapeEventListener> const& xListener,
296 uno::Reference<drawing::XShape> const& xShape ) override;
297 virtual void SAL_CALL removeShapeEventListener(
298 uno::Reference<presentation::XShapeEventListener> const& xListener,
299 uno::Reference<drawing::XShape> const& xShape ) override;
300 virtual void SAL_CALL setShapeCursor(
301 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape ) override;
303 // CursorManager
306 virtual bool requestCursor( sal_Int16 nCursorShape ) override;
307 virtual void resetCursor() override;
309 /** This is somewhat similar to displaySlide when called for the current
310 slide. It has been simplified to take advantage of that no slide
311 change takes place. Furthermore it does not show the slide
312 transition.
314 void redisplayCurrentSlide();
316 protected:
317 // WeakComponentImplHelperBase
318 virtual void SAL_CALL disposing() override;
320 bool isDisposed() const
322 return (rBHelper.bDisposed || rBHelper.bInDispose);
325 private:
326 struct SeparateListenerImpl; friend struct SeparateListenerImpl;
327 class PrefetchPropertiesFunc; friend class PrefetchPropertiesFunc;
329 /// Stop currently running show.
330 void stopShow();
332 ///Find a polygons vector in maPolygons (map)
333 PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
335 /// Creates a new slide.
336 SlideSharedPtr makeSlide(
337 uno::Reference<drawing::XDrawPage> const& xDrawPage,
338 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
339 uno::Reference<animations::XAnimationNode> const& xRootNode );
341 /// Checks whether the given slide/animation node matches mpPrefetchSlide
342 static bool matches(
343 SlideSharedPtr const& pSlide,
344 uno::Reference<drawing::XDrawPage> const& xSlide,
345 uno::Reference<animations::XAnimationNode> const& xNode )
347 if (pSlide)
348 return (pSlide->getXDrawPage() == xSlide &&
349 pSlide->getXAnimationNode() == xNode);
350 else
351 return (!xSlide.is() && !xNode.is());
354 /// Resets the current slide transition sound object with a new one:
355 SoundPlayerSharedPtr resetSlideTransitionSound(
356 uno::Any const& url, bool bLoopSound );
358 /// stops the current slide transition sound
359 void stopSlideTransitionSound();
361 /** Prepare a slide transition
363 This method registers all necessary events and
364 activities for a slide transition.
366 @return the slide change activity, or NULL for no transition effect
368 ActivitySharedPtr createSlideTransition(
369 const uno::Reference< drawing::XDrawPage >& xDrawPage,
370 const SlideSharedPtr& rLeavingSlide,
371 const SlideSharedPtr& rEnteringSlide,
372 const EventSharedPtr& rTransitionEndEvent );
374 /** Request/release the wait symbol. The wait symbol is displayed when
375 there are more requests then releases. Locking the wait symbol
376 helps to avoid intermediate repaints.
378 Do not call this method directly. Use WaitSymbolLock instead.
380 void requestWaitSymbol();
381 void releaseWaitSymbol();
383 class WaitSymbolLock {public:
384 explicit WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
385 { mrSlideShowImpl.requestWaitSymbol(); }
386 ~WaitSymbolLock()
387 { mrSlideShowImpl.releaseWaitSymbol(); }
388 private: SlideShowImpl& mrSlideShowImpl;
391 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
392 sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
394 /** This method is called asynchronously to finish the rewinding of an
395 effect to the previous slide that was initiated earlier.
397 void rewindEffectToPreviousSlide();
399 /// all registered views
400 UnoViewContainer maViewContainer;
402 /// all registered slide show listeners
403 comphelper::OInterfaceContainerHelper2 maListenerContainer;
405 /// map of vectors, containing all registered listeners for a shape
406 ShapeEventListenerMap maShapeEventListeners;
407 /// map of sal_Int16 values, specifying the mouse cursor for every shape
408 ShapeCursorMap maShapeCursors;
410 //map of vector of Polygons, containing polygons drawn on each slide.
411 PolygonMap maPolygons;
413 std::optional<RGBColor> maUserPaintColor;
415 double maUserPaintStrokeWidth;
417 //changed for the eraser project
418 std::optional<bool> maEraseAllInk;
419 std::optional<sal_Int32> maEraseInk;
420 //end changed
422 std::shared_ptr<canvas::tools::ElapsedTime> mpPresTimer;
423 ScreenUpdater maScreenUpdater;
424 EventQueue maEventQueue;
425 EventMultiplexer maEventMultiplexer;
426 ActivitiesQueue maActivitiesQueue;
427 UserEventQueue maUserEventQueue;
428 SubsettableShapeManagerSharedPtr mpDummyPtr;
429 box2d::utils::Box2DWorldSharedPtr mpBox2DDummyPtr;
431 std::shared_ptr<SeparateListenerImpl> mpListener;
433 std::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
434 std::shared_ptr<WaitSymbol> mpWaitSymbol;
436 std::shared_ptr<PointerSymbol> mpPointerSymbol;
438 /// the current slide transition sound object:
439 SoundPlayerSharedPtr mpCurrentSlideTransitionSound;
441 uno::Reference<uno::XComponentContext> mxComponentContext;
442 uno::Reference<
443 presentation::XTransitionFactory> mxOptionalTransitionFactory;
445 /// the previously running slide
446 SlideSharedPtr mpPreviousSlide;
447 /// the currently running slide
448 SlideSharedPtr mpCurrentSlide;
449 /// the already prefetched slide: best candidate for upcoming slide
450 SlideSharedPtr mpPrefetchSlide;
451 /// slide to be prefetched: best candidate for upcoming slide
452 uno::Reference<drawing::XDrawPage> mxPrefetchSlide;
453 /// save the XDrawPagesSupplier to retrieve polygons
454 uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier;
455 /// Used by MediaFileManager, for media files with package url.
456 uno::Reference<document::XStorageBasedDocument> mxSBD;
457 /// slide animation to be prefetched:
458 uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
460 sal_Int16 mnCurrentCursor;
462 sal_Int32 mnWaitSymbolRequestCount;
463 bool mbAutomaticAdvancementMode;
464 bool mbImageAnimationsAllowed;
465 bool mbNoSlideTransitions;
466 bool mbMouseVisible;
467 bool mbForceManualAdvance;
468 bool mbShowPaused;
469 bool mbSlideShowIdle;
470 bool mbDisableAnimationZOrder;
472 EffectRewinder maEffectRewinder;
473 FrameSynchronization maFrameSynchronization;
476 /** Separate event listener for animation, view and hyperlink events.
478 This handler is registered for slide animation end, view and
479 hyperlink events at the global EventMultiplexer, and forwards
480 notifications to the SlideShowImpl
482 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
483 public ViewRepaintHandler,
484 public HyperlinkHandler,
485 public AnimationEventHandler
487 SlideShowImpl& mrShow;
488 ScreenUpdater& mrScreenUpdater;
489 EventQueue& mrEventQueue;
491 SeparateListenerImpl( SlideShowImpl& rShow,
492 ScreenUpdater& rScreenUpdater,
493 EventQueue& rEventQueue ) :
494 mrShow( rShow ),
495 mrScreenUpdater( rScreenUpdater ),
496 mrEventQueue( rEventQueue )
499 SeparateListenerImpl( const SeparateListenerImpl& ) = delete;
500 SeparateListenerImpl& operator=( const SeparateListenerImpl& ) = delete;
502 // EventHandler
503 virtual bool handleEvent() override
505 // DON't call notifySlideAnimationsEnded()
506 // directly, but queue an event. handleEvent()
507 // might be called from e.g.
508 // showNext(), and notifySlideAnimationsEnded() must not be called
509 // in recursion. Note that the event is scheduled for the next
510 // frame so that its expensive execution does not come in between
511 // sprite hiding and shape redraw (at the end of the animation of a
512 // shape), which would cause a flicker.
513 mrEventQueue.addEventForNextRound(
514 makeEvent( [this] () { this->mrShow.notifySlideAnimationsEnded(); },
515 "SlideShowImpl::notifySlideAnimationsEnded"));
516 return true;
519 // ViewRepaintHandler
520 virtual void viewClobbered( const UnoViewSharedPtr& rView ) override
522 // given view needs repaint, request update
523 mrScreenUpdater.notifyUpdate(rView, true);
526 // HyperlinkHandler
527 virtual bool handleHyperlink( OUString const& rLink ) override
529 return mrShow.notifyHyperLinkClicked(rLink);
532 // AnimationEventHandler
533 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) override
535 return mrShow.handleAnimationEvent(rNode);
539 SlideShowImpl::SlideShowImpl(
540 uno::Reference<uno::XComponentContext> const& xContext )
541 : SlideShowImplBase(m_aMutex),
542 maViewContainer(),
543 maListenerContainer( m_aMutex ),
544 maShapeEventListeners(),
545 maShapeCursors(),
546 maUserPaintColor(),
547 maUserPaintStrokeWidth(4.0),
548 mpPresTimer( std::make_shared<canvas::tools::ElapsedTime>() ),
549 maScreenUpdater(maViewContainer),
550 maEventQueue( mpPresTimer ),
551 maEventMultiplexer( maEventQueue,
552 maViewContainer ),
553 maActivitiesQueue( mpPresTimer ),
554 maUserEventQueue( maEventMultiplexer,
555 maEventQueue,
556 *this ),
557 mpDummyPtr(),
558 mpBox2DDummyPtr(),
559 mpListener(),
560 mpRehearseTimingsActivity(),
561 mpWaitSymbol(),
562 mpPointerSymbol(),
563 mpCurrentSlideTransitionSound(),
564 mxComponentContext( xContext ),
565 mxOptionalTransitionFactory(),
566 mpCurrentSlide(),
567 mpPrefetchSlide(),
568 mxPrefetchSlide(),
569 mxDrawPagesSupplier(),
570 mxSBD(),
571 mxPrefetchAnimationNode(),
572 mnCurrentCursor(awt::SystemPointer::ARROW),
573 mnWaitSymbolRequestCount(0),
574 mbAutomaticAdvancementMode(false),
575 mbImageAnimationsAllowed( true ),
576 mbNoSlideTransitions( false ),
577 mbMouseVisible( true ),
578 mbForceManualAdvance( false ),
579 mbShowPaused( false ),
580 mbSlideShowIdle( true ),
581 mbDisableAnimationZOrder( false ),
582 maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue),
583 maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond)
586 // keep care not constructing any UNO references to this inside ctor,
587 // shift that code to create()!
589 uno::Reference<lang::XMultiComponentFactory> xFactory(
590 mxComponentContext->getServiceManager() );
592 if( xFactory.is() )
596 // #i82460# try to retrieve special transition factory
597 mxOptionalTransitionFactory.set(
598 xFactory->createInstanceWithContext(
599 "com.sun.star.presentation.TransitionFactory",
600 mxComponentContext ),
601 uno::UNO_QUERY );
603 catch (loader::CannotActivateFactoryException const&)
608 mpListener = std::make_shared<SeparateListenerImpl>(
609 *this,
610 maScreenUpdater,
611 maEventQueue );
612 maEventMultiplexer.addSlideAnimationsEndHandler( mpListener );
613 maEventMultiplexer.addViewRepaintHandler( mpListener );
614 maEventMultiplexer.addHyperlinkHandler( mpListener, 0.0 );
615 maEventMultiplexer.addAnimationStartHandler( mpListener );
616 maEventMultiplexer.addAnimationEndHandler( mpListener );
619 // we are about to be disposed (someone call dispose() on us)
620 void SlideShowImpl::disposing()
622 osl::MutexGuard const guard( m_aMutex );
624 maEffectRewinder.dispose();
626 // stop slide transition sound, if any:
627 stopSlideTransitionSound();
629 mxComponentContext.clear();
631 if( mpCurrentSlideTransitionSound )
633 mpCurrentSlideTransitionSound->dispose();
634 mpCurrentSlideTransitionSound.reset();
637 mpWaitSymbol.reset();
638 mpPointerSymbol.reset();
640 if( mpRehearseTimingsActivity )
642 mpRehearseTimingsActivity->dispose();
643 mpRehearseTimingsActivity.reset();
646 if( mpListener )
648 maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
649 maEventMultiplexer.removeViewRepaintHandler(mpListener);
650 maEventMultiplexer.removeHyperlinkHandler(mpListener);
651 maEventMultiplexer.removeAnimationStartHandler( mpListener );
652 maEventMultiplexer.removeAnimationEndHandler( mpListener );
654 mpListener.reset();
657 maUserEventQueue.clear();
658 maActivitiesQueue.clear();
659 maEventMultiplexer.clear();
660 maEventQueue.clear();
661 mpPresTimer.reset();
662 maShapeCursors.clear();
663 maShapeEventListeners.clear();
665 // send all listeners a disposing() that we are going down:
666 maListenerContainer.disposeAndClear(
667 lang::EventObject( static_cast<cppu::OWeakObject *>(this) ) );
669 maViewContainer.dispose();
671 // release slides:
672 mxPrefetchAnimationNode.clear();
673 mxPrefetchSlide.clear();
674 mpPrefetchSlide.reset();
675 mpCurrentSlide.reset();
676 mpPreviousSlide.reset();
679 uno::Sequence< OUString > SAL_CALL SlideShowImpl::getSupportedServiceNames()
681 return { "com.sun.star.presentation.SlideShow" };
684 OUString SAL_CALL SlideShowImpl::getImplementationName()
686 return "com.sun.star.comp.presentation.SlideShow";
689 sal_Bool SAL_CALL SlideShowImpl::supportsService(const OUString& aServiceName)
691 return cppu::supportsService(this, aServiceName);
694 /// stops the current slide transition sound
695 void SlideShowImpl::stopSlideTransitionSound()
697 if (mpCurrentSlideTransitionSound)
699 mpCurrentSlideTransitionSound->stopPlayback();
700 mpCurrentSlideTransitionSound->dispose();
701 mpCurrentSlideTransitionSound.reset();
705 SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
707 bool bStopSound = false;
708 OUString url;
710 if( !(rSound >>= bStopSound) )
711 bStopSound = false;
712 rSound >>= url;
714 if( !bStopSound && url.isEmpty() )
715 return SoundPlayerSharedPtr();
717 stopSlideTransitionSound();
719 if (!url.isEmpty())
723 mpCurrentSlideTransitionSound = SoundPlayer::create(
724 maEventMultiplexer, url, mxComponentContext, *this);
725 mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
727 catch (lang::NoSupportException const&)
729 // catch possible exceptions from SoundPlayer, since
730 // being not able to playback the sound is not a hard
731 // error here (still, the slide transition should be
732 // shown).
735 return mpCurrentSlideTransitionSound;
738 ActivitySharedPtr SlideShowImpl::createSlideTransition(
739 const uno::Reference< drawing::XDrawPage >& xDrawPage,
740 const SlideSharedPtr& rLeavingSlide,
741 const SlideSharedPtr& rEnteringSlide,
742 const EventSharedPtr& rTransitionEndEvent)
744 ENSURE_OR_THROW( !maViewContainer.empty(),
745 "createSlideTransition(): No views" );
746 ENSURE_OR_THROW( rEnteringSlide,
747 "createSlideTransition(): No entering slide" );
749 // return empty transition, if slide transitions
750 // are disabled.
751 if (mbNoSlideTransitions)
752 return ActivitySharedPtr();
754 // retrieve slide change parameters from XDrawPage
755 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
756 uno::UNO_QUERY );
758 if( !xPropSet.is() )
760 SAL_INFO("slideshow", "createSlideTransition(): "
761 "Slide has no PropertySet - assuming no transition" );
762 return ActivitySharedPtr();
765 sal_Int16 nTransitionType(0);
766 if( !getPropertyValue( nTransitionType,
767 xPropSet,
768 "TransitionType") )
770 SAL_INFO("slideshow", "createSlideTransition(): "
771 "Could not extract slide transition type from XDrawPage - assuming no transition" );
772 return ActivitySharedPtr();
775 sal_Int16 nTransitionSubType(0);
776 if( !getPropertyValue( nTransitionSubType,
777 xPropSet,
778 "TransitionSubtype") )
780 SAL_INFO("slideshow", "createSlideTransition(): "
781 "Could not extract slide transition subtype from XDrawPage - assuming no transition" );
782 return ActivitySharedPtr();
785 bool bTransitionDirection(false);
786 if( !getPropertyValue( bTransitionDirection,
787 xPropSet,
788 "TransitionDirection") )
790 SAL_INFO("slideshow", "createSlideTransition(): "
791 "Could not extract slide transition direction from XDrawPage - assuming default direction" );
794 sal_Int32 aUnoColor(0);
795 if( !getPropertyValue( aUnoColor,
796 xPropSet,
797 "TransitionFadeColor") )
799 SAL_INFO("slideshow", "createSlideTransition(): "
800 "Could not extract slide transition fade color from XDrawPage - assuming black" );
803 const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
805 uno::Any aSound;
806 bool bLoopSound = false;
808 if( !getPropertyValue( aSound, xPropSet, "Sound") )
809 SAL_INFO("slideshow", "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound" );
811 if( !getPropertyValue( bLoopSound, xPropSet, "LoopSound" ) )
812 SAL_INFO("slideshow", "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound" );
814 NumberAnimationSharedPtr pTransition(
815 TransitionFactory::createSlideTransition(
816 rLeavingSlide,
817 rEnteringSlide,
818 maViewContainer,
819 maScreenUpdater,
820 maEventMultiplexer,
821 mxOptionalTransitionFactory,
822 nTransitionType,
823 nTransitionSubType,
824 bTransitionDirection,
825 aTransitionFadeColor,
826 resetSlideTransitionSound( aSound, bLoopSound ) ));
828 if( !pTransition )
829 return ActivitySharedPtr(); // no transition effect has been
830 // generated. Normally, that means
831 // that simply no transition is
832 // set on this slide.
834 double nTransitionDuration(0.0);
835 if( !getPropertyValue( nTransitionDuration,
836 xPropSet,
837 "TransitionDuration") )
839 SAL_INFO("slideshow", "createSlideTransition(): "
840 "Could not extract slide transition duration from XDrawPage - assuming no transition" );
841 return ActivitySharedPtr();
844 sal_Int32 nMinFrames(5);
845 if( !getPropertyValue( nMinFrames,
846 xPropSet,
847 "MinimalFrameNumber") )
849 SAL_INFO("slideshow", "createSlideTransition(): "
850 "No minimal number of frames given - assuming 5" );
853 // prefetch slide transition bitmaps, but postpone it after
854 // displaySlide() has finished - sometimes, view size has not yet
855 // reached final size
856 maEventQueue.addEvent(
857 makeEvent( [pTransition] () {
858 pTransition->prefetch(); },
859 "Animation::prefetch"));
861 return ActivitySharedPtr(
862 ActivitiesFactory::createSimpleActivity(
863 ActivitiesFactory::CommonParameters(
864 rTransitionEndEvent,
865 maEventQueue,
866 maActivitiesQueue,
867 nTransitionDuration,
868 nMinFrames,
869 false,
870 std::optional<double>(1.0),
871 0.0,
872 0.0,
873 ShapeSharedPtr(),
874 basegfx::B2DSize( rEnteringSlide->getSlideSize() ) ),
875 pTransition,
876 true ));
879 PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
881 // TODO(P2): optimize research in the map.
882 return maPolygons.find(xDrawPage);
885 SlideSharedPtr SlideShowImpl::makeSlide(
886 uno::Reference<drawing::XDrawPage> const& xDrawPage,
887 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
888 uno::Reference<animations::XAnimationNode> const& xRootNode )
890 if( !xDrawPage.is() )
891 return SlideSharedPtr();
893 //Retrieve polygons for the current slide
894 PolygonMap::iterator aIter = findPolygons(xDrawPage);
896 const SlideSharedPtr pSlide( createSlide(xDrawPage,
897 xDrawPages,
898 xRootNode,
899 maEventQueue,
900 maEventMultiplexer,
901 maScreenUpdater,
902 maActivitiesQueue,
903 maUserEventQueue,
904 *this,
905 *this,
906 maViewContainer,
907 mxComponentContext,
908 maShapeEventListeners,
909 maShapeCursors,
910 (aIter != maPolygons.end()) ? aIter->second : PolyPolygonVector(),
911 maUserPaintColor ? *maUserPaintColor : RGBColor(),
912 maUserPaintStrokeWidth,
913 !!maUserPaintColor,
914 mbImageAnimationsAllowed,
915 mbDisableAnimationZOrder) );
917 // prefetch show content (reducing latency for slide
918 // bitmap and effect start later on)
919 pSlide->prefetch();
921 return pSlide;
924 void SlideShowImpl::requestWaitSymbol()
926 ++mnWaitSymbolRequestCount;
927 OSL_ASSERT(mnWaitSymbolRequestCount>0);
929 if (mnWaitSymbolRequestCount == 1)
931 if( !mpWaitSymbol )
933 // fall back to cursor
934 requestCursor(calcActiveCursor(mnCurrentCursor));
936 else
937 mpWaitSymbol->show();
941 void SlideShowImpl::releaseWaitSymbol()
943 --mnWaitSymbolRequestCount;
944 OSL_ASSERT(mnWaitSymbolRequestCount>=0);
946 if (mnWaitSymbolRequestCount == 0)
948 if( !mpWaitSymbol )
950 // fall back to cursor
951 requestCursor(calcActiveCursor(mnCurrentCursor));
953 else
954 mpWaitSymbol->hide();
958 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
960 if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
961 nCursorShape = awt::SystemPointer::WAIT;
962 else if( !mbMouseVisible ) // enforce INVISIBLE
963 nCursorShape = awt::SystemPointer::INVISIBLE;
964 else if( maUserPaintColor &&
965 nCursorShape == awt::SystemPointer::ARROW )
966 nCursorShape = awt::SystemPointer::PEN;
968 return nCursorShape;
971 void SlideShowImpl::stopShow()
973 // Force-end running animation
974 // ===========================
975 if (mpCurrentSlide)
977 mpCurrentSlide->hide();
978 //Register polygons in the map
979 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
980 maPolygons.erase(mpCurrentSlide->getXDrawPage());
982 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
985 // clear all queues
986 maEventQueue.clear();
987 maActivitiesQueue.clear();
989 // Attention: we MUST clear the user event queue here,
990 // this is because the current slide might have registered
991 // shape events (click or enter/leave), which might
992 // otherwise dangle forever in the queue (because of the
993 // shared ptr nature). If someone needs to change this:
994 // somehow unregister those shapes at the user event queue
995 // on notifySlideEnded().
996 maUserEventQueue.clear();
998 // re-enable automatic effect advancement
999 // (maEventQueue.clear() above might have killed
1000 // maEventMultiplexer's tick events)
1001 if (mbAutomaticAdvancementMode)
1003 // toggle automatic mode (enabling just again is
1004 // ignored by EventMultiplexer)
1005 maEventMultiplexer.setAutomaticMode( false );
1006 maEventMultiplexer.setAutomaticMode( true );
1010 class SlideShowImpl::PrefetchPropertiesFunc
1012 public:
1013 PrefetchPropertiesFunc( SlideShowImpl * that_,
1014 bool& rbSkipAllMainSequenceEffects,
1015 bool& rbSkipSlideTransition)
1016 : mpSlideShowImpl(that_),
1017 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1018 mrbSkipSlideTransition(rbSkipSlideTransition)
1021 void operator()( beans::PropertyValue const& rProperty ) const {
1022 if (rProperty.Name == "Prefetch" )
1024 uno::Sequence<uno::Any> seq;
1025 if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1027 seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1028 seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1031 else if ( rProperty.Name == "SkipAllMainSequenceEffects" )
1033 rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1035 else if ( rProperty.Name == "SkipSlideTransition" )
1037 rProperty.Value >>= mrbSkipSlideTransition;
1039 else
1041 SAL_WARN( "slideshow", rProperty.Name );
1044 private:
1045 SlideShowImpl *const mpSlideShowImpl;
1046 bool& mrbSkipAllMainSequenceEffects;
1047 bool& mrbSkipSlideTransition;
1050 void SlideShowImpl::displaySlide(
1051 uno::Reference<drawing::XDrawPage> const& xSlide,
1052 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
1053 uno::Reference<animations::XAnimationNode> const& xRootNode,
1054 uno::Sequence<beans::PropertyValue> const& rProperties )
1056 osl::MutexGuard const guard( m_aMutex );
1058 if (isDisposed())
1059 return;
1061 maEffectRewinder.setRootAnimationNode(xRootNode);
1062 maEffectRewinder.setCurrentSlide(xSlide);
1064 // precondition: must only be called from the main thread!
1065 DBG_TESTSOLARMUTEX();
1067 mxDrawPagesSupplier = xDrawPages;
1068 mxSBD = uno::Reference<document::XStorageBasedDocument>(mxDrawPagesSupplier, uno::UNO_QUERY);
1070 stopShow(); // MUST call that: results in
1071 // maUserEventQueue.clear(). What's more,
1072 // stopShow()'s currSlide->hide() call is
1073 // now also required, notifySlideEnded()
1074 // relies on that
1075 // unconditionally. Otherwise, genuine
1076 // shape animations (drawing layer and
1077 // GIF) will not be stopped.
1079 bool bSkipAllMainSequenceEffects (false);
1080 bool bSkipSlideTransition (false);
1081 std::for_each( rProperties.begin(),
1082 rProperties.end(),
1083 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1085 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1086 if (maViewContainer.empty())
1087 return;
1089 // this here might take some time
1091 WaitSymbolLock aLock (*this);
1093 mpPreviousSlide = mpCurrentSlide;
1094 mpCurrentSlide.reset();
1096 if (matches( mpPrefetchSlide, xSlide, xRootNode ))
1098 // prefetched slide matches:
1099 mpCurrentSlide = mpPrefetchSlide;
1101 else
1102 mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
1104 OSL_ASSERT( mpCurrentSlide );
1105 if (mpCurrentSlide)
1107 basegfx::B2DSize oldSlideSize;
1108 if( mpPreviousSlide )
1109 oldSlideSize = basegfx::B2DSize( mpPreviousSlide->getSlideSize() );
1111 basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
1113 // push new transformation to all views, if size changed
1114 if( !mpPreviousSlide || oldSlideSize != slideSize )
1116 for( const auto& pView : maViewContainer )
1117 pView->setViewSize( slideSize );
1119 // explicitly notify view change here,
1120 // because transformation might have changed:
1121 // optimization, this->notifyViewChange() would
1122 // repaint slide which is not necessary.
1123 maEventMultiplexer.notifyViewsChanged();
1126 // create slide transition, and add proper end event
1127 // (which then starts the slide effects
1128 // via CURRENT_SLIDE.show())
1129 ActivitySharedPtr pSlideChangeActivity (
1130 createSlideTransition(
1131 mpCurrentSlide->getXDrawPage(),
1132 mpPreviousSlide,
1133 mpCurrentSlide,
1134 makeEvent(
1135 [this] () { this->notifySlideTransitionEnded(false); },
1136 "SlideShowImpl::notifySlideTransitionEnded")));
1138 if (bSkipSlideTransition)
1140 // The transition activity was created for the side effects
1141 // (like sound transitions). Because we want to skip the
1142 // actual transition animation we do not need the activity
1143 // anymore.
1144 pSlideChangeActivity.reset();
1147 if (pSlideChangeActivity)
1149 // factory generated a slide transition - activate it!
1150 maActivitiesQueue.addActivity( pSlideChangeActivity );
1152 else
1154 // no transition effect on this slide - schedule slide
1155 // effect start event right away.
1156 maEventQueue.addEvent(
1157 makeEvent(
1158 [this] () { this->notifySlideTransitionEnded(true); },
1159 "SlideShowImpl::notifySlideTransitionEnded"));
1162 } // finally
1164 maListenerContainer.forEach<presentation::XSlideShowListener>(
1165 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1167 xListener->slideTransitionStarted();
1170 // We are currently rewinding an effect. This lead us from the next
1171 // slide to this one. To complete this we have to play back all main
1172 // sequence effects on this slide.
1173 if (bSkipAllMainSequenceEffects)
1174 maEffectRewinder.skipAllMainSequenceEffects();
1177 void SlideShowImpl::redisplayCurrentSlide()
1179 osl::MutexGuard const guard( m_aMutex );
1181 if (isDisposed())
1182 return;
1184 // precondition: must only be called from the main thread!
1185 DBG_TESTSOLARMUTEX();
1186 stopShow();
1188 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1189 if (maViewContainer.empty())
1190 return;
1192 // No transition effect on this slide - schedule slide
1193 // effect start event right away.
1194 maEventQueue.addEvent(
1195 makeEvent( [this] () { this->notifySlideTransitionEnded(true); },
1196 "SlideShowImpl::notifySlideTransitionEnded"));
1198 maListenerContainer.forEach<presentation::XSlideShowListener>(
1199 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1201 xListener->slideTransitionStarted();
1205 sal_Bool SlideShowImpl::nextEffect()
1207 osl::MutexGuard const guard( m_aMutex );
1209 if (isDisposed())
1210 return false;
1212 // precondition: must only be called from the main thread!
1213 DBG_TESTSOLARMUTEX();
1215 if (mbShowPaused)
1216 return true;
1217 else
1218 return maEventMultiplexer.notifyNextEffect();
1221 sal_Bool SlideShowImpl::previousEffect()
1223 osl::MutexGuard const guard( m_aMutex );
1225 if (isDisposed())
1226 return false;
1228 // precondition: must only be called from the main thread!
1229 DBG_TESTSOLARMUTEX();
1231 if (mbShowPaused)
1232 return true;
1233 else
1235 return maEffectRewinder.rewind(
1236 maScreenUpdater.createLock(),
1237 [this]() { return this->redisplayCurrentSlide(); },
1238 [this]() { return this->rewindEffectToPreviousSlide(); } );
1242 void SlideShowImpl::rewindEffectToPreviousSlide()
1244 // Show the wait symbol now and prevent it from showing temporary slide
1245 // content while effects are played back.
1246 WaitSymbolLock aLock (*this);
1248 // A previous call to EffectRewinder::Rewind could not rewind the current
1249 // effect because there are no effects on the current slide or none has
1250 // yet been displayed. Go to the previous slide.
1251 notifySlideEnded(true);
1253 // Process pending events once more in order to have the following
1254 // screen update show the last effect. Not sure whether this should be
1255 // necessary.
1256 maEventQueue.forceEmpty();
1258 // We have to call the screen updater before the wait symbol is turned
1259 // off. Otherwise the wait symbol would force the display of an
1260 // intermediate state of the slide (before the effects are replayed.)
1261 maScreenUpdater.commitUpdates();
1264 sal_Bool SlideShowImpl::startShapeActivity(
1265 uno::Reference<drawing::XShape> const& /*xShape*/ )
1267 osl::MutexGuard const guard( m_aMutex );
1269 // precondition: must only be called from the main thread!
1270 DBG_TESTSOLARMUTEX();
1272 // TODO(F3): NYI
1273 OSL_FAIL( "not yet implemented!" );
1274 return false;
1277 sal_Bool SlideShowImpl::stopShapeActivity(
1278 uno::Reference<drawing::XShape> const& /*xShape*/ )
1280 osl::MutexGuard const guard( m_aMutex );
1282 // precondition: must only be called from the main thread!
1283 DBG_TESTSOLARMUTEX();
1285 // TODO(F3): NYI
1286 OSL_FAIL( "not yet implemented!" );
1287 return false;
1290 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1292 osl::MutexGuard const guard( m_aMutex );
1294 if (isDisposed())
1295 return false;
1297 // precondition: must only be called from the main thread!
1298 DBG_TESTSOLARMUTEX();
1300 if (bPauseShow)
1301 mpPresTimer->pauseTimer();
1302 else
1303 mpPresTimer->continueTimer();
1305 maEventMultiplexer.notifyPauseMode(bPauseShow);
1307 mbShowPaused = bPauseShow;
1308 return true;
1311 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1313 osl::MutexGuard const guard( m_aMutex );
1315 if (isDisposed())
1316 return uno::Reference<drawing::XDrawPage>();
1318 // precondition: must only be called from the main thread!
1319 DBG_TESTSOLARMUTEX();
1321 if (mpCurrentSlide)
1322 return mpCurrentSlide->getXDrawPage();
1323 else
1324 return uno::Reference<drawing::XDrawPage>();
1327 sal_Bool SlideShowImpl::addView(
1328 uno::Reference<presentation::XSlideShowView> const& xView )
1330 osl::MutexGuard const guard( m_aMutex );
1332 if (isDisposed())
1333 return false;
1335 // precondition: must only be called from the main thread!
1336 DBG_TESTSOLARMUTEX();
1338 // first of all, check if view has a valid canvas
1339 ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
1340 ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(),
1341 "addView(): View does not provide a valid canvas" );
1343 UnoViewSharedPtr const pView( createSlideView(
1344 xView,
1345 maEventQueue,
1346 maEventMultiplexer ));
1347 if (!maViewContainer.addView( pView ))
1348 return false; // view already added
1350 // initialize view content
1351 // =======================
1353 if (mpCurrentSlide)
1355 // set view transformation
1356 const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
1357 pView->setViewSize( basegfx::B2DSize( slideSize.getX(),
1358 slideSize.getY() ) );
1361 // clear view area (since it's newly added,
1362 // we need a clean slate)
1363 pView->clearAll();
1365 // broadcast newly added view
1366 maEventMultiplexer.notifyViewAdded( pView );
1368 // set current mouse ptr
1369 pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1371 return true;
1374 sal_Bool SlideShowImpl::removeView(
1375 uno::Reference<presentation::XSlideShowView> const& xView )
1377 osl::MutexGuard const guard( m_aMutex );
1379 // precondition: must only be called from the main thread!
1380 DBG_TESTSOLARMUTEX();
1382 ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
1384 UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
1385 if( !pView )
1386 return false; // view was not added in the first place
1388 // remove view from EventMultiplexer (mouse events etc.)
1389 maEventMultiplexer.notifyViewRemoved( pView );
1391 pView->_dispose();
1393 return true;
1396 void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory )
1398 //Retrieve Polygons if user ends presentation by context menu
1399 if (mpCurrentSlide)
1401 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1402 maPolygons.erase(mpCurrentSlide->getXDrawPage());
1404 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1407 //Creating the layer for shapes drawn during slideshow
1408 // query for the XLayerManager
1409 uno::Reference< drawing::XLayerSupplier > xLayerSupplier(xDocFactory, uno::UNO_QUERY);
1410 uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager();
1411 uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY);
1413 // create layer
1414 uno::Reference< drawing::XLayer > xDrawnInSlideshow;
1415 uno::Any aPropLayer;
1416 OUString sLayerName = "DrawnInSlideshow";
1417 if (xNameAccess->hasByName(sLayerName))
1419 xNameAccess->getByName(sLayerName) >>= xDrawnInSlideshow;
1421 else
1423 xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
1424 aPropLayer <<= sLayerName;
1425 xDrawnInSlideshow->setPropertyValue("Name", aPropLayer);
1428 // ODF defaults from ctor of SdrLayer are not automatically set on the here
1429 // created XLayer. Need to be done explicitly here.
1430 aPropLayer <<= true;
1431 xDrawnInSlideshow->setPropertyValue("IsVisible", aPropLayer);
1432 xDrawnInSlideshow->setPropertyValue("IsPrintable", aPropLayer);
1433 aPropLayer <<= false;
1434 xDrawnInSlideshow->setPropertyValue("IsLocked", aPropLayer);
1436 //Register polygons for each slide
1437 for( const auto& rPoly : maPolygons )
1439 PolyPolygonVector aPolygons = rPoly.second;
1440 //Get shapes for the slide
1441 css::uno::Reference< css::drawing::XShapes > Shapes = rPoly.first;
1442 //Retrieve polygons for one slide
1443 for( const auto& pPolyPoly : aPolygons )
1445 ::basegfx::B2DPolyPolygon b2DPolyPoly = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(pPolyPoly->getUNOPolyPolygon());
1447 //Normally there is only one polygon
1448 for(sal_uInt32 i=0; i< b2DPolyPoly.count();i++)
1450 const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
1451 sal_uInt32 nPoints = aPoly.count();
1453 if( nPoints > 1)
1455 //create the PolyLineShape
1456 uno::Reference< uno::XInterface > polyshape(xDocFactory->createInstance(
1457 "com.sun.star.drawing.PolyLineShape" ) );
1458 uno::Reference< drawing::XShape > rPolyShape(polyshape, uno::UNO_QUERY);
1460 //Add the shape to the slide
1461 Shapes->add(rPolyShape);
1463 //Retrieve shape properties
1464 uno::Reference< beans::XPropertySet > aXPropSet( rPolyShape, uno::UNO_QUERY );
1465 //Construct a sequence of points sequence
1466 drawing::PointSequenceSequence aRetval;
1467 //Create only one sequence for one polygon
1468 aRetval.realloc( 1 );
1469 // Retrieve the sequence of points from aRetval
1470 drawing::PointSequence* pOuterSequence = aRetval.getArray();
1471 // Create 2 points in this sequence
1472 pOuterSequence->realloc(nPoints);
1473 // Get these points which are in an array
1474 awt::Point* pInnerSequence = pOuterSequence->getArray();
1475 for( sal_uInt32 n = 0; n < nPoints; n++ )
1477 //Create a point from the polygon
1478 *pInnerSequence++ = awt::Point(
1479 basegfx::fround(aPoly.getB2DPoint(n).getX()),
1480 basegfx::fround(aPoly.getB2DPoint(n).getY()));
1483 //Fill the properties
1484 //Give the built PointSequenceSequence.
1485 uno::Any aParam;
1486 aParam <<= aRetval;
1487 aXPropSet->setPropertyValue("PolyPolygon", aParam );
1489 //LineStyle : SOLID by default
1490 drawing::LineStyle eLS;
1491 eLS = drawing::LineStyle_SOLID;
1492 aXPropSet->setPropertyValue("LineStyle", uno::Any(eLS) );
1494 //LineColor
1495 sal_uInt32 nLineColor;
1496 nLineColor = pPolyPoly->getRGBALineColor();
1497 //Transform polygon color from RRGGBBAA to AARRGGBB
1498 aXPropSet->setPropertyValue("LineColor", uno::Any(RGBAColor2UnoColor(nLineColor)) );
1500 //LineWidth
1501 double fLineWidth;
1502 fLineWidth = pPolyPoly->getStrokeWidth();
1503 aXPropSet->setPropertyValue("LineWidth", uno::Any(static_cast<sal_Int32>(fLineWidth)) );
1505 // make polygons special
1506 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1513 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1515 osl::MutexGuard const guard( m_aMutex );
1517 if (isDisposed())
1518 return false;
1520 // precondition: must only be called from the main thread!
1521 DBG_TESTSOLARMUTEX();
1523 if ( rProperty.Name == "AutomaticAdvancement" )
1525 double nTimeout(0.0);
1526 mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
1527 if (mbAutomaticAdvancementMode)
1529 maEventMultiplexer.setAutomaticTimeout( nTimeout );
1531 maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
1532 return true;
1535 if ( rProperty.Name == "UserPaintColor" )
1537 sal_Int32 nColor(0);
1538 if (rProperty.Value >>= nColor)
1540 OSL_ENSURE( mbMouseVisible,
1541 "setProperty(): User paint overrides invisible mouse" );
1543 // enable user paint
1544 maUserPaintColor = unoColor2RGBColor(nColor);
1545 if( mpCurrentSlide && !mpCurrentSlide->isPaintOverlayActive() )
1546 mpCurrentSlide->enablePaintOverlay();
1548 maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
1550 else
1552 // disable user paint
1553 maUserPaintColor.reset();
1554 maEventMultiplexer.notifyUserPaintDisabled();
1557 resetCursor();
1559 return true;
1562 //adding support for erasing features in UserPaintOverlay
1563 if ( rProperty.Name == "EraseAllInk" )
1565 bool bEraseAllInk(false);
1566 if (rProperty.Value >>= bEraseAllInk)
1568 OSL_ENSURE( mbMouseVisible,
1569 "setProperty(): User paint overrides invisible mouse" );
1571 // enable user paint
1572 maEraseAllInk = bEraseAllInk;
1573 maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
1576 return true;
1579 if ( rProperty.Name == "SwitchPenMode" )
1581 bool bSwitchPenMode(false);
1582 if (rProperty.Value >>= bSwitchPenMode)
1584 OSL_ENSURE( mbMouseVisible,
1585 "setProperty(): User paint overrides invisible mouse" );
1587 if(bSwitchPenMode){
1588 // Switch to Pen Mode
1589 maEventMultiplexer.notifySwitchPenMode();
1592 return true;
1595 if ( rProperty.Name == "SwitchEraserMode" )
1597 bool bSwitchEraserMode(false);
1598 if (rProperty.Value >>= bSwitchEraserMode)
1600 OSL_ENSURE( mbMouseVisible,
1601 "setProperty(): User paint overrides invisible mouse" );
1602 if(bSwitchEraserMode){
1603 // switch to Eraser mode
1604 maEventMultiplexer.notifySwitchEraserMode();
1608 return true;
1611 if ( rProperty.Name == "EraseInk" )
1613 sal_Int32 nEraseInk(100);
1614 if (rProperty.Value >>= nEraseInk)
1616 OSL_ENSURE( mbMouseVisible,
1617 "setProperty(): User paint overrides invisible mouse" );
1619 // enable user paint
1620 maEraseInk = nEraseInk;
1621 maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
1624 return true;
1627 // new Property for pen's width
1628 if ( rProperty.Name == "UserPaintStrokeWidth" )
1630 double nWidth(4.0);
1631 if (rProperty.Value >>= nWidth)
1633 OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
1634 // enable user paint stroke width
1635 maUserPaintStrokeWidth = nWidth;
1636 maEventMultiplexer.notifyUserPaintStrokeWidth( maUserPaintStrokeWidth );
1639 return true;
1642 if ( rProperty.Name == "AdvanceOnClick" )
1644 bool bAdvanceOnClick = false;
1645 if (! (rProperty.Value >>= bAdvanceOnClick))
1646 return false;
1647 maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1648 return true;
1651 if ( rProperty.Name == "DisableAnimationZOrder" )
1653 bool bDisableAnimationZOrder = false;
1654 if (! (rProperty.Value >>= bDisableAnimationZOrder))
1655 return false;
1656 mbDisableAnimationZOrder = bDisableAnimationZOrder;
1657 return true;
1660 if ( rProperty.Name == "ImageAnimationsAllowed" )
1662 if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1663 return false;
1665 // TODO(F3): Forward to slides!
1666 return true;
1669 if ( rProperty.Name == "MouseVisible" )
1671 if (! (rProperty.Value >>= mbMouseVisible))
1672 return false;
1674 requestCursor(mnCurrentCursor);
1676 return true;
1679 if ( rProperty.Name == "ForceManualAdvance" )
1681 return (rProperty.Value >>= mbForceManualAdvance);
1684 if ( rProperty.Name == "RehearseTimings" )
1686 bool bRehearseTimings = false;
1687 if (! (rProperty.Value >>= bRehearseTimings))
1688 return false;
1690 if (bRehearseTimings)
1692 // TODO(Q3): Move to slide
1693 mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1694 SlideShowContext(
1695 mpDummyPtr,
1696 maEventQueue,
1697 maEventMultiplexer,
1698 maScreenUpdater,
1699 maActivitiesQueue,
1700 maUserEventQueue,
1701 *this,
1702 *this,
1703 maViewContainer,
1704 mxComponentContext,
1705 mpBox2DDummyPtr ) );
1707 else if (mpRehearseTimingsActivity)
1709 // removes timer from all views:
1710 mpRehearseTimingsActivity->dispose();
1711 mpRehearseTimingsActivity.reset();
1713 return true;
1716 if ( rProperty.Name == "WaitSymbolBitmap" )
1718 uno::Reference<rendering::XBitmap> xBitmap;
1719 if (! (rProperty.Value >>= xBitmap))
1720 return false;
1722 mpWaitSymbol = WaitSymbol::create( xBitmap,
1723 maScreenUpdater,
1724 maEventMultiplexer,
1725 maViewContainer );
1727 return true;
1730 if ( rProperty.Name == "PointerSymbolBitmap" )
1732 uno::Reference<rendering::XBitmap> xBitmap;
1733 if (! (rProperty.Value >>= xBitmap))
1734 return false;
1736 mpPointerSymbol = PointerSymbol::create( xBitmap,
1737 maScreenUpdater,
1738 maEventMultiplexer,
1739 maViewContainer );
1741 return true;
1744 if ( rProperty.Name == "PointerVisible" )
1746 bool visible;
1747 if (!(rProperty.Value >>= visible))
1748 return false;
1750 mpPointerSymbol->setVisible(visible);
1751 return true;
1754 if ( rProperty.Name == "PointerPosition")
1756 css::geometry::RealPoint2D pos;
1757 if (! (rProperty.Value >>= pos))
1758 return false;
1760 mpPointerSymbol->viewsChanged(pos);
1761 return true;
1764 if (rProperty.Name == "NoSlideTransitions" )
1766 return (rProperty.Value >>= mbNoSlideTransitions);
1769 if ( rProperty.Name == "IsSoundEnabled" )
1771 uno::Sequence<uno::Any> aValues;
1772 uno::Reference<presentation::XSlideShowView> xView;
1773 bool bValue (false);
1774 if ((rProperty.Value >>= aValues)
1775 && aValues.getLength()==2
1776 && (aValues[0] >>= xView)
1777 && (aValues[1] >>= bValue))
1779 // Look up the view.
1780 auto iView = std::find_if(maViewContainer.begin(), maViewContainer.end(),
1781 [&xView](const UnoViewSharedPtr& rxView) { return rxView && rxView->getUnoView() == xView; });
1782 if (iView != maViewContainer.end())
1784 // Store the flag at the view so that media shapes have
1785 // access to it.
1786 (*iView)->setIsSoundEnabled(bValue);
1787 return true;
1792 return false;
1795 void SlideShowImpl::addSlideShowListener(
1796 uno::Reference<presentation::XSlideShowListener> const& xListener )
1798 osl::MutexGuard const guard( m_aMutex );
1800 if (isDisposed())
1801 return;
1803 // container syncs with passed mutex ref
1804 maListenerContainer.addInterface(xListener);
1807 void SlideShowImpl::removeSlideShowListener(
1808 uno::Reference<presentation::XSlideShowListener> const& xListener )
1810 osl::MutexGuard const guard( m_aMutex );
1812 // container syncs with passed mutex ref
1813 maListenerContainer.removeInterface(xListener);
1816 void SlideShowImpl::addShapeEventListener(
1817 uno::Reference<presentation::XShapeEventListener> const& xListener,
1818 uno::Reference<drawing::XShape> const& xShape )
1820 osl::MutexGuard const guard( m_aMutex );
1822 if (isDisposed())
1823 return;
1825 // precondition: must only be called from the main thread!
1826 DBG_TESTSOLARMUTEX();
1828 ShapeEventListenerMap::iterator aIter;
1829 if( (aIter=maShapeEventListeners.find( xShape )) ==
1830 maShapeEventListeners.end() )
1832 // no entry for this shape -> create one
1833 aIter = maShapeEventListeners.emplace(
1834 xShape,
1835 std::make_shared<comphelper::OInterfaceContainerHelper2>(
1836 m_aMutex)).first;
1839 // add new listener to broadcaster
1840 if( aIter->second )
1841 aIter->second->addInterface( xListener );
1843 maEventMultiplexer.notifyShapeListenerAdded(xShape);
1846 void SlideShowImpl::removeShapeEventListener(
1847 uno::Reference<presentation::XShapeEventListener> const& xListener,
1848 uno::Reference<drawing::XShape> const& xShape )
1850 osl::MutexGuard const guard( m_aMutex );
1852 // precondition: must only be called from the main thread!
1853 DBG_TESTSOLARMUTEX();
1855 ShapeEventListenerMap::iterator aIter;
1856 if( (aIter = maShapeEventListeners.find( xShape )) !=
1857 maShapeEventListeners.end() )
1859 // entry for this shape found -> remove listener from
1860 // helper object
1861 ENSURE_OR_THROW(
1862 aIter->second,
1863 "SlideShowImpl::removeShapeEventListener(): "
1864 "listener map contains NULL broadcast helper" );
1866 aIter->second->removeInterface( xListener );
1869 maEventMultiplexer.notifyShapeListenerRemoved(xShape);
1872 void SlideShowImpl::setShapeCursor(
1873 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
1875 osl::MutexGuard const guard( m_aMutex );
1877 if (isDisposed())
1878 return;
1880 // precondition: must only be called from the main thread!
1881 DBG_TESTSOLARMUTEX();
1883 ShapeCursorMap::iterator aIter;
1884 if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
1886 // no entry for this shape -> create one
1887 if( nPointerShape != awt::SystemPointer::ARROW )
1889 // add new entry, unless shape shall display
1890 // normal pointer arrow -> no need to handle that
1891 // case
1892 maShapeCursors.emplace(xShape, nPointerShape);
1895 else if( nPointerShape == awt::SystemPointer::ARROW )
1897 // shape shall display normal cursor -> can disable
1898 // the cursor and clear the entry
1899 maShapeCursors.erase( xShape );
1901 else
1903 // existing entry found, update with new cursor ID
1904 aIter->second = nPointerShape;
1908 bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape )
1910 mnCurrentCursor = nCursorShape;
1912 const sal_Int16 nActualCursor = calcActiveCursor(mnCurrentCursor);
1914 // change all views to the requested cursor ID
1915 for( const auto& pView : maViewContainer )
1916 pView->setCursorShape( nActualCursor );
1918 return nActualCursor==nCursorShape;
1921 void SlideShowImpl::resetCursor()
1923 mnCurrentCursor = awt::SystemPointer::ARROW;
1925 const sal_Int16 nActualCursor = calcActiveCursor( mnCurrentCursor );
1926 // change all views to the default cursor ID
1927 for( const auto& pView : maViewContainer )
1928 pView->setCursorShape( nActualCursor );
1931 sal_Bool SlideShowImpl::update( double & nNextTimeout )
1933 osl::MutexGuard const guard( m_aMutex );
1935 if (isDisposed())
1936 return false;
1938 // precondition: update() must only be called from the
1939 // main thread!
1940 DBG_TESTSOLARMUTEX();
1942 if( mbShowPaused )
1944 // commit frame (might be repaints pending)
1945 maScreenUpdater.commitUpdates();
1947 return false;
1949 else
1951 // TODO(F2): re-evaluate whether that timer lagging makes
1952 // sense.
1954 // hold timer, while processing the queues:
1955 // 1. when there is more than one active activity this ensures the
1956 // same time for all activities and events
1957 // 2. processing of events may lead to creation of further events
1958 // that have zero delay. While the timer is stopped these events
1959 // are processed in the same run.
1961 //Get a shared-ptr that outlives the scope-guard which will
1962 //ensure that the pointed-to-item exists in the case of a
1963 //::dispose clearing mpPresTimer
1964 std::shared_ptr<canvas::tools::ElapsedTime> xTimer(mpPresTimer);
1965 comphelper::ScopeGuard scopeGuard(
1966 [&xTimer]() { return xTimer->releaseTimer(); } );
1967 xTimer->holdTimer();
1969 // process queues
1970 maEventQueue.process();
1972 // #i118671# the call above may execute a macro bound to an object. In
1973 // that case this macro may have destroyed this local slideshow so that it
1974 // is disposed (see bugdoc at task). In that case, detect this and exit
1975 // gently from this slideshow. Do not forget to disable the scoped
1976 // call to mpPresTimer, this will be deleted if we are disposed.
1977 if (isDisposed())
1979 scopeGuard.dismiss();
1980 return false;
1983 maActivitiesQueue.process();
1985 // commit frame to screen
1986 maFrameSynchronization.Synchronize();
1987 maScreenUpdater.commitUpdates();
1989 // TODO(Q3): remove need to call dequeued() from
1990 // activities. feels like a wart.
1992 // Rationale for ActivitiesQueue::processDequeued(): when
1993 // an activity ends, it usually pushed the end state to
1994 // the animated shape in question, and ends the animation
1995 // (which, in turn, will usually disable shape sprite
1996 // mode). Disabling shape sprite mode causes shape
1997 // repaint, which, depending on slide content, takes
1998 // considerably more time than sprite updates. Thus, the
1999 // last animation step tends to look delayed. To
2000 // camouflage this, reaching end position and disabling
2001 // sprite mode is split into two (normal Activity::end(),
2002 // and Activity::dequeued()). Now, the reason to call
2003 // commitUpdates() twice here is caused by the unrelated
2004 // fact that during wait cursor display/hide, the screen
2005 // is updated, and shows hidden sprites, but, in case of
2006 // leaving the second commitUpdates() call out and punting
2007 // that to the next round, no updated static slide
2008 // content. In short, the last shape animation of a slide
2009 // tends to blink at its end.
2011 // process dequeued activities _after_ commit to screen
2012 maActivitiesQueue.processDequeued();
2014 // commit frame to screen
2015 maScreenUpdater.commitUpdates();
2017 // Time held until here
2019 const bool bActivitiesLeft = ! maActivitiesQueue.isEmpty();
2020 const bool bTimerEventsLeft = ! maEventQueue.isEmpty();
2021 const bool bRet = (bActivitiesLeft || bTimerEventsLeft);
2023 if (bRet)
2025 // calc nNextTimeout value:
2026 if (bActivitiesLeft)
2028 // Activity queue is not empty. Tell caller that we would
2029 // like to render another frame.
2031 // Return a zero time-out to signal our caller to call us
2032 // back as soon as possible. The actual timing, waiting the
2033 // appropriate amount of time between frames, is then done
2034 // by the maFrameSynchronization object.
2035 nNextTimeout = 0;
2036 maFrameSynchronization.Activate();
2038 else
2040 // timer events left:
2041 // difference from current time (nota bene:
2042 // time no longer held here!) to the next event in
2043 // the event queue.
2045 // #i61190# Retrieve next timeout only _after_
2046 // processing activity queue
2048 // ensure positive value:
2049 nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() );
2051 // There is no active animation so the frame rate does not
2052 // need to be synchronized.
2053 maFrameSynchronization.Deactivate();
2056 mbSlideShowIdle = false;
2059 #if defined(DBG_UTIL)
2060 // when slideshow is idle, issue an XUpdatable::update() call
2061 // exactly once after a previous animation sequence finished -
2062 // this might trigger screen dumps on some canvas
2063 // implementations
2064 if( !mbSlideShowIdle &&
2065 (!bRet ||
2066 nNextTimeout > 1.0) )
2068 for( const auto& pView : maViewContainer )
2072 uno::Reference< presentation::XSlideShowView > xView( pView->getUnoView(),
2073 uno::UNO_SET_THROW );
2074 uno::Reference<util::XUpdatable> const xUpdatable(
2075 xView->getCanvas(), uno::UNO_QUERY);
2076 if (xUpdatable.is()) // not supported in PresenterCanvas
2078 xUpdatable->update();
2081 catch( uno::RuntimeException& )
2083 throw;
2085 catch( uno::Exception& )
2087 TOOLS_WARN_EXCEPTION( "slideshow", "" );
2091 mbSlideShowIdle = true;
2093 #endif
2095 return bRet;
2099 void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide )
2101 osl::MutexGuard const guard( m_aMutex );
2103 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2104 OSL_ENSURE( mpCurrentSlide,
2105 "notifySlideTransitionEnded(): Invalid current slide" );
2106 if (mpCurrentSlide)
2108 mpCurrentSlide->update_settings( !!maUserPaintColor, maUserPaintColor ? *maUserPaintColor : RGBColor(), maUserPaintStrokeWidth );
2110 // first init show, to give the animations
2111 // the chance to register SlideStartEvents
2112 const bool bBackgroundLayerRendered( !bPaintSlide );
2113 mpCurrentSlide->show( bBackgroundLayerRendered );
2114 maEventMultiplexer.notifySlideStartEvent();
2118 void queryAutomaticSlideTransition( uno::Reference<drawing::XDrawPage> const& xDrawPage,
2119 double& nAutomaticNextSlideTimeout,
2120 bool& bHasAutomaticNextSlide )
2122 // retrieve slide change parameters from XDrawPage
2123 // ===============================================
2125 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
2126 uno::UNO_QUERY );
2128 sal_Int32 nChange(0);
2129 if( !xPropSet.is() ||
2130 !getPropertyValue( nChange,
2131 xPropSet,
2132 "Change") )
2134 SAL_INFO("slideshow",
2135 "queryAutomaticSlideTransition(): "
2136 "Could not extract slide change mode from XDrawPage - assuming <none>" );
2139 bHasAutomaticNextSlide = nChange == 1;
2141 if( !xPropSet.is() ||
2142 !getPropertyValue( nAutomaticNextSlideTimeout,
2143 xPropSet,
2144 "HighResDuration") )
2146 SAL_INFO("slideshow",
2147 "queryAutomaticSlideTransition(): "
2148 "Could not extract slide transition timeout from "
2149 "XDrawPage - assuming 1 sec" );
2153 void SlideShowImpl::notifySlideAnimationsEnded()
2155 osl::MutexGuard const guard( m_aMutex );
2157 //Draw polygons above animations
2158 mpCurrentSlide->drawPolygons();
2160 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2162 // This struct will receive the (interruptable) event,
2163 // that triggers the notifySlideEnded() method.
2164 InterruptableEventPair aNotificationEvents;
2166 if( maEventMultiplexer.getAutomaticMode() )
2168 OSL_ENSURE( ! mpRehearseTimingsActivity,
2169 "unexpected: RehearseTimings mode!" );
2171 // schedule a slide end event, with automatic mode's
2172 // delay
2173 aNotificationEvents = makeInterruptableDelay(
2174 [this]() { return this->notifySlideEnded( false ); },
2175 maEventMultiplexer.getAutomaticTimeout() );
2177 else
2179 OSL_ENSURE( mpCurrentSlide,
2180 "notifySlideAnimationsEnded(): Invalid current slide!" );
2182 bool bHasAutomaticNextSlide=false;
2183 double nAutomaticNextSlideTimeout=0.0;
2184 queryAutomaticSlideTransition(mpCurrentSlide->getXDrawPage(),
2185 nAutomaticNextSlideTimeout,
2186 bHasAutomaticNextSlide);
2188 // check whether slide transition should happen
2189 // 'automatically'. If yes, simply schedule the
2190 // specified timeout.
2191 // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
2192 // override any individual slide setting, to always
2193 // step slides manually.
2194 if( !mbForceManualAdvance &&
2195 !mpRehearseTimingsActivity &&
2196 bHasAutomaticNextSlide )
2198 aNotificationEvents = makeInterruptableDelay(
2199 [this]() { return this->notifySlideEnded( false ); },
2200 nAutomaticNextSlideTimeout);
2202 // TODO(F2): Provide a mechanism to let the user override
2203 // this automatic timeout via next()
2205 else
2207 if (mpRehearseTimingsActivity)
2208 mpRehearseTimingsActivity->start();
2210 // generate click event. Thus, the user must
2211 // trigger the actual end of a slide. No need to
2212 // generate interruptable event here, there's no
2213 // timeout involved.
2214 aNotificationEvents.mpImmediateEvent =
2215 makeEvent( [this] () { this->notifySlideEnded(false); },
2216 "SlideShowImpl::notifySlideEnded");
2220 // register events on the queues. To make automatic slide
2221 // changes interruptable, register the interruption event
2222 // as a nextEffectEvent target. Note that the timeout
2223 // event is optional (e.g. manual slide changes don't
2224 // generate a timeout)
2225 maUserEventQueue.registerNextEffectEvent(
2226 aNotificationEvents.mpImmediateEvent );
2228 if( aNotificationEvents.mpTimeoutEvent )
2229 maEventQueue.addEvent( aNotificationEvents.mpTimeoutEvent );
2231 // current slide's main sequence is over. Now should be
2232 // the time to prefetch the next slide (if any), and
2233 // prepare the initial slide bitmap (speeds up slide
2234 // change setup time a lot). Show the wait cursor, this
2235 // indeed might take some seconds.
2237 WaitSymbolLock aLock (*this);
2239 if (! matches( mpPrefetchSlide,
2240 mxPrefetchSlide, mxPrefetchAnimationNode ))
2242 mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
2243 mxPrefetchAnimationNode );
2245 if (mpPrefetchSlide)
2247 // ignore return value, this is just to populate
2248 // Slide's internal bitmap buffer, such that the time
2249 // needed to generate the slide bitmap is not spent
2250 // when the slide change is requested.
2251 mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
2253 } // finally
2255 maListenerContainer.forEach<presentation::XSlideShowListener>(
2256 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
2258 xListener->slideAnimationsEnded();
2262 void SlideShowImpl::notifySlideEnded (const bool bReverse)
2264 osl::MutexGuard const guard( m_aMutex );
2266 OSL_ENSURE( !isDisposed(), "### already disposed!" );
2268 if (mpRehearseTimingsActivity && !bReverse)
2270 const double time = mpRehearseTimingsActivity->stop();
2271 if (mpRehearseTimingsActivity->hasBeenClicked())
2273 // save time at current drawpage:
2274 uno::Reference<beans::XPropertySet> xPropSet(
2275 mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
2276 OSL_ASSERT( xPropSet.is() );
2277 if (xPropSet.is())
2279 xPropSet->setPropertyValue(
2280 "Change",
2281 uno::Any( static_cast<sal_Int32>(1) ) );
2282 xPropSet->setPropertyValue(
2283 "Duration",
2284 uno::Any( static_cast<sal_Int32>(time) ) );
2289 if (bReverse)
2290 maEventMultiplexer.notifySlideEndEvent();
2292 stopShow(); // MUST call that: results in
2293 // maUserEventQueue.clear(). What's more,
2294 // stopShow()'s currSlide->hide() call is
2295 // now also required, notifySlideEnded()
2296 // relies on that
2297 // unconditionally. Otherwise, genuine
2298 // shape animations (drawing layer and
2299 // GIF) will not be stopped.
2301 maListenerContainer.forEach<presentation::XSlideShowListener>(
2302 [&bReverse]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2303 { return xListener->slideEnded( bReverse ); } );
2306 bool SlideShowImpl::notifyHyperLinkClicked( OUString const& hyperLink )
2308 osl::MutexGuard const guard( m_aMutex );
2310 maListenerContainer.forEach<presentation::XSlideShowListener>(
2311 [&hyperLink]( const uno::Reference< presentation::XSlideShowListener >& xListener )
2312 { return xListener->hyperLinkClicked( hyperLink ); } );
2313 return true;
2316 /** Notification from eventmultiplexer that an animation event has occurred.
2317 This will be forwarded to all registered XSlideShoeListener
2319 bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
2321 osl::MutexGuard const guard( m_aMutex );
2323 uno::Reference<animations::XAnimationNode> xNode( rNode->getXAnimationNode() );
2325 switch( rNode->getState() )
2327 case AnimationNode::ACTIVE:
2328 maListenerContainer.forEach<presentation::XSlideShowListener>(
2329 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2330 { return xListener->beginEvent( xNode ); } );
2331 break;
2333 case AnimationNode::FROZEN:
2334 case AnimationNode::ENDED:
2335 maListenerContainer.forEach<presentation::XSlideShowListener>(
2336 [&xNode]( const uno::Reference< animations::XAnimationListener >& xListener )
2337 { return xListener->endEvent( xNode ); } );
2338 if(mpCurrentSlide->isPaintOverlayActive())
2339 mpCurrentSlide->drawPolygons();
2340 break;
2341 default:
2342 break;
2345 return true;
2348 std::shared_ptr<avmedia::MediaTempFile> SlideShowImpl::getMediaTempFile(const OUString& aUrl)
2350 std::shared_ptr<avmedia::MediaTempFile> aRet;
2352 if (!mxSBD.is())
2353 return aRet;
2355 comphelper::LifecycleProxy aProxy;
2356 uno::Reference<io::XStream> xStream =
2357 comphelper::OStorageHelper::GetStreamAtPackageURL(mxSBD->getDocumentStorage(), aUrl,
2358 css::embed::ElementModes::READ, aProxy);
2360 uno::Reference<io::XInputStream> xInStream = xStream->getInputStream();
2361 if (xInStream.is())
2363 sal_Int32 nLastDot = aUrl.lastIndexOf('.');
2364 sal_Int32 nLastSlash = aUrl.lastIndexOf('/');
2365 OUString sDesiredExtension;
2366 if (nLastDot > nLastSlash && nLastDot+1 < aUrl.getLength())
2367 sDesiredExtension = aUrl.copy(nLastDot);
2369 OUString sTempUrl;
2370 if (::avmedia::CreateMediaTempFile(xInStream, sTempUrl, sDesiredExtension))
2371 aRet = std::make_shared<avmedia::MediaTempFile>(sTempUrl);
2373 xInStream->closeInput();
2376 return aRet;
2379 //===== FrameSynchronization ==================================================
2381 FrameSynchronization::FrameSynchronization (const double nFrameDuration)
2382 : maTimer(),
2383 mnFrameDuration(nFrameDuration),
2384 mnNextFrameTargetTime(0),
2385 mbIsActive(false)
2387 MarkCurrentFrame();
2390 void FrameSynchronization::MarkCurrentFrame()
2392 mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2395 void FrameSynchronization::Synchronize()
2397 if (mbIsActive)
2399 // Do busy waiting for now.
2400 for(;;)
2402 double remainingTime = mnNextFrameTargetTime - maTimer.getElapsedTime();
2403 if(remainingTime <= 0)
2404 break;
2405 // Try to sleep most of it.
2406 int remainingMilliseconds = remainingTime * 1000;
2407 if(remainingMilliseconds > 2)
2408 osl::Thread::wait(std::chrono::milliseconds(remainingMilliseconds - 2));
2412 MarkCurrentFrame();
2415 void FrameSynchronization::Activate()
2417 mbIsActive = true;
2420 void FrameSynchronization::Deactivate()
2422 mbIsActive = false;
2425 } // anon namespace
2427 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
2428 slideshow_SlideShowImpl_get_implementation(
2429 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
2431 return cppu::acquire(new SlideShowImpl(context));
2433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */