Bump version to 6.0-36
[LibreOffice.git] / slideshow / source / engine / slideshowimpl.cxx
blob18c2534ce54114b9750e3c9bfd1b983a234d269e
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/factory.hxx>
26 #include <cppuhelper/implementationentry.hxx>
27 #include <cppuhelper/interfacecontainer.h>
28 #include <cppuhelper/exc_hlp.hxx>
30 #include <comphelper/anytostring.hxx>
31 #include <comphelper/make_shared_from_uno.hxx>
32 #include <comphelper/scopeguard.hxx>
33 #include <comphelper/servicedecl.hxx>
34 #include <comphelper/namecontainer.hxx>
36 #include <cppcanvas/spritecanvas.hxx>
37 #include <cppcanvas/vclfactory.hxx>
38 #include <cppcanvas/basegfxfactory.hxx>
40 #include <tools/debug.hxx>
42 #include <basegfx/point/b2dpoint.hxx>
43 #include <basegfx/polygon/b2dpolygon.hxx>
44 #include <basegfx/matrix/b2dhommatrix.hxx>
45 #include <basegfx/polygon/b2dpolygontools.hxx>
46 #include <basegfx/polygon/b2dpolypolygontools.hxx>
47 #include <basegfx/utils/canvastools.hxx>
49 #include <vcl/font.hxx>
50 #include <rtl/ref.hxx>
52 #include <com/sun/star/beans/XPropertySet.hpp>
53 #include <com/sun/star/util/XModifyListener.hpp>
54 #include <com/sun/star/util/XUpdatable.hpp>
55 #include <com/sun/star/awt/XPaintListener.hpp>
56 #include <com/sun/star/awt/SystemPointer.hpp>
57 #include <com/sun/star/animations/TransitionType.hpp>
58 #include <com/sun/star/animations/TransitionSubType.hpp>
59 #include <com/sun/star/presentation/XSlideShow.hpp>
60 #include <com/sun/star/presentation/XSlideShowListener.hpp>
61 #include <com/sun/star/lang/NoSupportException.hpp>
62 #include <com/sun/star/lang/XServiceInfo.hpp>
63 #include <com/sun/star/lang/XServiceName.hpp>
64 #include <com/sun/star/lang/XComponent.hpp>
65 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
66 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
67 #include <com/sun/star/drawing/PointSequence.hpp>
68 #include <com/sun/star/drawing/XLayer.hpp>
69 #include <com/sun/star/drawing/XLayerSupplier.hpp>
70 #include <com/sun/star/drawing/XLayerManager.hpp>
71 #include <com/sun/star/container/XNameAccess.hpp>
73 #include <com/sun/star/uno/Reference.hxx>
74 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
76 #include <unoviewcontainer.hxx>
77 #include <transitionfactory.hxx>
78 #include <eventmultiplexer.hxx>
79 #include <usereventqueue.hxx>
80 #include <eventqueue.hxx>
81 #include <cursormanager.hxx>
82 #include <slideshowcontext.hxx>
83 #include <activitiesqueue.hxx>
84 #include <activitiesfactory.hxx>
85 #include <interruptabledelayevent.hxx>
86 #include <slide.hxx>
87 #include <shapemaps.hxx>
88 #include <slideview.hxx>
89 #include <tools.hxx>
90 #include <unoview.hxx>
91 #include <slidebitmap.hxx>
92 #include "rehearsetimingsactivity.hxx"
93 #include "waitsymbol.hxx"
94 #include "effectrewinder.hxx"
95 #include <framerate.hxx>
96 #include "pointersymbol.hxx"
98 #include <map>
99 #include <vector>
100 #include <iterator>
101 #include <string>
102 #include <algorithm>
103 #include <stdio.h>
104 #include <iostream>
106 using namespace com::sun::star;
107 using namespace ::slideshow::internal;
109 namespace {
111 /** During animations the update() method tells its caller to call it as
112 soon as possible. This gives us more time to render the next frame and
113 still maintain a steady frame rate. This class is responsible for
114 synchronizing the display of new frames and thus keeping the frame rate
115 steady.
117 class FrameSynchronization
119 public:
120 /** Create new object with a predefined duration between two frames.
121 @param nFrameDuration
122 The preferred duration between the display of two frames in
123 seconds.
125 explicit FrameSynchronization (const double nFrameDuration);
127 /** Set the current time as the time at which the current frame is
128 displayed. From this the target time of the next frame is derived.
130 void MarkCurrentFrame();
132 /** When there is time left until the next frame is due then wait.
133 Otherwise return without delay.
135 void Synchronize();
137 /** Activate frame synchronization when an animation is active and
138 frames are to be displayed in a steady rate. While active
139 Synchronize() will wait until the frame duration time has passed.
141 void Activate();
143 /** Deactivate frame sychronization when no animation is active and the
144 time between frames depends on user actions and other external
145 sources. While deactivated Synchronize() will return without delay.
147 void Deactivate();
149 private:
150 /** The timer that is used for synchronization is independent from the
151 one used by SlideShowImpl: it is not paused or modified by
152 animations.
154 canvas::tools::ElapsedTime maTimer;
155 /** Time between the display of frames. Enforced only when mbIsActive
156 is <TRUE/>.
158 const double mnFrameDuration;
159 /** Time (of maTimer) when the next frame shall be displayed.
160 Synchronize() will wait until this time.
162 double mnNextFrameTargetTime;
163 /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise
164 it returns immediately.
166 bool mbIsActive;
169 /******************************************************************************
171 SlideShowImpl
173 This class encapsulates the slideshow presentation viewer.
175 With an instance of this class, it is possible to statically
176 and dynamically show a presentation, as defined by the
177 constructor-provided draw model (represented by a sequence
178 of css::drawing::XDrawPage objects).
180 It is possible to show the presentation on multiple views
181 simultaneously (e.g. for a multi-monitor setup). Since this
182 class also relies on user interaction, the corresponding
183 XSlideShowView interface provides means to register some UI
184 event listeners (mostly borrowed from awt::XWindow interface).
186 Since currently (mid 2004), OOo isn't very well suited to
187 multi-threaded rendering, this class relies on <em>very
188 frequent</em> external update() calls, which will render the
189 next frame of animations. This works as follows: after the
190 displaySlide() has been successfully called (which setup and
191 starts an actual slide show), the update() method must be
192 called until it returns false.
193 Effectively, this puts the burden of providing
194 concurrency to the clients of this class, which, as noted
195 above, is currently unavoidable with the current state of
196 affairs (I've actually tried threading here, but failed
197 miserably when using the VCL canvas as the render backend -
198 deadlocked).
200 ******************************************************************************/
202 typedef cppu::WeakComponentImplHelper<presentation::XSlideShow> SlideShowImplBase;
204 typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
206 /// Maps XDrawPage for annotations persistence
207 typedef ::std::map< css::uno::Reference<
208 css::drawing::XDrawPage>,
209 PolyPolygonVector> PolygonMap;
211 class SlideShowImpl : private cppu::BaseMutex,
212 public CursorManager,
213 public SlideShowImplBase
215 public:
216 explicit SlideShowImpl(
217 uno::Reference<uno::XComponentContext> const& xContext );
219 /** Notify that the transition phase of the current slide
220 has ended.
222 The life of a slide has three phases: the transition
223 phase, when the previous slide vanishes, and the
224 current slide becomes visible, the shape animation
225 phase, when shape effects are running, and the phase
226 after the last shape animation has ended, but before
227 the next slide transition starts.
229 This method notifies the end of the first phase.
231 @param bPaintSlide
232 When true, Slide::show() is passed a true as well, denoting
233 explicit paint of slide content. Pass false here, if e.g. a
234 slide transition has already rendered the initial slide image.
236 void notifySlideTransitionEnded( bool bPaintSlide );
238 /** Notify that the shape animation phase of the current slide
239 has ended.
241 The life of a slide has three phases: the transition
242 phase, when the previous slide vanishes, and the
243 current slide becomes visible, the shape animation
244 phase, when shape effects are running, and the phase
245 after the last shape animation has ended, but before
246 the next slide transition starts.
248 This method notifies the end of the second phase.
250 void notifySlideAnimationsEnded();
252 /** Notify that the slide has ended.
254 The life of a slide has three phases: the transition
255 phase, when the previous slide vanishes, and the
256 current slide becomes visible, the shape animation
257 phase, when shape effects are running, and the phase
258 after the last shape animation has ended, but before
259 the next slide transition starts.
261 This method notifies the end of the third phase.
263 void notifySlideEnded (const bool bReverse);
265 /** Notification from eventmultiplexer that a hyperlink
266 has been clicked.
268 bool notifyHyperLinkClicked( OUString const& hyperLink );
270 /** Notification from eventmultiplexer that an animation event has occoured.
271 This will be forewarded to all registered XSlideShowListener
273 bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
275 private:
276 // XSlideShow:
277 virtual sal_Bool SAL_CALL nextEffect() override;
278 virtual sal_Bool SAL_CALL previousEffect() override;
279 virtual sal_Bool SAL_CALL startShapeActivity(
280 uno::Reference<drawing::XShape> const& xShape ) override;
281 virtual sal_Bool SAL_CALL stopShapeActivity(
282 uno::Reference<drawing::XShape> const& xShape ) override;
283 virtual sal_Bool SAL_CALL pause( sal_Bool bPauseShow ) override;
284 virtual uno::Reference<drawing::XDrawPage> SAL_CALL getCurrentSlide() override;
285 virtual void SAL_CALL displaySlide(
286 uno::Reference<drawing::XDrawPage> const& xSlide,
287 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
288 uno::Reference<animations::XAnimationNode> const& xRootNode,
289 uno::Sequence<beans::PropertyValue> const& rProperties ) override;
290 virtual void SAL_CALL registerUserPaintPolygons( const css::uno::Reference< css::lang::XMultiServiceFactory >& xDocFactory ) override;
291 virtual sal_Bool SAL_CALL setProperty(
292 beans::PropertyValue const& rProperty ) override;
293 virtual sal_Bool SAL_CALL addView(
294 uno::Reference<presentation::XSlideShowView> const& xView ) override;
295 virtual sal_Bool SAL_CALL removeView(
296 uno::Reference<presentation::XSlideShowView> const& xView ) override;
297 virtual sal_Bool SAL_CALL update( double & nNextTimeout ) override;
298 virtual void SAL_CALL addSlideShowListener(
299 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
300 virtual void SAL_CALL removeSlideShowListener(
301 uno::Reference<presentation::XSlideShowListener> const& xListener ) override;
302 virtual void SAL_CALL addShapeEventListener(
303 uno::Reference<presentation::XShapeEventListener> const& xListener,
304 uno::Reference<drawing::XShape> const& xShape ) override;
305 virtual void SAL_CALL removeShapeEventListener(
306 uno::Reference<presentation::XShapeEventListener> const& xListener,
307 uno::Reference<drawing::XShape> const& xShape ) override;
308 virtual void SAL_CALL setShapeCursor(
309 uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape ) override;
311 // CursorManager
314 virtual bool requestCursor( sal_Int16 nCursorShape ) override;
315 virtual void resetCursor() override;
317 /** This is somewhat similar to displaySlide when called for the current
318 slide. It has been simplified to take advantage of that no slide
319 change takes place. Furthermore it does not show the slide
320 transition.
322 void redisplayCurrentSlide();
324 protected:
325 // WeakComponentImplHelperBase
326 virtual void SAL_CALL disposing() override;
328 bool isDisposed() const
330 return (rBHelper.bDisposed || rBHelper.bInDispose);
333 private:
334 struct SeparateListenerImpl; friend struct SeparateListenerImpl;
335 class PrefetchPropertiesFunc; friend class PrefetchPropertiesFunc;
337 /// Stop currently running show.
338 void stopShow();
340 ///Find a polygons vector in maPolygons (map)
341 PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
343 /// Creates a new slide.
344 SlideSharedPtr makeSlide(
345 uno::Reference<drawing::XDrawPage> const& xDrawPage,
346 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
347 uno::Reference<animations::XAnimationNode> const& xRootNode );
349 /// Checks whether the given slide/animation node matches mpPrefetchSlide
350 static bool matches(
351 SlideSharedPtr const& pSlide,
352 uno::Reference<drawing::XDrawPage> const& xSlide,
353 uno::Reference<animations::XAnimationNode> const& xNode )
355 if (pSlide)
356 return (pSlide->getXDrawPage() == xSlide &&
357 pSlide->getXAnimationNode() == xNode);
358 else
359 return (!xSlide.is() && !xNode.is());
362 /// Resets the current slide transition sound object with a new one:
363 SoundPlayerSharedPtr resetSlideTransitionSound(
364 uno::Any const& url, bool bLoopSound );
366 /// stops the current slide transition sound
367 void stopSlideTransitionSound();
369 /** Prepare a slide transition
371 This method registers all necessary events and
372 activities for a slide transition.
374 @return the slide change activity, or NULL for no transition effect
376 ActivitySharedPtr createSlideTransition(
377 const uno::Reference< drawing::XDrawPage >& xDrawPage,
378 const SlideSharedPtr& rLeavingSlide,
379 const SlideSharedPtr& rEnteringSlide,
380 const EventSharedPtr& rTransitionEndEvent );
382 /** Request/release the wait symbol. The wait symbol is displayed when
383 there are more requests then releases. Locking the wait symbol
384 helps to avoid intermediate repaints.
386 Do not call this method directly. Use WaitSymbolLock instead.
388 void requestWaitSymbol();
389 void releaseWaitSymbol();
391 class WaitSymbolLock {public:
392 explicit WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
393 { mrSlideShowImpl.requestWaitSymbol(); }
394 ~WaitSymbolLock()
395 { mrSlideShowImpl.releaseWaitSymbol(); }
396 private: SlideShowImpl& mrSlideShowImpl;
399 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
400 sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
402 /** This method is called asynchronously to finish the rewinding of an
403 effect to the previous slide that was initiated earlier.
405 void rewindEffectToPreviousSlide();
407 /// all registered views
408 UnoViewContainer maViewContainer;
410 /// all registered slide show listeners
411 comphelper::OInterfaceContainerHelper2 maListenerContainer;
413 /// map of vectors, containing all registered listeners for a shape
414 ShapeEventListenerMap maShapeEventListeners;
415 /// map of sal_Int16 values, specifying the mouse cursor for every shape
416 ShapeCursorMap maShapeCursors;
418 //map of vector of Polygons, containing polygons drawn on each slide.
419 PolygonMap maPolygons;
421 boost::optional<RGBColor> maUserPaintColor;
423 double maUserPaintStrokeWidth;
425 //changed for the eraser project
426 boost::optional<bool> maEraseAllInk;
427 boost::optional<bool> maSwitchPenMode;
428 boost::optional<bool> maSwitchEraserMode;
429 boost::optional<sal_Int32> maEraseInk;
430 //end changed
432 std::shared_ptr<canvas::tools::ElapsedTime> mpPresTimer;
433 ScreenUpdater maScreenUpdater;
434 EventQueue maEventQueue;
435 EventMultiplexer maEventMultiplexer;
436 ActivitiesQueue maActivitiesQueue;
437 UserEventQueue maUserEventQueue;
438 SubsettableShapeManagerSharedPtr mpDummyPtr;
440 std::shared_ptr<SeparateListenerImpl> mpListener;
442 std::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
443 std::shared_ptr<WaitSymbol> mpWaitSymbol;
445 std::shared_ptr<PointerSymbol> mpPointerSymbol;
447 /// the current slide transition sound object:
448 SoundPlayerSharedPtr mpCurrentSlideTransitionSound;
450 uno::Reference<uno::XComponentContext> mxComponentContext;
451 uno::Reference<
452 presentation::XTransitionFactory> mxOptionalTransitionFactory;
454 /// the previously running slide
455 SlideSharedPtr mpPreviousSlide;
456 /// the currently running slide
457 SlideSharedPtr mpCurrentSlide;
458 /// the already prefetched slide: best candidate for upcoming slide
459 SlideSharedPtr mpPrefetchSlide;
460 /// slide to be prefetched: best candidate for upcoming slide
461 uno::Reference<drawing::XDrawPage> mxPrefetchSlide;
462 /// save the XDrawPagesSupplier to retrieve polygons
463 uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier;
464 /// slide animation to be prefetched:
465 uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
467 sal_Int16 mnCurrentCursor;
469 sal_Int32 mnWaitSymbolRequestCount;
470 bool mbAutomaticAdvancementMode;
471 bool mbImageAnimationsAllowed;
472 bool mbNoSlideTransitions;
473 bool mbMouseVisible;
474 bool mbForceManualAdvance;
475 bool mbShowPaused;
476 bool mbSlideShowIdle;
477 bool mbDisableAnimationZOrder;
479 EffectRewinder maEffectRewinder;
480 FrameSynchronization maFrameSynchronization;
483 /** Separate event listener for animation, view and hyperlink events.
485 This handler is registered for slide animation end, view and
486 hyperlink events at the global EventMultiplexer, and forwards
487 notifications to the SlideShowImpl
489 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
490 public ViewRepaintHandler,
491 public HyperlinkHandler,
492 public AnimationEventHandler
494 SlideShowImpl& mrShow;
495 ScreenUpdater& mrScreenUpdater;
496 EventQueue& mrEventQueue;
498 SeparateListenerImpl( SlideShowImpl& rShow,
499 ScreenUpdater& rScreenUpdater,
500 EventQueue& rEventQueue ) :
501 mrShow( rShow ),
502 mrScreenUpdater( rScreenUpdater ),
503 mrEventQueue( rEventQueue )
506 SeparateListenerImpl( const SeparateListenerImpl& ) = delete;
507 SeparateListenerImpl& operator=( const SeparateListenerImpl& ) = delete;
509 // EventHandler
510 virtual bool handleEvent() override
512 // DON't call notifySlideAnimationsEnded()
513 // directly, but queue an event. handleEvent()
514 // might be called from e.g.
515 // showNext(), and notifySlideAnimationsEnded() must not be called
516 // in recursion. Note that the event is scheduled for the next
517 // frame so that its expensive execution does not come in between
518 // sprite hiding and shape redraw (at the end of the animation of a
519 // shape), which would cause a flicker.
520 mrEventQueue.addEventForNextRound(
521 makeEvent( [this] () { this->mrShow.notifySlideAnimationsEnded(); },
522 "SlideShowImpl::notifySlideAnimationsEnded"));
523 return true;
526 // ViewRepaintHandler
527 virtual void viewClobbered( const UnoViewSharedPtr& rView ) override
529 // given view needs repaint, request update
530 mrScreenUpdater.notifyUpdate(rView, true);
533 // HyperlinkHandler
534 virtual bool handleHyperlink( OUString const& rLink ) override
536 return mrShow.notifyHyperLinkClicked(rLink);
539 // AnimationEventHandler
540 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode ) override
542 return mrShow.handleAnimationEvent(rNode);
546 SlideShowImpl::SlideShowImpl(
547 uno::Reference<uno::XComponentContext> const& xContext )
548 : SlideShowImplBase(m_aMutex),
549 maViewContainer(),
550 maListenerContainer( m_aMutex ),
551 maShapeEventListeners(),
552 maShapeCursors(),
553 maUserPaintColor(),
554 maUserPaintStrokeWidth(4.0),
555 mpPresTimer( new canvas::tools::ElapsedTime ),
556 maScreenUpdater(maViewContainer),
557 maEventQueue( mpPresTimer ),
558 maEventMultiplexer( maEventQueue,
559 maViewContainer ),
560 maActivitiesQueue( mpPresTimer ),
561 maUserEventQueue( maEventMultiplexer,
562 maEventQueue,
563 *this ),
564 mpDummyPtr(),
565 mpListener(),
566 mpRehearseTimingsActivity(),
567 mpWaitSymbol(),
568 mpPointerSymbol(),
569 mpCurrentSlideTransitionSound(),
570 mxComponentContext( xContext ),
571 mxOptionalTransitionFactory(),
572 mpCurrentSlide(),
573 mpPrefetchSlide(),
574 mxPrefetchSlide(),
575 mxDrawPagesSupplier(),
576 mxPrefetchAnimationNode(),
577 mnCurrentCursor(awt::SystemPointer::ARROW),
578 mnWaitSymbolRequestCount(0),
579 mbAutomaticAdvancementMode(false),
580 mbImageAnimationsAllowed( true ),
581 mbNoSlideTransitions( false ),
582 mbMouseVisible( true ),
583 mbForceManualAdvance( false ),
584 mbShowPaused( false ),
585 mbSlideShowIdle( true ),
586 mbDisableAnimationZOrder( false ),
587 maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue),
588 maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond)
591 // keep care not constructing any UNO references to this inside ctor,
592 // shift that code to create()!
594 uno::Reference<lang::XMultiComponentFactory> xFactory(
595 mxComponentContext->getServiceManager() );
597 if( xFactory.is() )
601 // #i82460# try to retrieve special transition factory
602 mxOptionalTransitionFactory.set(
603 xFactory->createInstanceWithContext(
604 "com.sun.star.presentation.TransitionFactory",
605 mxComponentContext ),
606 uno::UNO_QUERY );
608 catch (loader::CannotActivateFactoryException const&)
613 mpListener.reset( new SeparateListenerImpl(
614 *this,
615 maScreenUpdater,
616 maEventQueue ));
617 maEventMultiplexer.addSlideAnimationsEndHandler( mpListener );
618 maEventMultiplexer.addViewRepaintHandler( mpListener );
619 maEventMultiplexer.addHyperlinkHandler( mpListener, 0.0 );
620 maEventMultiplexer.addAnimationStartHandler( mpListener );
621 maEventMultiplexer.addAnimationEndHandler( mpListener );
624 // we are about to be disposed (someone call dispose() on us)
625 void SlideShowImpl::disposing()
627 osl::MutexGuard const guard( m_aMutex );
629 maEffectRewinder.dispose();
631 // stop slide transition sound, if any:
632 stopSlideTransitionSound();
634 mxComponentContext.clear();
636 if( mpCurrentSlideTransitionSound )
638 mpCurrentSlideTransitionSound->dispose();
639 mpCurrentSlideTransitionSound.reset();
642 mpWaitSymbol.reset();
643 mpPointerSymbol.reset();
645 if( mpRehearseTimingsActivity )
647 mpRehearseTimingsActivity->dispose();
648 mpRehearseTimingsActivity.reset();
651 if( mpListener )
653 maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
654 maEventMultiplexer.removeViewRepaintHandler(mpListener);
655 maEventMultiplexer.removeHyperlinkHandler(mpListener);
656 maEventMultiplexer.removeAnimationStartHandler( mpListener );
657 maEventMultiplexer.removeAnimationEndHandler( mpListener );
659 mpListener.reset();
662 maUserEventQueue.clear();
663 maActivitiesQueue.clear();
664 maEventMultiplexer.clear();
665 maEventQueue.clear();
666 mpPresTimer.reset();
667 maShapeCursors.clear();
668 maShapeEventListeners.clear();
670 // send all listeners a disposing() that we are going down:
671 maListenerContainer.disposeAndClear(
672 lang::EventObject( static_cast<cppu::OWeakObject *>(this) ) );
674 maViewContainer.dispose();
676 // release slides:
677 mxPrefetchAnimationNode.clear();
678 mxPrefetchSlide.clear();
679 mpPrefetchSlide.reset();
680 mpCurrentSlide.reset();
681 mpPreviousSlide.reset();
684 /// stops the current slide transition sound
685 void SlideShowImpl::stopSlideTransitionSound()
687 if (mpCurrentSlideTransitionSound)
689 mpCurrentSlideTransitionSound->stopPlayback();
690 mpCurrentSlideTransitionSound->dispose();
691 mpCurrentSlideTransitionSound.reset();
695 SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
697 bool bStopSound = false;
698 OUString url;
700 if( !(rSound >>= bStopSound) )
701 bStopSound = false;
702 rSound >>= url;
704 if( !bStopSound && url.isEmpty() )
705 return SoundPlayerSharedPtr();
707 stopSlideTransitionSound();
709 if (!url.isEmpty())
713 mpCurrentSlideTransitionSound = SoundPlayer::create(
714 maEventMultiplexer, url, mxComponentContext );
715 mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
717 catch (lang::NoSupportException const&)
719 // catch possible exceptions from SoundPlayer, since
720 // being not able to playback the sound is not a hard
721 // error here (still, the slide transition should be
722 // shown).
725 return mpCurrentSlideTransitionSound;
728 ActivitySharedPtr SlideShowImpl::createSlideTransition(
729 const uno::Reference< drawing::XDrawPage >& xDrawPage,
730 const SlideSharedPtr& rLeavingSlide,
731 const SlideSharedPtr& rEnteringSlide,
732 const EventSharedPtr& rTransitionEndEvent)
734 ENSURE_OR_THROW( !maViewContainer.empty(),
735 "createSlideTransition(): No views" );
736 ENSURE_OR_THROW( rEnteringSlide,
737 "createSlideTransition(): No entering slide" );
739 // return empty transition, if slide transitions
740 // are disabled.
741 if (mbNoSlideTransitions)
742 return ActivitySharedPtr();
744 // retrieve slide change parameters from XDrawPage
745 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
746 uno::UNO_QUERY );
748 if( !xPropSet.is() )
750 SAL_INFO("slideshow", "createSlideTransition(): "
751 "Slide has no PropertySet - assuming no transition" );
752 return ActivitySharedPtr();
755 sal_Int16 nTransitionType(0);
756 if( !getPropertyValue( nTransitionType,
757 xPropSet,
758 "TransitionType") )
760 SAL_INFO("slideshow", "createSlideTransition(): "
761 "Could not extract slide transition type from XDrawPage - assuming no transition" );
762 return ActivitySharedPtr();
765 sal_Int16 nTransitionSubType(0);
766 if( !getPropertyValue( nTransitionSubType,
767 xPropSet,
768 "TransitionSubtype") )
770 SAL_INFO("slideshow", "createSlideTransition(): "
771 "Could not extract slide transition subtype from XDrawPage - assuming no transition" );
772 return ActivitySharedPtr();
775 bool bTransitionDirection(false);
776 if( !getPropertyValue( bTransitionDirection,
777 xPropSet,
778 "TransitionDirection") )
780 SAL_INFO("slideshow", "createSlideTransition(): "
781 "Could not extract slide transition direction from XDrawPage - assuming default direction" );
784 sal_Int32 aUnoColor(0);
785 if( !getPropertyValue( aUnoColor,
786 xPropSet,
787 "TransitionFadeColor") )
789 SAL_INFO("slideshow", "createSlideTransition(): "
790 "Could not extract slide transition fade color from XDrawPage - assuming black" );
793 const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
795 uno::Any aSound;
796 bool bLoopSound = false;
798 if( !getPropertyValue( aSound, xPropSet, "Sound") )
799 SAL_INFO("slideshow", "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound" );
801 if( !getPropertyValue( bLoopSound, xPropSet, "LoopSound" ) )
802 SAL_INFO("slideshow", "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound" );
804 NumberAnimationSharedPtr pTransition(
805 TransitionFactory::createSlideTransition(
806 rLeavingSlide,
807 rEnteringSlide,
808 maViewContainer,
809 maScreenUpdater,
810 maEventMultiplexer,
811 mxOptionalTransitionFactory,
812 nTransitionType,
813 nTransitionSubType,
814 bTransitionDirection,
815 aTransitionFadeColor,
816 resetSlideTransitionSound( aSound, bLoopSound ) ));
818 if( !pTransition )
819 return ActivitySharedPtr(); // no transition effect has been
820 // generated. Normally, that means
821 // that simply no transition is
822 // set on this slide.
824 double nTransitionDuration(0.0);
825 if( !getPropertyValue( nTransitionDuration,
826 xPropSet,
827 "TransitionDuration") )
829 SAL_INFO("slideshow", "createSlideTransition(): "
830 "Could not extract slide transition duration from XDrawPage - assuming no transition" );
831 return ActivitySharedPtr();
834 sal_Int32 nMinFrames(5);
835 if( !getPropertyValue( nMinFrames,
836 xPropSet,
837 "MinimalFrameNumber") )
839 SAL_INFO("slideshow", "createSlideTransition(): "
840 "No minimal number of frames given - assuming 5" );
843 // prefetch slide transition bitmaps, but postpone it after
844 // displaySlide() has finished - sometimes, view size has not yet
845 // reached final size
846 maEventQueue.addEvent(
847 makeEvent( [pTransition] () {
848 pTransition->prefetch(
849 AnimatableShapeSharedPtr(),
850 ShapeAttributeLayerSharedPtr()); },
851 "Animation::prefetch"));
853 return ActivitySharedPtr(
854 ActivitiesFactory::createSimpleActivity(
855 ActivitiesFactory::CommonParameters(
856 rTransitionEndEvent,
857 maEventQueue,
858 maActivitiesQueue,
859 nTransitionDuration,
860 nMinFrames,
861 false,
862 boost::optional<double>(1.0),
863 0.0,
864 0.0,
865 ShapeSharedPtr(),
866 basegfx::B2DSize( rEnteringSlide->getSlideSize() ) ),
867 pTransition,
868 true ));
871 PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
873 // TODO(P2): optimize research in the map.
874 PolygonMap::iterator aEnd = maPolygons.end();
875 for( PolygonMap::iterator aIter = maPolygons.begin();
876 aIter != aEnd;
877 ++aIter )
878 if( aIter->first == xDrawPage )
879 return aIter;
881 return aEnd;
884 SlideSharedPtr SlideShowImpl::makeSlide(
885 uno::Reference<drawing::XDrawPage> const& xDrawPage,
886 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
887 uno::Reference<animations::XAnimationNode> const& xRootNode )
889 if( !xDrawPage.is() )
890 return SlideSharedPtr();
892 //Retrieve polygons for the current slide
893 PolygonMap::iterator aIter;
894 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 maViewContainer,
906 mxComponentContext,
907 maShapeEventListeners,
908 maShapeCursors,
909 (aIter != maPolygons.end()) ? aIter->second : PolyPolygonVector(),
910 maUserPaintColor ? *maUserPaintColor : RGBColor(),
911 maUserPaintStrokeWidth,
912 !!maUserPaintColor,
913 mbImageAnimationsAllowed,
914 mbDisableAnimationZOrder) );
916 // prefetch show content (reducing latency for slide
917 // bitmap and effect start later on)
918 pSlide->prefetch();
920 return pSlide;
923 void SlideShowImpl::requestWaitSymbol()
925 ++mnWaitSymbolRequestCount;
926 OSL_ASSERT(mnWaitSymbolRequestCount>0);
928 if (mnWaitSymbolRequestCount == 1)
930 if( !mpWaitSymbol )
932 // fall back to cursor
933 requestCursor(calcActiveCursor(mnCurrentCursor));
935 else
936 mpWaitSymbol->show();
940 void SlideShowImpl::releaseWaitSymbol()
942 --mnWaitSymbolRequestCount;
943 OSL_ASSERT(mnWaitSymbolRequestCount>=0);
945 if (mnWaitSymbolRequestCount == 0)
947 if( !mpWaitSymbol )
949 // fall back to cursor
950 requestCursor(calcActiveCursor(mnCurrentCursor));
952 else
953 mpWaitSymbol->hide();
957 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
959 if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
960 nCursorShape = awt::SystemPointer::WAIT;
961 else if( !mbMouseVisible ) // enforce INVISIBLE
962 nCursorShape = awt::SystemPointer::INVISIBLE;
963 else if( maUserPaintColor &&
964 nCursorShape == awt::SystemPointer::ARROW )
965 nCursorShape = awt::SystemPointer::PEN;
967 return nCursorShape;
970 void SlideShowImpl::stopShow()
972 // Force-end running animation
973 // ===========================
974 if (mpCurrentSlide)
976 mpCurrentSlide->hide();
977 //Register polygons in the map
978 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
979 maPolygons.erase(mpCurrentSlide->getXDrawPage());
981 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
984 // clear all queues
985 maEventQueue.clear();
986 maActivitiesQueue.clear();
988 // Attention: we MUST clear the user event queue here,
989 // this is because the current slide might have registered
990 // shape events (click or enter/leave), which might
991 // otherwise dangle forever in the queue (because of the
992 // shared ptr nature). If someone needs to change this:
993 // somehow unregister those shapes at the user event queue
994 // on notifySlideEnded().
995 maUserEventQueue.clear();
997 // re-enable automatic effect advancement
998 // (maEventQueue.clear() above might have killed
999 // maEventMultiplexer's tick events)
1000 if (mbAutomaticAdvancementMode)
1002 // toggle automatic mode (enabling just again is
1003 // ignored by EventMultiplexer)
1004 maEventMultiplexer.setAutomaticMode( false );
1005 maEventMultiplexer.setAutomaticMode( true );
1009 class SlideShowImpl::PrefetchPropertiesFunc
1011 public:
1012 PrefetchPropertiesFunc( SlideShowImpl * that_,
1013 bool& rbSkipAllMainSequenceEffects,
1014 bool& rbSkipSlideTransition)
1015 : mpSlideShowImpl(that_),
1016 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1017 mrbSkipSlideTransition(rbSkipSlideTransition)
1020 void operator()( beans::PropertyValue const& rProperty ) const {
1021 if (rProperty.Name == "Prefetch" )
1023 uno::Sequence<uno::Any> seq;
1024 if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1026 seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1027 seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1030 else if ( rProperty.Name == "SkipAllMainSequenceEffects" )
1032 rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1034 else if ( rProperty.Name == "SkipSlideTransition" )
1036 rProperty.Value >>= mrbSkipSlideTransition;
1038 else
1040 SAL_WARN( "slideshow", rProperty.Name );
1043 private:
1044 SlideShowImpl *const mpSlideShowImpl;
1045 bool& mrbSkipAllMainSequenceEffects;
1046 bool& mrbSkipSlideTransition;
1049 void SlideShowImpl::displaySlide(
1050 uno::Reference<drawing::XDrawPage> const& xSlide,
1051 uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
1052 uno::Reference<animations::XAnimationNode> const& xRootNode,
1053 uno::Sequence<beans::PropertyValue> const& rProperties )
1055 osl::MutexGuard const guard( m_aMutex );
1057 if (isDisposed())
1058 return;
1060 maEffectRewinder.setRootAnimationNode(xRootNode);
1062 // precondition: must only be called from the main thread!
1063 DBG_TESTSOLARMUTEX();
1065 mxDrawPagesSupplier = xDrawPages;
1067 stopShow(); // MUST call that: results in
1068 // maUserEventQueue.clear(). What's more,
1069 // stopShow()'s currSlide->hide() call is
1070 // now also required, notifySlideEnded()
1071 // relies on that
1072 // unconditionally. Otherwise, genuine
1073 // shape animations (drawing layer and
1074 // GIF) will not be stopped.
1076 bool bSkipAllMainSequenceEffects (false);
1077 bool bSkipSlideTransition (false);
1078 std::for_each( rProperties.begin(),
1079 rProperties.end(),
1080 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1082 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1083 if (maViewContainer.empty())
1084 return;
1086 // this here might take some time
1088 WaitSymbolLock aLock (*this);
1090 mpPreviousSlide = mpCurrentSlide;
1091 mpCurrentSlide.reset();
1093 if (matches( mpPrefetchSlide, xSlide, xRootNode ))
1095 // prefetched slide matches:
1096 mpCurrentSlide = mpPrefetchSlide;
1098 else
1099 mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
1101 OSL_ASSERT( mpCurrentSlide );
1102 if (mpCurrentSlide)
1104 basegfx::B2DSize oldSlideSize;
1105 if( mpPreviousSlide )
1106 oldSlideSize = basegfx::B2DSize( mpPreviousSlide->getSlideSize() );
1108 basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
1110 // push new transformation to all views, if size changed
1111 if( !mpPreviousSlide || oldSlideSize != slideSize )
1113 for( const auto& pView : maViewContainer )
1114 pView->setViewSize( slideSize );
1116 // explicitly notify view change here,
1117 // because transformation might have changed:
1118 // optimization, this->notifyViewChange() would
1119 // repaint slide which is not necessary.
1120 maEventMultiplexer.notifyViewsChanged();
1123 // create slide transition, and add proper end event
1124 // (which then starts the slide effects
1125 // via CURRENT_SLIDE.show())
1126 ActivitySharedPtr pSlideChangeActivity (
1127 createSlideTransition(
1128 mpCurrentSlide->getXDrawPage(),
1129 mpPreviousSlide,
1130 mpCurrentSlide,
1131 makeEvent(
1132 [this] () { this->notifySlideTransitionEnded(false); },
1133 "SlideShowImpl::notifySlideTransitionEnded")));
1135 if (bSkipSlideTransition)
1137 // The transition activity was created for the side effects
1138 // (like sound transitions). Because we want to skip the
1139 // actual transition animation we do not need the activity
1140 // anymore.
1141 pSlideChangeActivity.reset();
1144 if (pSlideChangeActivity)
1146 // factory generated a slide transition - activate it!
1147 maActivitiesQueue.addActivity( pSlideChangeActivity );
1149 else
1151 // no transition effect on this slide - schedule slide
1152 // effect start event right away.
1153 maEventQueue.addEvent(
1154 makeEvent(
1155 [this] () { this->notifySlideTransitionEnded(true); },
1156 "SlideShowImpl::notifySlideTransitionEnded"));
1159 } // finally
1161 maListenerContainer.forEach<presentation::XSlideShowListener>(
1162 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1164 xListener->slideTransitionStarted();
1167 // We are currently rewinding an effect. This lead us from the next
1168 // slide to this one. To complete this we have to play back all main
1169 // sequence effects on this slide.
1170 if (bSkipAllMainSequenceEffects)
1171 maEffectRewinder.skipAllMainSequenceEffects();
1174 void SlideShowImpl::redisplayCurrentSlide()
1176 osl::MutexGuard const guard( m_aMutex );
1178 if (isDisposed())
1179 return;
1181 // precondition: must only be called from the main thread!
1182 DBG_TESTSOLARMUTEX();
1183 stopShow();
1185 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1186 if (maViewContainer.empty())
1187 return;
1189 // No transition effect on this slide - schedule slide
1190 // effect start event right away.
1191 maEventQueue.addEvent(
1192 makeEvent( [this] () { this->notifySlideTransitionEnded(true); },
1193 "SlideShowImpl::notifySlideTransitionEnded"));
1195 maListenerContainer.forEach<presentation::XSlideShowListener>(
1196 [](uno::Reference<presentation::XSlideShowListener> const& xListener)
1198 xListener->slideTransitionStarted();
1202 sal_Bool SlideShowImpl::nextEffect()
1204 osl::MutexGuard const guard( m_aMutex );
1206 if (isDisposed())
1207 return false;
1209 // precondition: must only be called from the main thread!
1210 DBG_TESTSOLARMUTEX();
1212 if (mbShowPaused)
1213 return true;
1214 else
1215 return maEventMultiplexer.notifyNextEffect();
1218 sal_Bool SlideShowImpl::previousEffect()
1220 osl::MutexGuard const guard( m_aMutex );
1222 if (isDisposed())
1223 return false;
1225 // precondition: must only be called from the main thread!
1226 DBG_TESTSOLARMUTEX();
1228 if (mbShowPaused)
1229 return true;
1230 else
1232 return maEffectRewinder.rewind(
1233 maScreenUpdater.createLock(),
1234 [this]() { return this->redisplayCurrentSlide(); },
1235 [this]() { return this->rewindEffectToPreviousSlide(); } );
1239 void SlideShowImpl::rewindEffectToPreviousSlide()
1241 // Show the wait symbol now and prevent it from showing temporary slide
1242 // content while effects are played back.
1243 WaitSymbolLock aLock (*this);
1245 // A previous call to EffectRewinder::Rewind could not rewind the current
1246 // effect because there are no effects on the current slide or none has
1247 // yet been displayed. Go to the previous slide.
1248 notifySlideEnded(true);
1250 // Process pending events once more in order to have the following
1251 // screen update show the last effect. Not sure whether this should be
1252 // necessary.
1253 maEventQueue.forceEmpty();
1255 // We have to call the screen updater before the wait symbol is turned
1256 // off. Otherwise the wait symbol would force the display of an
1257 // intermediate state of the slide (before the effects are replayed.)
1258 maScreenUpdater.commitUpdates();
1261 sal_Bool SlideShowImpl::startShapeActivity(
1262 uno::Reference<drawing::XShape> const& /*xShape*/ )
1264 osl::MutexGuard const guard( m_aMutex );
1266 // precondition: must only be called from the main thread!
1267 DBG_TESTSOLARMUTEX();
1269 // TODO(F3): NYI
1270 OSL_FAIL( "not yet implemented!" );
1271 return false;
1274 sal_Bool SlideShowImpl::stopShapeActivity(
1275 uno::Reference<drawing::XShape> const& /*xShape*/ )
1277 osl::MutexGuard const guard( m_aMutex );
1279 // precondition: must only be called from the main thread!
1280 DBG_TESTSOLARMUTEX();
1282 // TODO(F3): NYI
1283 OSL_FAIL( "not yet implemented!" );
1284 return false;
1287 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1289 osl::MutexGuard const guard( m_aMutex );
1291 if (isDisposed())
1292 return false;
1294 // precondition: must only be called from the main thread!
1295 DBG_TESTSOLARMUTEX();
1297 if (bPauseShow)
1298 mpPresTimer->pauseTimer();
1299 else
1300 mpPresTimer->continueTimer();
1302 maEventMultiplexer.notifyPauseMode(bPauseShow);
1304 mbShowPaused = bPauseShow;
1305 return true;
1308 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1310 osl::MutexGuard const guard( m_aMutex );
1312 if (isDisposed())
1313 return uno::Reference<drawing::XDrawPage>();
1315 // precondition: must only be called from the main thread!
1316 DBG_TESTSOLARMUTEX();
1318 if (mpCurrentSlide)
1319 return mpCurrentSlide->getXDrawPage();
1320 else
1321 return uno::Reference<drawing::XDrawPage>();
1324 sal_Bool SlideShowImpl::addView(
1325 uno::Reference<presentation::XSlideShowView> const& xView )
1327 osl::MutexGuard const guard( m_aMutex );
1329 if (isDisposed())
1330 return false;
1332 // precondition: must only be called from the main thread!
1333 DBG_TESTSOLARMUTEX();
1335 // first of all, check if view has a valid canvas
1336 ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
1337 ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(),
1338 "addView(): View does not provide a valid canvas" );
1340 UnoViewSharedPtr const pView( createSlideView(
1341 xView,
1342 maEventQueue,
1343 maEventMultiplexer ));
1344 if (!maViewContainer.addView( pView ))
1345 return false; // view already added
1347 // initialize view content
1348 // =======================
1350 if (mpCurrentSlide)
1352 // set view transformation
1353 const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
1354 pView->setViewSize( basegfx::B2DSize( slideSize.getX(),
1355 slideSize.getY() ) );
1358 // clear view area (since its newly added,
1359 // we need a clean slate)
1360 pView->clearAll();
1362 // broadcast newly added view
1363 maEventMultiplexer.notifyViewAdded( pView );
1365 // set current mouse ptr
1366 pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1368 return true;
1371 sal_Bool SlideShowImpl::removeView(
1372 uno::Reference<presentation::XSlideShowView> const& xView )
1374 osl::MutexGuard const guard( m_aMutex );
1376 // precondition: must only be called from the main thread!
1377 DBG_TESTSOLARMUTEX();
1379 ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
1381 UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
1382 if( !pView )
1383 return false; // view was not added in the first place
1385 // remove view from EventMultiplexer (mouse events etc.)
1386 maEventMultiplexer.notifyViewRemoved( pView );
1388 pView->_dispose();
1390 return true;
1393 void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory )
1395 //Retrieve Polygons if user ends presentation by context menu
1396 if (mpCurrentSlide)
1398 if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1399 maPolygons.erase(mpCurrentSlide->getXDrawPage());
1401 maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1404 //Creating the layer for shapes
1405 // query for the XLayerManager
1406 uno::Reference< drawing::XLayerSupplier > xLayerSupplier(xDocFactory, uno::UNO_QUERY);
1407 uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager();
1409 uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY);
1410 // create a layer and set its properties
1411 uno::Reference< drawing::XLayer > xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
1412 uno::Reference< beans::XPropertySet > xLayerPropSet(xDrawnInSlideshow, uno::UNO_QUERY);
1414 //Layer Name which enables to catch annotations
1415 OUString layerName = "DrawnInSlideshow";
1416 uno::Any aPropLayer;
1418 aPropLayer <<= layerName;
1419 xLayerPropSet->setPropertyValue("Name", aPropLayer);
1421 aPropLayer <<= true;
1422 xLayerPropSet->setPropertyValue("IsVisible", aPropLayer);
1424 aPropLayer <<= false;
1425 xLayerPropSet->setPropertyValue("IsLocked", aPropLayer);
1427 //Register polygons for each slide
1428 for( const auto& rPoly : maPolygons )
1430 PolyPolygonVector aPolygons = rPoly.second;
1431 //Get shapes for the slide
1432 css::uno::Reference< css::drawing::XShapes > Shapes(rPoly.first, css::uno::UNO_QUERY);
1433 //Retrieve polygons for one slide
1434 for( const auto& pPolyPoly : aPolygons )
1436 ::basegfx::B2DPolyPolygon b2DPolyPoly = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(pPolyPoly->getUNOPolyPolygon());
1438 //Normally there is only one polygon
1439 for(sal_uInt32 i=0; i< b2DPolyPoly.count();i++)
1441 const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
1442 sal_uInt32 nPoints = aPoly.count();
1444 if( nPoints > 1)
1446 //create the PolyLineShape
1447 uno::Reference< uno::XInterface > polyshape(xDocFactory->createInstance(
1448 "com.sun.star.drawing.PolyLineShape" ) );
1449 uno::Reference< drawing::XShape > rPolyShape(polyshape, uno::UNO_QUERY);
1451 //Add the shape to the slide
1452 Shapes->add(rPolyShape);
1454 //Retrieve shape properties
1455 uno::Reference< beans::XPropertySet > aXPropSet( rPolyShape, uno::UNO_QUERY );
1456 //Construct a sequence of points sequence
1457 drawing::PointSequenceSequence aRetval;
1458 //Create only one sequence for one polygon
1459 aRetval.realloc( 1 );
1460 // Retrieve the sequence of points from aRetval
1461 drawing::PointSequence* pOuterSequence = aRetval.getArray();
1462 // Create 2 points in this sequence
1463 pOuterSequence->realloc(nPoints);
1464 // Get these points which are in an array
1465 awt::Point* pInnerSequence = pOuterSequence->getArray();
1466 for( sal_uInt32 n = 0; n < nPoints; n++ )
1468 //Create a point from the polygon
1469 *pInnerSequence++ = awt::Point(
1470 basegfx::fround(aPoly.getB2DPoint(n).getX()),
1471 basegfx::fround(aPoly.getB2DPoint(n).getY()));
1474 //Fill the properties
1475 //Give the built PointSequenceSequence.
1476 uno::Any aParam;
1477 aParam <<= aRetval;
1478 aXPropSet->setPropertyValue("PolyPolygon", aParam );
1480 //LineStyle : SOLID by default
1481 drawing::LineStyle eLS;
1482 eLS = drawing::LineStyle_SOLID;
1483 aXPropSet->setPropertyValue("LineStyle", uno::Any(eLS) );
1485 //LineColor
1486 sal_uInt32 nLineColor;
1487 nLineColor = pPolyPoly->getRGBALineColor();
1488 //Transform polygon color from RRGGBBAA to AARRGGBB
1489 aXPropSet->setPropertyValue("LineColor", uno::Any(RGBAColor2UnoColor(nLineColor)) );
1491 //LineWidth
1492 double fLineWidth;
1493 fLineWidth = pPolyPoly->getStrokeWidth();
1494 aXPropSet->setPropertyValue("LineWidth", uno::Any((sal_Int32)fLineWidth) );
1496 // make polygons special
1497 xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1504 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1506 osl::MutexGuard const guard( m_aMutex );
1508 if (isDisposed())
1509 return false;
1511 // precondition: must only be called from the main thread!
1512 DBG_TESTSOLARMUTEX();
1514 if ( rProperty.Name == "AutomaticAdvancement" )
1516 double nTimeout(0.0);
1517 mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
1518 if (mbAutomaticAdvancementMode)
1520 maEventMultiplexer.setAutomaticTimeout( nTimeout );
1522 maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
1523 return true;
1526 if ( rProperty.Name == "UserPaintColor" )
1528 sal_Int32 nColor(0);
1529 if (rProperty.Value >>= nColor)
1531 OSL_ENSURE( mbMouseVisible,
1532 "setProperty(): User paint overrides invisible mouse" );
1534 // enable user paint
1535 maUserPaintColor.reset( unoColor2RGBColor( nColor ) );
1536 if( mpCurrentSlide && !mpCurrentSlide->isPaintOverlayActive() )
1537 mpCurrentSlide->enablePaintOverlay();
1539 maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
1541 else
1543 // disable user paint
1544 maUserPaintColor.reset();
1545 maEventMultiplexer.notifyUserPaintDisabled();
1546 if( mpCurrentSlide )
1547 mpCurrentSlide->disablePaintOverlay();
1550 resetCursor();
1552 return true;
1555 //adding support for erasing features in UserPaintOverlay
1556 if ( rProperty.Name == "EraseAllInk" )
1558 bool bEraseAllInk(false);
1559 if (rProperty.Value >>= bEraseAllInk)
1561 OSL_ENSURE( mbMouseVisible,
1562 "setProperty(): User paint overrides invisible mouse" );
1564 // enable user paint
1565 maEraseAllInk.reset( bEraseAllInk );
1566 maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
1569 return true;
1572 if ( rProperty.Name == "SwitchPenMode" )
1574 bool bSwitchPenMode(false);
1575 if (rProperty.Value >>= bSwitchPenMode)
1577 OSL_ENSURE( mbMouseVisible,
1578 "setProperty(): User paint overrides invisible mouse" );
1580 if(bSwitchPenMode){
1581 // Switch to Pen Mode
1582 maSwitchPenMode.reset( bSwitchPenMode );
1583 maEventMultiplexer.notifySwitchPenMode();
1586 return true;
1589 if ( rProperty.Name == "SwitchEraserMode" )
1591 bool bSwitchEraserMode(false);
1592 if (rProperty.Value >>= bSwitchEraserMode)
1594 OSL_ENSURE( mbMouseVisible,
1595 "setProperty(): User paint overrides invisible mouse" );
1596 if(bSwitchEraserMode){
1597 // switch to Eraser mode
1598 maSwitchEraserMode.reset( bSwitchEraserMode );
1599 maEventMultiplexer.notifySwitchEraserMode();
1603 return true;
1606 if ( rProperty.Name == "EraseInk" )
1608 sal_Int32 nEraseInk(100);
1609 if (rProperty.Value >>= nEraseInk)
1611 OSL_ENSURE( mbMouseVisible,
1612 "setProperty(): User paint overrides invisible mouse" );
1614 // enable user paint
1615 maEraseInk.reset( nEraseInk );
1616 maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
1619 return true;
1622 // new Property for pen's width
1623 if ( rProperty.Name == "UserPaintStrokeWidth" )
1625 double nWidth(4.0);
1626 if (rProperty.Value >>= nWidth)
1628 OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
1629 // enable user paint stroke width
1630 maUserPaintStrokeWidth = nWidth;
1631 maEventMultiplexer.notifyUserPaintStrokeWidth( maUserPaintStrokeWidth );
1634 return true;
1637 if ( rProperty.Name == "AdvanceOnClick" )
1639 bool bAdvanceOnClick = false;
1640 if (! (rProperty.Value >>= bAdvanceOnClick))
1641 return false;
1642 maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1643 return true;
1646 if ( rProperty.Name == "DisableAnimationZOrder" )
1648 bool bDisableAnimationZOrder = false;
1649 if (! (rProperty.Value >>= bDisableAnimationZOrder))
1650 return false;
1651 mbDisableAnimationZOrder = bDisableAnimationZOrder;
1652 return true;
1655 if ( rProperty.Name == "ImageAnimationsAllowed" )
1657 if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1658 return false;
1660 // TODO(F3): Forward to slides!
1661 return true;
1664 if ( rProperty.Name == "MouseVisible" )
1666 if (! (rProperty.Value >>= mbMouseVisible))
1667 return false;
1669 requestCursor(mnCurrentCursor);
1671 return true;
1674 if ( rProperty.Name == "ForceManualAdvance" )
1676 return (rProperty.Value >>= mbForceManualAdvance);
1679 if ( rProperty.Name == "RehearseTimings" )
1681 bool bRehearseTimings = false;
1682 if (! (rProperty.Value >>= bRehearseTimings))
1683 return false;
1685 if (bRehearseTimings)
1687 // TODO(Q3): Move to slide
1688 mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1689 SlideShowContext(
1690 mpDummyPtr,
1691 maEventQueue,
1692 maEventMultiplexer,
1693 maScreenUpdater,
1694 maActivitiesQueue,
1695 maUserEventQueue,
1696 *this,
1697 maViewContainer,
1698 mxComponentContext) );
1700 else if (mpRehearseTimingsActivity)
1702 // removes timer from all views:
1703 mpRehearseTimingsActivity->dispose();
1704 mpRehearseTimingsActivity.reset();
1706 return true;
1709 if ( rProperty.Name == "WaitSymbolBitmap" )
1711 uno::Reference<rendering::XBitmap> xBitmap;
1712 if (! (rProperty.Value >>= xBitmap))
1713 return false;
1715 mpWaitSymbol = WaitSymbol::create( xBitmap,
1716 maScreenUpdater,
1717 maEventMultiplexer,
1718 maViewContainer );
1720 return true;
1723 if ( rProperty.Name == "PointerSymbolBitmap" )
1725 uno::Reference<rendering::XBitmap> xBitmap;
1726 if (! (rProperty.Value >>= xBitmap))
1727 return false;
1729 mpPointerSymbol = PointerSymbol::create( xBitmap,
1730 maScreenUpdater,
1731 maEventMultiplexer,
1732 maViewContainer );
1734 return true;
1737 if ( rProperty.Name == "PointerVisible" )
1739 bool visible;
1740 if (!(rProperty.Value >>= visible))
1741 return false;
1743 mpPointerSymbol->setVisible(visible);
1744 return true;
1747 if ( rProperty.Name == "PointerPosition")
1749 css::geometry::RealPoint2D pos;
1750 if (! (rProperty.Value >>= pos))
1751 return false;
1753 mpPointerSymbol->viewsChanged(pos);
1754 return true;
1757 if (rProperty.Name == "NoSlideTransitions" )
1759 return (rProperty.Value >>= mbNoSlideTransitions);
1762 if ( rProperty.Name == "IsSoundEnabled" )
1764 uno::Sequence<uno::Any> aValues;
1765 uno::Reference<presentation::XSlideShowView> xView;
1766 bool bValue (false);
1767 if ((rProperty.Value >>= aValues)
1768 && aValues.getLength()==2
1769 && (aValues[0] >>= xView)
1770 && (aValues[1] >>= bValue))
1772 // Look up the view.
1773 for (UnoViewVector::const_iterator
1774 iView (maViewContainer.begin()),
1775 iEnd (maViewContainer.end());
1776 iView!=iEnd;
1777 ++iView)
1779 if (*iView && (*iView)->getUnoView()==xView)
1781 // Store the flag at the view so that media shapes have
1782 // access to it.
1783 (*iView)->setIsSoundEnabled(bValue);
1784 return true;
1790 return false;
1793 void SlideShowImpl::addSlideShowListener(
1794 uno::Reference<presentation::XSlideShowListener> const& xListener )
1796 osl::MutexGuard const guard( m_aMutex );
1798 if (isDisposed())
1799 return;
1801 // container syncs with passed mutex ref
1802 maListenerContainer.addInterface(xListener);
1805 void SlideShowImpl::removeSlideShowListener(
1806 uno::Reference<presentation::XSlideShowListener> const& xListener )
1808 osl::MutexGuard const guard( m_aMutex );
1810 // container syncs with passed mutex ref
1811 maListenerContainer.removeInterface(xListener);
1814 void SlideShowImpl::addShapeEventListener(
1815 uno::Reference<presentation::XShapeEventListener> const& xListener,
1816 uno::Reference<drawing::XShape> const& xShape )
1818 osl::MutexGuard const guard( m_aMutex );
1820 if (isDisposed())
1821 return;
1823 // precondition: must only be called from the main thread!
1824 DBG_TESTSOLARMUTEX();
1826 ShapeEventListenerMap::iterator aIter;
1827 if( (aIter=maShapeEventListeners.find( xShape )) ==
1828 maShapeEventListeners.end() )
1830 // no entry for this shape -> create one
1831 aIter = maShapeEventListeners.emplace(
1832 xShape,
1833 std::make_shared<comphelper::OInterfaceContainerHelper2>(
1834 m_aMutex)).first;
1837 // add new listener to broadcaster
1838 if( aIter->second.get() )
1839 aIter->second->addInterface( xListener );
1841 maEventMultiplexer.notifyShapeListenerAdded(xListener,
1842 xShape);
1845 void SlideShowImpl::removeShapeEventListener(
1846 uno::Reference<presentation::XShapeEventListener> const& xListener,
1847 uno::Reference<drawing::XShape> const& xShape )
1849 osl::MutexGuard const guard( m_aMutex );
1851 // precondition: must only be called from the main thread!
1852 DBG_TESTSOLARMUTEX();
1854 ShapeEventListenerMap::iterator aIter;
1855 if( (aIter = maShapeEventListeners.find( xShape )) !=
1856 maShapeEventListeners.end() )
1858 // entry for this shape found -> remove listener from
1859 // helper object
1860 ENSURE_OR_THROW(
1861 aIter->second.get(),
1862 "SlideShowImpl::removeShapeEventListener(): "
1863 "listener map contains NULL broadcast helper" );
1865 aIter->second->removeInterface( xListener );
1868 maEventMultiplexer.notifyShapeListenerRemoved(xListener,
1869 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_QUERY_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 SAL_WARN( "slideshow", comphelper::anyToString( cppu::getCaughtException() ) );
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 occoured.
2317 This will be forewarded 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 //===== FrameSynchronization ==================================================
2350 FrameSynchronization::FrameSynchronization (const double nFrameDuration)
2351 : maTimer(),
2352 mnFrameDuration(nFrameDuration),
2353 mnNextFrameTargetTime(0),
2354 mbIsActive(false)
2356 MarkCurrentFrame();
2359 void FrameSynchronization::MarkCurrentFrame()
2361 mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2364 void FrameSynchronization::Synchronize()
2366 if (mbIsActive)
2368 // Do busy waiting for now.
2369 while (maTimer.getElapsedTime() < mnNextFrameTargetTime)
2373 MarkCurrentFrame();
2376 void FrameSynchronization::Activate()
2378 mbIsActive = true;
2381 void FrameSynchronization::Deactivate()
2383 mbIsActive = false;
2386 } // anon namespace
2388 namespace sdecl = comphelper::service_decl;
2389 const sdecl::ServiceDecl slideShowDecl(
2390 sdecl::class_<SlideShowImpl>(),
2391 "com.sun.star.comp.presentation.SlideShow",
2392 "com.sun.star.presentation.SlideShow" );
2394 // The C shared lib entry points
2395 extern "C"
2396 SAL_DLLPUBLIC_EXPORT void* SAL_CALL slideshow_component_getFactory( sal_Char const* pImplName,
2397 void*, void* )
2399 return sdecl::component_getFactoryHelper( pImplName, {&slideShowDecl} );
2402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */