summaryrefslogtreecommitdiff
path: root/slideshow/source/engine/slideshowimpl.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'slideshow/source/engine/slideshowimpl.cxx')
-rw-r--r--slideshow/source/engine/slideshowimpl.cxx2534
1 files changed, 2534 insertions, 0 deletions
diff --git a/slideshow/source/engine/slideshowimpl.cxx b/slideshow/source/engine/slideshowimpl.cxx
new file mode 100644
index 000000000000..d2af8c0daaed
--- /dev/null
+++ b/slideshow/source/engine/slideshowimpl.cxx
@@ -0,0 +1,2534 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_slideshow.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase1.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/implementationentry.hxx>
+#include <cppuhelper/compbase2.hxx>
+#include <cppuhelper/interfacecontainer.h>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <comphelper/anytostring.hxx>
+#include <comphelper/make_shared_from_uno.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/optional.hxx>
+#include <comphelper/servicedecl.hxx>
+#include <comphelper/namecontainer.hxx>
+
+#include <cppcanvas/spritecanvas.hxx>
+#include <cppcanvas/vclfactory.hxx>
+#include <cppcanvas/basegfxfactory.hxx>
+
+#include <tools/debug.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/tools/canvastools.hxx>
+
+#include <vcl/font.hxx>
+#include "rtl/ref.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/awt/XPaintListener.hpp>
+#include <com/sun/star/awt/SystemPointer.hpp>
+#include <com/sun/star/animations/TransitionType.hpp>
+#include <com/sun/star/animations/TransitionSubType.hpp>
+#include <com/sun/star/presentation/XSlideShow.hpp>
+#include <com/sun/star/presentation/XSlideShowListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/drawing/XLayer.hpp>
+#include <com/sun/star/drawing/XLayerSupplier.hpp>
+#include <com/sun/star/drawing/XLayerManager.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+
+#include "com/sun/star/uno/Reference.hxx"
+#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
+
+#include "unoviewcontainer.hxx"
+#include "transitionfactory.hxx"
+#include "eventmultiplexer.hxx"
+#include "usereventqueue.hxx"
+#include "eventqueue.hxx"
+#include "cursormanager.hxx"
+#include "slideshowcontext.hxx"
+#include "activitiesqueue.hxx"
+#include "activitiesfactory.hxx"
+#include "interruptabledelayevent.hxx"
+#include "slide.hxx"
+#include "shapemaps.hxx"
+#include "slideview.hxx"
+#include "tools.hxx"
+#include "unoview.hxx"
+#include "slidebitmap.hxx"
+#include "rehearsetimingsactivity.hxx"
+#include "waitsymbol.hxx"
+#include "effectrewinder.hxx"
+#include "framerate.hxx"
+
+#include <boost/noncopyable.hpp>
+#include <boost/bind.hpp>
+
+#include <map>
+#include <vector>
+#include <iterator>
+#include <string>
+#include <algorithm>
+#include <stdio.h>
+#include <iostream>
+
+using namespace com::sun::star;
+using namespace ::slideshow::internal;
+
+namespace {
+
+/** During animations the update() method tells its caller to call it as
+ soon as possible. This gives us more time to render the next frame and
+ still maintain a steady frame rate. This class is responsible for
+ synchronizing the display of new frames and thus keeping the frame rate
+ steady.
+*/
+class FrameSynchronization
+{
+public:
+ /** Create new object with a predefined duration between two frames.
+ @param nFrameDuration
+ The preferred duration between the display of two frames in
+ seconds.
+ */
+ FrameSynchronization (const double nFrameDuration);
+
+ /** Set the current time as the time at which the current frame is
+ displayed. From this the target time of the next frame is derived.
+ */
+ void MarkCurrentFrame (void);
+
+ /** When there is time left until the next frame is due then wait.
+ Otherwise return without delay.
+ */
+ void Synchronize (void);
+
+ /** Activate frame synchronization when an animation is active and
+ frames are to be displayed in a steady rate. While active
+ Synchronize() will wait until the frame duration time has passed.
+ */
+ void Activate (void);
+
+ /** Deactivate frame sychronization when no animation is active and the
+ time between frames depends on user actions and other external
+ sources. While deactivated Synchronize() will return without delay.
+ */
+ void Deactivate (void);
+
+ /** Return the current time of the timer. It is not synchronized with
+ any other timer so its absolute values are of no concern. Typically
+ used during debugging to measure durations.
+ */
+ double GetCurrentTime (void) const;
+
+private:
+ /** The timer that is used for synchronization is independent from the
+ one used by SlideShowImpl: it is not paused or modified by
+ animations.
+ */
+ canvas::tools::ElapsedTime maTimer;
+ /** Time between the display of frames. Enforced only when mbIsActive
+ is <TRUE/>.
+ */
+ const double mnFrameDuration;
+ /** Time (of maTimer) when the next frame shall be displayed.
+ Synchronize() will wait until this time.
+ */
+ double mnNextFrameTargetTime;
+ /** Synchronize() will wait only when this flag is <TRUE/>. Otherwise
+ it returns immediately.
+ */
+ bool mbIsActive;
+};
+
+
+
+
+/******************************************************************************
+
+ SlideShowImpl
+
+ This class encapsulates the slideshow presentation viewer.
+
+ With an instance of this class, it is possible to statically
+ and dynamically show a presentation, as defined by the
+ constructor-provided draw model (represented by a sequence
+ of ::com::sun::star::drawing::XDrawPage objects).
+
+ It is possible to show the presentation on multiple views
+ simultaneously (e.g. for a multi-monitor setup). Since this
+ class also relies on user interaction, the corresponding
+ XSlideShowView interface provides means to register some UI
+ event listeners (mostly borrowed from awt::XWindow interface).
+
+ Since currently (mid 2004), OOo isn't very well suited to
+ multi-threaded rendering, this class relies on <em>very
+ frequent</em> external update() calls, which will render the
+ next frame of animations. This works as follows: after the
+ displaySlide() has been successfully called (which setup and
+ starts an actual slide show), the update() method must be
+ called until it returns false.
+ Effectively, this puts the burden of providing
+ concurrency to the clients of this class, which, as noted
+ above, is currently unavoidable with the current state of
+ affairs (I've actually tried threading here, but failed
+ miserably when using the VCL canvas as the render backend -
+ deadlocked).
+
+ ******************************************************************************/
+
+typedef cppu::WeakComponentImplHelper1<presentation::XSlideShow> SlideShowImplBase;
+
+typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
+
+/// Maps XDrawPage for annotations persistence
+typedef ::std::map< ::com::sun::star::uno::Reference<
+ ::com::sun::star::drawing::XDrawPage>,
+ PolyPolygonVector> PolygonMap;
+
+class SlideShowImpl : private cppu::BaseMutex,
+ public CursorManager,
+ public SlideShowImplBase
+{
+public:
+ explicit SlideShowImpl(
+ uno::Reference<uno::XComponentContext> const& xContext );
+
+ /** Notify that the transition phase of the current slide
+ has ended.
+
+ The life of a slide has three phases: the transition
+ phase, when the previous slide vanishes, and the
+ current slide becomes visible, the shape animation
+ phase, when shape effects are running, and the phase
+ after the last shape animation has ended, but before
+ the next slide transition starts.
+
+ This method notifies the end of the first phase.
+
+ @param bPaintSlide
+ When true, Slide::show() is passed a true as well, denoting
+ explicit paint of slide content. Pass false here, if e.g. a
+ slide transition has already rendered the initial slide image.
+ */
+ void notifySlideTransitionEnded( bool bPaintSlide );
+
+ /** Notify that the shape animation phase of the current slide
+ has ended.
+
+ The life of a slide has three phases: the transition
+ phase, when the previous slide vanishes, and the
+ current slide becomes visible, the shape animation
+ phase, when shape effects are running, and the phase
+ after the last shape animation has ended, but before
+ the next slide transition starts.
+
+ This method notifies the end of the second phase.
+ */
+ void notifySlideAnimationsEnded();
+
+ /** Notify that the slide has ended.
+
+ The life of a slide has three phases: the transition
+ phase, when the previous slide vanishes, and the
+ current slide becomes visible, the shape animation
+ phase, when shape effects are running, and the phase
+ after the last shape animation has ended, but before
+ the next slide transition starts.
+
+ This method notifies the end of the third phase.
+ */
+ void notifySlideEnded (const bool bReverse);
+
+ /** Notification from eventmultiplexer that a hyperlink
+ has been clicked.
+ */
+ bool notifyHyperLinkClicked( rtl::OUString const& hyperLink );
+
+ /** Notification from eventmultiplexer that an animation event has occoured.
+ This will be forewarded to all registered XSlideShowListener
+ */
+ bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
+
+private:
+ // XSlideShow:
+ virtual sal_Bool SAL_CALL nextEffect() throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL previousEffect() throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL startShapeActivity(
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL stopShapeActivity(
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL pause( sal_Bool bPauseShow )
+ throw (uno::RuntimeException);
+ virtual uno::Reference<drawing::XDrawPage> SAL_CALL getCurrentSlide()
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL displaySlide(
+ uno::Reference<drawing::XDrawPage> const& xSlide,
+ uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
+ uno::Reference<animations::XAnimationNode> const& xRootNode,
+ uno::Sequence<beans::PropertyValue> const& rProperties )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL registerUserPaintPolygons( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xDocFactory ) throw (::com::sun::star::uno::RuntimeException);
+ virtual sal_Bool SAL_CALL setProperty(
+ beans::PropertyValue const& rProperty ) throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL addView(
+ uno::Reference<presentation::XSlideShowView> const& xView )
+ throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL removeView(
+ uno::Reference<presentation::XSlideShowView> const& xView )
+ throw (uno::RuntimeException);
+ virtual sal_Bool SAL_CALL update( double & nNextTimeout )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL addSlideShowListener(
+ uno::Reference<presentation::XSlideShowListener> const& xListener )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL removeSlideShowListener(
+ uno::Reference<presentation::XSlideShowListener> const& xListener )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL addShapeEventListener(
+ uno::Reference<presentation::XShapeEventListener> const& xListener,
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL removeShapeEventListener(
+ uno::Reference<presentation::XShapeEventListener> const& xListener,
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException);
+ virtual void SAL_CALL setShapeCursor(
+ uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
+ throw (uno::RuntimeException);
+
+
+ // CursorManager
+ // -----------------------------------------------------------
+
+ virtual bool requestCursor( sal_Int16 nCursorShape );
+ virtual void resetCursor();
+
+ /** This is somewhat similar to displaySlide when called for the current
+ slide. It has been simplified to take advantage of that no slide
+ change takes place. Furthermore it does not show the slide
+ transition.
+ */
+ void redisplayCurrentSlide (void);
+
+protected:
+ // WeakComponentImplHelperBase
+ virtual void SAL_CALL disposing();
+
+ bool isDisposed() const
+ {
+ return (rBHelper.bDisposed || rBHelper.bInDispose);
+ }
+
+private:
+ struct SeparateListenerImpl; friend struct SeparateListenerImpl;
+ struct PrefetchPropertiesFunc; friend struct PrefetchPropertiesFunc;
+
+ /// Stop currently running show.
+ void stopShow();
+
+ ///Find a polygons vector in maPolygons (map)
+ PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
+
+ /// Creates a new slide.
+ SlideSharedPtr makeSlide(
+ uno::Reference<drawing::XDrawPage> const& xDrawPage,
+ uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
+ uno::Reference<animations::XAnimationNode> const& xRootNode );
+
+ /// Checks whether the given slide/animation node matches mpPrefetchSlide
+ static bool matches(
+ SlideSharedPtr const& pSlide,
+ uno::Reference<drawing::XDrawPage> const& xSlide,
+ uno::Reference<animations::XAnimationNode> const& xNode )
+ {
+ if (pSlide)
+ return (pSlide->getXDrawPage() == xSlide &&
+ pSlide->getXAnimationNode() == xNode);
+ else
+ return (!xSlide.is() && !xNode.is());
+ }
+
+ /// Resets the current slide transition sound object with a new one:
+ SoundPlayerSharedPtr resetSlideTransitionSound(
+ uno::Any const& url = uno::Any(), bool bLoopSound = false );
+
+ /// stops the current slide transition sound
+ void stopSlideTransitionSound();
+
+ /** Prepare a slide transition
+
+ This method registers all necessary events and
+ activities for a slide transition.
+
+ @return the slide change activity, or NULL for no transition effect
+ */
+ ActivitySharedPtr createSlideTransition(
+ const uno::Reference< drawing::XDrawPage >& xDrawPage,
+ const SlideSharedPtr& rLeavingSlide,
+ const SlideSharedPtr& rEnteringSlide,
+ const EventSharedPtr& rTransitionEndEvent );
+
+ /** Request/release the wait symbol. The wait symbol is displayed when
+ there are more requests then releases. Locking the wait symbol
+ helps to avoid intermediate repaints.
+
+ Do not call this method directly. Use WaitSymbolLock instead.
+ */
+ void requestWaitSymbol (void);
+ void releaseWaitSymbol (void);
+
+ class WaitSymbolLock {public:
+ WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
+ { mrSlideShowImpl.requestWaitSymbol(); }
+ ~WaitSymbolLock(void)
+ { mrSlideShowImpl.releaseWaitSymbol(); }
+ private: SlideShowImpl& mrSlideShowImpl;
+ };
+
+
+ /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
+ sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
+
+ /** This method is called asynchronously to finish the rewinding of an
+ effect to the previous slide that was initiated earlier.
+ */
+ void rewindEffectToPreviousSlide (void);
+
+ /// all registered views
+ UnoViewContainer maViewContainer;
+
+ /// all registered slide show listeners
+ cppu::OInterfaceContainerHelper maListenerContainer;
+
+ /// map of vectors, containing all registered listeners for a shape
+ ShapeEventListenerMap maShapeEventListeners;
+ /// map of sal_Int16 values, specifying the mouse cursor for every shape
+ ShapeCursorMap maShapeCursors;
+
+ //map of vector of Polygons, containing polygons drawn on each slide.
+ PolygonMap maPolygons;
+
+ boost::optional<RGBColor> maUserPaintColor;
+
+ boost::optional<double> maUserPaintStrokeWidth;
+
+ //changed for the eraser project
+ boost::optional<bool> maEraseAllInk;
+ boost::optional<bool> maSwitchPenMode;
+ boost::optional<bool> maSwitchEraserMode;
+ boost::optional<sal_Int32> maEraseInk;
+ //end changed
+
+ boost::shared_ptr<canvas::tools::ElapsedTime> mpPresTimer;
+ ScreenUpdater maScreenUpdater;
+ EventQueue maEventQueue;
+ EventMultiplexer maEventMultiplexer;
+ ActivitiesQueue maActivitiesQueue;
+ UserEventQueue maUserEventQueue;
+ SubsettableShapeManagerSharedPtr mpDummyPtr;
+
+ boost::shared_ptr<SeparateListenerImpl> mpListener;
+
+ boost::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
+ boost::shared_ptr<WaitSymbol> mpWaitSymbol;
+
+ /// the current slide transition sound object:
+ SoundPlayerSharedPtr mpCurrentSlideTransitionSound;
+
+ uno::Reference<uno::XComponentContext> mxComponentContext;
+ uno::Reference<
+ presentation::XTransitionFactory> mxOptionalTransitionFactory;
+
+ /// the previously running slide
+ SlideSharedPtr mpPreviousSlide;
+ /// the currently running slide
+ SlideSharedPtr mpCurrentSlide;
+ /// the already prefetched slide: best candidate for upcoming slide
+ SlideSharedPtr mpPrefetchSlide;
+ /// slide to be prefetched: best candidate for upcoming slide
+ uno::Reference<drawing::XDrawPage> mxPrefetchSlide;
+ /// save the XDrawPagesSupplier to retieve polygons
+ uno::Reference<drawing::XDrawPagesSupplier> mxDrawPagesSupplier;
+ /// slide animation to be prefetched:
+ uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
+
+ sal_Int16 mnCurrentCursor;
+
+ sal_Int32 mnWaitSymbolRequestCount;
+ bool mbAutomaticAdvancementMode;
+ bool mbImageAnimationsAllowed;
+ bool mbNoSlideTransitions;
+ bool mbMouseVisible;
+ bool mbForceManualAdvance;
+ bool mbShowPaused;
+ bool mbSlideShowIdle;
+ bool mbDisableAnimationZOrder;
+
+ EffectRewinder maEffectRewinder;
+ FrameSynchronization maFrameSynchronization;
+};
+
+
+/** Separate event listener for animation, view and hyperlink events.
+
+ This handler is registered for slide animation end, view and
+ hyperlink events at the global EventMultiplexer, and forwards
+ notifications to the SlideShowImpl
+*/
+struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
+ public ViewRepaintHandler,
+ public HyperlinkHandler,
+ public AnimationEventHandler,
+ private boost::noncopyable
+{
+ SlideShowImpl& mrShow;
+ ScreenUpdater& mrScreenUpdater;
+ EventQueue& mrEventQueue;
+
+ SeparateListenerImpl( SlideShowImpl& rShow,
+ ScreenUpdater& rScreenUpdater,
+ EventQueue& rEventQueue ) :
+ mrShow( rShow ),
+ mrScreenUpdater( rScreenUpdater ),
+ mrEventQueue( rEventQueue )
+ {}
+
+ // EventHandler
+ virtual bool handleEvent()
+ {
+ // DON't call notifySlideAnimationsEnded()
+ // directly, but queue an event. handleEvent()
+ // might be called from e.g.
+ // showNext(), and notifySlideAnimationsEnded() must not be called
+ // in recursion. Note that the event is scheduled for the next
+ // frame so that its expensive execution does not come in between
+ // sprite hiding and shape redraw (at the end of the animation of a
+ // shape), which would cause a flicker.
+ mrEventQueue.addEventForNextRound(
+ makeEvent(
+ boost::bind( &SlideShowImpl::notifySlideAnimationsEnded, boost::ref(mrShow) ),
+ "SlideShowImpl::notifySlideAnimationsEnded"));
+ return true;
+ }
+
+ // ViewRepaintHandler
+ virtual void viewClobbered( const UnoViewSharedPtr& rView )
+ {
+ // given view needs repaint, request update
+ mrScreenUpdater.notifyUpdate(rView, true);
+ }
+
+ // HyperlinkHandler
+ virtual bool handleHyperlink( ::rtl::OUString const& rLink )
+ {
+ return mrShow.notifyHyperLinkClicked(rLink);
+ }
+
+ // AnimationEventHandler
+ virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
+ {
+ return mrShow.handleAnimationEvent(rNode);
+ }
+};
+
+
+SlideShowImpl::SlideShowImpl(
+ uno::Reference<uno::XComponentContext> const& xContext )
+ : SlideShowImplBase(m_aMutex),
+ maViewContainer(),
+ maListenerContainer( m_aMutex ),
+ maShapeEventListeners(),
+ maShapeCursors(),
+ maUserPaintColor(),
+ maUserPaintStrokeWidth(4.0),
+ mpPresTimer( new canvas::tools::ElapsedTime ),
+ maScreenUpdater(maViewContainer),
+ maEventQueue( mpPresTimer ),
+ maEventMultiplexer( maEventQueue,
+ maViewContainer ),
+ maActivitiesQueue( mpPresTimer ),
+ maUserEventQueue( maEventMultiplexer,
+ maEventQueue,
+ *this ),
+ mpDummyPtr(),
+ mpListener(),
+ mpRehearseTimingsActivity(),
+ mpWaitSymbol(),
+ mpCurrentSlideTransitionSound(),
+ mxComponentContext( xContext ),
+ mxOptionalTransitionFactory(),
+ mpCurrentSlide(),
+ mpPrefetchSlide(),
+ mxPrefetchSlide(),
+ mxDrawPagesSupplier(),
+ mxPrefetchAnimationNode(),
+ mnCurrentCursor(awt::SystemPointer::ARROW),
+ mnWaitSymbolRequestCount(0),
+ mbAutomaticAdvancementMode(false),
+ mbImageAnimationsAllowed( true ),
+ mbNoSlideTransitions( false ),
+ mbMouseVisible( true ),
+ mbForceManualAdvance( false ),
+ mbShowPaused( false ),
+ mbSlideShowIdle( true ),
+ mbDisableAnimationZOrder( false ),
+ maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue),
+ maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond)
+
+{
+ // keep care not constructing any UNO references to this inside ctor,
+ // shift that code to create()!
+
+ uno::Reference<lang::XMultiComponentFactory> xFactory(
+ mxComponentContext->getServiceManager() );
+
+ if( xFactory.is() )
+ {
+ try
+ {
+ // #i82460# try to retrieve special transition factory
+ mxOptionalTransitionFactory.set(
+ xFactory->createInstanceWithContext(
+ ::rtl::OUString::createFromAscii( "com.sun.star.presentation.TransitionFactory" ),
+ mxComponentContext ),
+ uno::UNO_QUERY );
+ }
+ catch (loader::CannotActivateFactoryException const&)
+ {
+ }
+ }
+
+ mpListener.reset( new SeparateListenerImpl(
+ *this,
+ maScreenUpdater,
+ maEventQueue ));
+ maEventMultiplexer.addSlideAnimationsEndHandler( mpListener );
+ maEventMultiplexer.addViewRepaintHandler( mpListener );
+ maEventMultiplexer.addHyperlinkHandler( mpListener, 0.0 );
+ maEventMultiplexer.addAnimationStartHandler( mpListener );
+ maEventMultiplexer.addAnimationEndHandler( mpListener );
+}
+
+// we are about to be disposed (someone call dispose() on us)
+void SlideShowImpl::disposing()
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ maEffectRewinder.dispose();
+
+ // stop slide transition sound, if any:
+ stopSlideTransitionSound();
+
+ mxComponentContext.clear();
+
+ if( mpCurrentSlideTransitionSound )
+ {
+ mpCurrentSlideTransitionSound->dispose();
+ mpCurrentSlideTransitionSound.reset();
+ }
+
+ mpWaitSymbol.reset();
+
+ if( mpRehearseTimingsActivity )
+ {
+ mpRehearseTimingsActivity->dispose();
+ mpRehearseTimingsActivity.reset();
+ }
+
+ if( mpListener )
+ {
+ maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
+ maEventMultiplexer.removeViewRepaintHandler(mpListener);
+ maEventMultiplexer.removeHyperlinkHandler(mpListener);
+ maEventMultiplexer.removeAnimationStartHandler( mpListener );
+ maEventMultiplexer.removeAnimationEndHandler( mpListener );
+
+ mpListener.reset();
+ }
+
+ maUserEventQueue.clear();
+ maActivitiesQueue.clear();
+ maEventMultiplexer.clear();
+ maEventQueue.clear();
+ mpPresTimer.reset();
+ maShapeCursors.clear();
+ maShapeEventListeners.clear();
+
+ // send all listeners a disposing() that we are going down:
+ maListenerContainer.disposeAndClear(
+ lang::EventObject( static_cast<cppu::OWeakObject *>(this) ) );
+
+ maViewContainer.dispose();
+
+ // release slides:
+ mxPrefetchAnimationNode.clear();
+ mxPrefetchSlide.clear();
+ mpPrefetchSlide.reset();
+ mpCurrentSlide.reset();
+ mpPreviousSlide.reset();
+}
+
+/// stops the current slide transition sound
+void SlideShowImpl::stopSlideTransitionSound()
+{
+ if (mpCurrentSlideTransitionSound)
+ {
+ mpCurrentSlideTransitionSound->stopPlayback();
+ mpCurrentSlideTransitionSound->dispose();
+ mpCurrentSlideTransitionSound.reset();
+ }
+ }
+
+SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
+{
+ sal_Bool bStopSound = sal_False;
+ rtl::OUString url;
+
+ if( !(rSound >>= bStopSound) )
+ bStopSound = sal_False;
+ rSound >>= url;
+
+ if( !bStopSound && (url.getLength() == 0) )
+ return SoundPlayerSharedPtr();
+
+ stopSlideTransitionSound();
+
+ if (url.getLength() > 0)
+ {
+ try
+ {
+ mpCurrentSlideTransitionSound = SoundPlayer::create(
+ maEventMultiplexer, url, mxComponentContext );
+ mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
+ }
+ catch (lang::NoSupportException const&)
+ {
+ // catch possible exceptions from SoundPlayer, since
+ // being not able to playback the sound is not a hard
+ // error here (still, the slide transition should be
+ // shown).
+ }
+ }
+ return mpCurrentSlideTransitionSound;
+}
+
+ActivitySharedPtr SlideShowImpl::createSlideTransition(
+ const uno::Reference< drawing::XDrawPage >& xDrawPage,
+ const SlideSharedPtr& rLeavingSlide,
+ const SlideSharedPtr& rEnteringSlide,
+ const EventSharedPtr& rTransitionEndEvent)
+{
+ ENSURE_OR_THROW( !maViewContainer.empty(),
+ "createSlideTransition(): No views" );
+ ENSURE_OR_THROW( rEnteringSlide,
+ "createSlideTransition(): No entering slide" );
+
+ // return empty transition, if slide transitions
+ // are disabled.
+ if (mbNoSlideTransitions)
+ return ActivitySharedPtr();
+
+ // retrieve slide change parameters from XDrawPage
+ uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
+ uno::UNO_QUERY );
+
+ if( !xPropSet.is() )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Slide has no PropertySet - assuming no transition\n" );
+ return ActivitySharedPtr();
+ }
+
+ sal_Int16 nTransitionType(0);
+ if( !getPropertyValue( nTransitionType,
+ xPropSet,
+ OUSTR("TransitionType" )) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Could not extract slide transition type from XDrawPage - assuming no transition\n" );
+ return ActivitySharedPtr();
+ }
+
+ sal_Int16 nTransitionSubType(0);
+ if( !getPropertyValue( nTransitionSubType,
+ xPropSet,
+ OUSTR("TransitionSubtype" )) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Could not extract slide transition subtype from XDrawPage - assuming no transition\n" );
+ return ActivitySharedPtr();
+ }
+
+ bool bTransitionDirection(false);
+ if( !getPropertyValue( bTransitionDirection,
+ xPropSet,
+ OUSTR("TransitionDirection")) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Could not extract slide transition direction from XDrawPage - assuming default direction\n" );
+ }
+
+ sal_Int32 aUnoColor(0);
+ if( !getPropertyValue( aUnoColor,
+ xPropSet,
+ OUSTR("TransitionFadeColor")) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Could not extract slide transition fade color from XDrawPage - assuming black\n" );
+ }
+
+ const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
+
+ uno::Any aSound;
+ sal_Bool bLoopSound = sal_False;
+
+ if( !getPropertyValue( aSound, xPropSet, OUSTR("Sound")) )
+ OSL_TRACE( "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound\n" );
+
+ if( !getPropertyValue( bLoopSound, xPropSet, OUSTR("LoopSound") ) )
+ OSL_TRACE( "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound\n" );
+
+ NumberAnimationSharedPtr pTransition(
+ TransitionFactory::createSlideTransition(
+ rLeavingSlide,
+ rEnteringSlide,
+ maViewContainer,
+ maScreenUpdater,
+ maEventMultiplexer,
+ mxOptionalTransitionFactory,
+ nTransitionType,
+ nTransitionSubType,
+ bTransitionDirection,
+ aTransitionFadeColor,
+ resetSlideTransitionSound( aSound, bLoopSound ) ));
+
+ if( !pTransition )
+ return ActivitySharedPtr(); // no transition effect has been
+ // generated. Normally, that means
+ // that simply no transition is
+ // set on this slide.
+
+ double nTransitionDuration(0.0);
+ if( !getPropertyValue( nTransitionDuration,
+ xPropSet,
+ OUSTR("TransitionDuration")) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "Could not extract slide transition duration from XDrawPage - assuming no transition\n" );
+ return ActivitySharedPtr();
+ }
+
+ sal_Int32 nMinFrames(5);
+ if( !getPropertyValue( nMinFrames,
+ xPropSet,
+ OUSTR("MinimalFrameNumber")) )
+ {
+ OSL_TRACE( "createSlideTransition(): "
+ "No minimal number of frames given - assuming 5\n" );
+ }
+
+ // prefetch slide transition bitmaps, but postpone it after
+ // displaySlide() has finished - sometimes, view size has not yet
+ // reached final size
+ maEventQueue.addEvent(
+ makeEvent(
+ boost::bind(
+ &::slideshow::internal::Animation::prefetch,
+ pTransition,
+ AnimatableShapeSharedPtr(),
+ ShapeAttributeLayerSharedPtr()),
+ "Animation::prefetch"));
+
+ return ActivitySharedPtr(
+ ActivitiesFactory::createSimpleActivity(
+ ActivitiesFactory::CommonParameters(
+ rTransitionEndEvent,
+ maEventQueue,
+ maActivitiesQueue,
+ nTransitionDuration,
+ nMinFrames,
+ false,
+ boost::optional<double>(1.0),
+ 0.0,
+ 0.0,
+ ShapeSharedPtr(),
+ rEnteringSlide->getSlideSize() ),
+ pTransition,
+ true ));
+}
+
+PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
+{
+ // TODO(P2) : Optimze research in the map.
+ bool bFound = false;
+ PolygonMap::iterator aIter=maPolygons.begin();
+
+
+ while(aIter!=maPolygons.end() && !bFound)
+ {
+ if(aIter->first == xDrawPage)
+ bFound = true;
+ else
+ aIter++;
+ }
+
+ return aIter;
+}
+
+SlideSharedPtr SlideShowImpl::makeSlide(
+ uno::Reference<drawing::XDrawPage> const& xDrawPage,
+ uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
+ uno::Reference<animations::XAnimationNode> const& xRootNode )
+{
+ if( !xDrawPage.is() )
+ return SlideSharedPtr();
+
+ //Retrieve polygons for the current slide
+ PolygonMap::iterator aIter;
+ aIter = findPolygons(xDrawPage);
+
+ const SlideSharedPtr pSlide( createSlide(xDrawPage,
+ xDrawPages,
+ xRootNode,
+ maEventQueue,
+ maEventMultiplexer,
+ maScreenUpdater,
+ maActivitiesQueue,
+ maUserEventQueue,
+ *this,
+ maViewContainer,
+ mxComponentContext,
+ maShapeEventListeners,
+ maShapeCursors,
+ (aIter != maPolygons.end()) ? aIter->second : PolyPolygonVector(),
+ maUserPaintColor ? *maUserPaintColor : RGBColor(),
+ *maUserPaintStrokeWidth,
+ !!maUserPaintColor,
+ mbImageAnimationsAllowed,
+ mbDisableAnimationZOrder) );
+
+ // prefetch show content (reducing latency for slide
+ // bitmap and effect start later on)
+ pSlide->prefetch();
+
+ return pSlide;
+}
+
+void SlideShowImpl::requestWaitSymbol (void)
+{
+ ++mnWaitSymbolRequestCount;
+ OSL_ASSERT(mnWaitSymbolRequestCount>0);
+
+ if (mnWaitSymbolRequestCount == 1)
+ {
+ if( !mpWaitSymbol )
+ {
+ // fall back to cursor
+ requestCursor(calcActiveCursor(mnCurrentCursor));
+ }
+ else
+ mpWaitSymbol->show();
+ }
+}
+
+void SlideShowImpl::releaseWaitSymbol (void)
+{
+ --mnWaitSymbolRequestCount;
+ OSL_ASSERT(mnWaitSymbolRequestCount>=0);
+
+ if (mnWaitSymbolRequestCount == 0)
+ {
+ if( !mpWaitSymbol )
+ {
+ // fall back to cursor
+ requestCursor(calcActiveCursor(mnCurrentCursor));
+ }
+ else
+ mpWaitSymbol->hide();
+ }
+}
+
+sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
+{
+ if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
+ nCursorShape = awt::SystemPointer::WAIT;
+ else if( !mbMouseVisible ) // enforce INVISIBLE
+ nCursorShape = awt::SystemPointer::INVISIBLE;
+ else if( maUserPaintColor &&
+ nCursorShape == awt::SystemPointer::ARROW )
+ nCursorShape = awt::SystemPointer::PEN;
+
+ return nCursorShape;
+}
+
+
+void SlideShowImpl::stopShow()
+{
+ // Force-end running animation
+ // ===========================
+ if (mpCurrentSlide)
+ {
+ mpCurrentSlide->hide();
+ //Register polygons in the map
+ if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
+ maPolygons.erase(mpCurrentSlide->getXDrawPage());
+
+ maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
+ }
+
+ // clear all queues
+ maEventQueue.clear();
+ maActivitiesQueue.clear();
+
+ // Attention: we MUST clear the user event queue here,
+ // this is because the current slide might have registered
+ // shape events (click or enter/leave), which might
+ // otherwise dangle forever in the queue (because of the
+ // shared ptr nature). If someone needs to change this:
+ // somehow unregister those shapes at the user event queue
+ // on notifySlideEnded().
+ maUserEventQueue.clear();
+
+ // re-enable automatic effect advancement
+ // (maEventQueue.clear() above might have killed
+ // maEventMultiplexer's tick events)
+ if (mbAutomaticAdvancementMode)
+ {
+ // toggle automatic mode (enabling just again is
+ // ignored by EventMultiplexer)
+ maEventMultiplexer.setAutomaticMode( false );
+ maEventMultiplexer.setAutomaticMode( true );
+ }
+}
+
+
+
+class SlideShowImpl::PrefetchPropertiesFunc
+{
+public:
+ PrefetchPropertiesFunc( SlideShowImpl * that_,
+ bool& rbSkipAllMainSequenceEffects,
+ bool& rbSkipSlideTransition)
+ : mpSlideShowImpl(that_),
+ mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
+ mrbSkipSlideTransition(rbSkipSlideTransition)
+ {}
+
+ void operator()( beans::PropertyValue const& rProperty ) const {
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("Prefetch") ))
+ {
+ uno::Sequence<uno::Any> seq;
+ if ((rProperty.Value >>= seq) && seq.getLength() == 2)
+ {
+ seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
+ seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
+ }
+ }
+ else if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") ))
+ {
+ rProperty.Value >>= mrbSkipAllMainSequenceEffects;
+ }
+ else if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") ))
+ {
+ rProperty.Value >>= mrbSkipSlideTransition;
+ }
+ else
+ {
+ OSL_ENSURE( false, rtl::OUStringToOString(
+ rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ }
+private:
+ SlideShowImpl *const mpSlideShowImpl;
+ bool& mrbSkipAllMainSequenceEffects;
+ bool& mrbSkipSlideTransition;
+};
+
+void SlideShowImpl::displaySlide(
+ uno::Reference<drawing::XDrawPage> const& xSlide,
+ uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
+ uno::Reference<animations::XAnimationNode> const& xRootNode,
+ uno::Sequence<beans::PropertyValue> const& rProperties )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return;
+
+ maEffectRewinder.setRootAnimationNode(xRootNode);
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+#ifdef ENABLE_PRESENTER_EXTRA_UI
+ mxDrawPagesSupplier = xDrawPages;
+#else
+ mxDrawPagesSupplier = NULL;
+#endif
+
+ stopShow(); // MUST call that: results in
+ // maUserEventQueue.clear(). What's more,
+ // stopShow()'s currSlide->hide() call is
+ // now also required, notifySlideEnded()
+ // relies on that
+ // unconditionally. Otherwise, genuine
+ // shape animations (drawing layer and
+ // GIF) will not be stopped.
+
+ bool bSkipAllMainSequenceEffects (false);
+ bool bSkipSlideTransition (false);
+ std::for_each( rProperties.getConstArray(),
+ rProperties.getConstArray() + rProperties.getLength(),
+ PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
+
+ OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
+ if (maViewContainer.empty())
+ return;
+
+ // this here might take some time
+ {
+ WaitSymbolLock aLock (*this);
+
+ mpPreviousSlide = mpCurrentSlide;
+ mpCurrentSlide.reset();
+
+ if (matches( mpPrefetchSlide, xSlide, xRootNode ))
+ {
+ // prefetched slide matches:
+ mpCurrentSlide = mpPrefetchSlide;
+ }
+ else
+ mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
+
+ OSL_ASSERT( mpCurrentSlide );
+ if (mpCurrentSlide)
+ {
+ basegfx::B2DSize oldSlideSize;
+ if( mpPreviousSlide )
+ oldSlideSize = mpPreviousSlide->getSlideSize();
+
+ basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
+
+ // push new transformation to all views, if size changed
+ if( !mpPreviousSlide || oldSlideSize != slideSize )
+ {
+ std::for_each( maViewContainer.begin(),
+ maViewContainer.end(),
+ boost::bind( &View::setViewSize, _1,
+ boost::cref(slideSize) ));
+
+ // explicitly notify view change here,
+ // because transformation might have changed:
+ // optimization, this->notifyViewChange() would
+ // repaint slide which is not necessary.
+ maEventMultiplexer.notifyViewsChanged();
+ }
+
+ // create slide transition, and add proper end event
+ // (which then starts the slide effects
+ // via CURRENT_SLIDE.show())
+ ActivitySharedPtr pSlideChangeActivity (
+ createSlideTransition(
+ mpCurrentSlide->getXDrawPage(),
+ mpPreviousSlide,
+ mpCurrentSlide,
+ makeEvent(
+ boost::bind(
+ &SlideShowImpl::notifySlideTransitionEnded,
+ this,
+ false ),
+ "SlideShowImpl::notifySlideTransitionEnded")));
+
+ if (bSkipSlideTransition)
+ {
+ // The transition activity was created for the side effects
+ // (like sound transitions). Because we want to skip the
+ // acutual transition animation we do not need the activity
+ // anymore.
+ pSlideChangeActivity.reset();
+ }
+
+ if (pSlideChangeActivity)
+ {
+ // factory generated a slide transition - activate it!
+ maActivitiesQueue.addActivity( pSlideChangeActivity );
+ }
+ else
+ {
+ // no transition effect on this slide - schedule slide
+ // effect start event right away.
+ maEventQueue.addEvent(
+ makeEvent(
+ boost::bind(
+ &SlideShowImpl::notifySlideTransitionEnded,
+ this,
+ true ),
+ "SlideShowImpl::notifySlideTransitionEnded"));
+ }
+ }
+ } // finally
+
+ maEventMultiplexer.notifySlideTransitionStarted();
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
+
+ // We are currently rewinding an effect. This lead us from the next
+ // slide to this one. To complete this we have to play back all main
+ // sequence effects on this slide.
+ if (bSkipAllMainSequenceEffects)
+ maEffectRewinder.skipAllMainSequenceEffects();
+}
+
+void SlideShowImpl::redisplayCurrentSlide (void)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+ stopShow();
+
+ OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
+ if (maViewContainer.empty())
+ return;
+
+ // No transition effect on this slide - schedule slide
+ // effect start event right away.
+ maEventQueue.addEvent(
+ makeEvent(
+ boost::bind(
+ &SlideShowImpl::notifySlideTransitionEnded,
+ this,
+ true ),
+ "SlideShowImpl::notifySlideTransitionEnded"));
+
+ maEventMultiplexer.notifySlideTransitionStarted();
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
+}
+
+sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ if (mbShowPaused)
+ return true;
+ else
+ return maEventMultiplexer.notifyNextEffect();
+}
+
+
+sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ if (mbShowPaused)
+ return true;
+ else
+ {
+ return maEffectRewinder.rewind(
+ maScreenUpdater.createLock(false),
+ ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::redisplayCurrentSlide), this),
+ ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::rewindEffectToPreviousSlide), this));
+ }
+}
+
+void SlideShowImpl::rewindEffectToPreviousSlide (void)
+{
+ // Show the wait symbol now and prevent it from showing temporary slide
+ // content while effects are played back.
+ WaitSymbolLock aLock (*this);
+
+ // A previous call to EffectRewinder::Rewind could not rewind the current
+ // effect because there are no effects on the current slide or none has
+ // yet been displayed. Go to the previous slide.
+ notifySlideEnded(true);
+
+ // Process pending events once more in order to have the following
+ // screen update show the last effect. Not sure whether this should be
+ // necessary.
+ maEventQueue.forceEmpty();
+
+ // We have to call the screen updater before the wait symbol is turned
+ // off. Otherwise the wait symbol would force the display of an
+ // intermediate state of the slide (before the effects are replayed.)
+ maScreenUpdater.commitUpdates();
+}
+
+sal_Bool SlideShowImpl::startShapeActivity(
+ uno::Reference<drawing::XShape> const& /*xShape*/ )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ // TODO(F3): NYI
+ OSL_ENSURE( false, "not yet implemented!" );
+ return false;
+}
+
+sal_Bool SlideShowImpl::stopShapeActivity(
+ uno::Reference<drawing::XShape> const& /*xShape*/ )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ // TODO(F3): NYI
+ OSL_ENSURE( false, "not yet implemented!" );
+ return false;
+}
+
+sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+
+ if (bPauseShow)
+ mpPresTimer->pauseTimer();
+ else
+ mpPresTimer->continueTimer();
+
+ maEventMultiplexer.notifyPauseMode(bPauseShow);
+
+ mbShowPaused = bPauseShow;
+ return true;
+}
+
+uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return uno::Reference<drawing::XDrawPage>();
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ if (mpCurrentSlide)
+ return mpCurrentSlide->getXDrawPage();
+ else
+ return uno::Reference<drawing::XDrawPage>();
+}
+
+sal_Bool SlideShowImpl::addView(
+ uno::Reference<presentation::XSlideShowView> const& xView )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ // first of all, check if view has a valid canvas
+ ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
+ ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(),
+ "addView(): View does not provide a valid canvas" );
+
+ UnoViewSharedPtr const pView( createSlideView(
+ xView,
+ maEventQueue,
+ maEventMultiplexer ));
+ if (!maViewContainer.addView( pView ))
+ return false; // view already added
+
+ // initialize view content
+ // =======================
+
+ if (mpCurrentSlide)
+ {
+ // set view transformation
+ const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
+ pView->setViewSize( basegfx::B2DSize( slideSize.getX(),
+ slideSize.getY() ) );
+ }
+
+ // clear view area (since its newly added,
+ // we need a clean slate)
+ pView->clearAll();
+
+ // broadcast newly added view
+ maEventMultiplexer.notifyViewAdded( pView );
+
+ // set current mouse ptr
+ pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
+
+ return true;
+}
+
+sal_Bool SlideShowImpl::removeView(
+ uno::Reference<presentation::XSlideShowView> const& xView )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
+
+ UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
+ if( !pView )
+ return false; // view was not added in the first place
+
+ // remove view from EventMultiplexer (mouse events etc.)
+ maEventMultiplexer.notifyViewRemoved( pView );
+
+ pView->_dispose();
+
+ return true;
+}
+
+void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory ) throw (uno::RuntimeException)
+{
+ //Retrieve Polygons if user ends presentation by context menu
+ if (mpCurrentSlide)
+ {
+ if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
+ maPolygons.erase(mpCurrentSlide->getXDrawPage());
+
+ maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
+ }
+
+ //Creating the layer for shapes
+ // query for the XLayerManager
+ uno::Reference< drawing::XLayerSupplier > xLayerSupplier(xDocFactory, uno::UNO_QUERY);
+ uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager();
+
+ uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY);
+ // create a layer and set its properties
+ uno::Reference< drawing::XLayer > xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
+ uno::Reference< beans::XPropertySet > xLayerPropSet(xDrawnInSlideshow, uno::UNO_QUERY);
+
+ //Layer Name which enables to catch annotations
+ rtl::OUString layerName = rtl::OUString::createFromAscii("DrawnInSlideshow");
+ uno::Any aPropLayer;
+
+ aPropLayer <<= layerName;
+ xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("Name"), aPropLayer);
+
+ aPropLayer <<= true;
+ xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("IsVisible"), aPropLayer);
+
+ aPropLayer <<= false;
+ xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("IsLocked"), aPropLayer);
+
+ PolygonMap::iterator aIter=maPolygons.begin();
+
+ PolyPolygonVector aPolygons;
+ ::cppcanvas::PolyPolygonSharedPtr pPolyPoly;
+ ::basegfx::B2DPolyPolygon b2DPolyPoly;
+
+ //Register polygons for each slide
+ while(aIter!=maPolygons.end())
+ {
+ aPolygons = aIter->second;
+ //Get shapes for the slide
+ ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes > Shapes(aIter->first, ::com::sun::star::uno::UNO_QUERY);
+ //Retrieve polygons for one slide
+ for( PolyPolygonVector::iterator aIterPoly=aPolygons.begin(),
+ aEnd=aPolygons.end();
+ aIterPoly!=aEnd; ++aIterPoly )
+ {
+ pPolyPoly = (*aIterPoly);
+ b2DPolyPoly = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(pPolyPoly->getUNOPolyPolygon());
+
+ //Normally there is only one polygon
+ for(sal_uInt32 i=0; i< b2DPolyPoly.count();i++)
+ {
+ const ::basegfx::B2DPolygon& aPoly = b2DPolyPoly.getB2DPolygon(i);
+ sal_uInt32 nPoints = aPoly.count();
+
+ if( nPoints > 1)
+ {
+ //create the PolyLineShape
+ uno::Reference< uno::XInterface > polyshape(xDocFactory->createInstance(
+ rtl::OUString::createFromAscii("com.sun.star.drawing.PolyLineShape") ) );
+ uno::Reference< drawing::XShape > rPolyShape(polyshape, uno::UNO_QUERY);
+
+ //Add the shape to the slide
+ Shapes->add(rPolyShape);
+
+ //Retrieve shape properties
+ uno::Reference< beans::XPropertySet > aXPropSet = uno::Reference< beans::XPropertySet >( rPolyShape, uno::UNO_QUERY );
+ //Construct a sequence of points sequence
+ drawing::PointSequenceSequence aRetval;
+ //Create only one sequence for one polygon
+ aRetval.realloc( 1 );
+ // Retrieve the sequence of points from aRetval
+ drawing::PointSequence* pOuterSequence = aRetval.getArray();
+ // Create 2 points in this sequence
+ pOuterSequence->realloc(nPoints);
+ // Get these points which are in an array
+ awt::Point* pInnerSequence = pOuterSequence->getArray();
+ for( sal_uInt32 n = 0; n < nPoints; n++ )
+ {
+ //Create a point from the polygon
+ *pInnerSequence++ = awt::Point( aPoly.getB2DPoint(n).getX(), aPoly.getB2DPoint(n).getY());
+ }
+
+ //Fill the properties
+ //Give the built PointSequenceSequence.
+ uno::Any aParam;
+ aParam <<= aRetval;
+ aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("PolyPolygon"), aParam );
+
+ //LineStyle : SOLID by default
+ uno::Any aAny;
+ drawing::LineStyle eLS;
+ eLS = drawing::LineStyle_SOLID;
+ aAny <<= eLS;
+ aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineStyle"), aAny );
+
+ //LineColor
+ sal_uInt32 nLineColor;
+ nLineColor = pPolyPoly->getRGBALineColor();
+ //Transform polygon color from RRGGBBAA to AARRGGBB
+ aAny <<= RGBAColor2UnoColor(nLineColor);
+ aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineColor"), aAny );
+
+ //LineWidth
+ double fLineWidth;
+ fLineWidth = pPolyPoly->getStrokeWidth();
+ aAny <<= (sal_Int32)fLineWidth;
+ aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineWidth"), aAny );
+
+ // make polygons special
+ xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
+ }
+ }
+ }
+ ++aIter;
+ }
+}
+
+sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("AutomaticAdvancement") ))
+ {
+ double nTimeout(0.0);
+ mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
+ if (mbAutomaticAdvancementMode)
+ {
+ maEventMultiplexer.setAutomaticTimeout( nTimeout );
+ }
+ maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("UserPaintColor") ))
+ {
+ sal_Int32 nColor(0);
+ if (rProperty.Value >>= nColor)
+ {
+ OSL_ENSURE( mbMouseVisible,
+ "setProperty(): User paint overrides invisible mouse" );
+
+ // enable user paint
+ maUserPaintColor.reset( unoColor2RGBColor( nColor ) );
+ maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
+ }
+ else
+ {
+ // disable user paint
+ maUserPaintColor.reset();
+ maEventMultiplexer.notifyUserPaintDisabled();
+ }
+
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+
+ return true;
+ }
+
+ //adding support for erasing features in UserPaintOverlay
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("EraseAllInk") ))
+ {
+ bool nEraseAllInk(false);
+ if (rProperty.Value >>= nEraseAllInk)
+ {
+ OSL_ENSURE( mbMouseVisible,
+ "setProperty(): User paint overrides invisible mouse" );
+
+ // enable user paint
+ maEraseAllInk.reset( nEraseAllInk );
+ maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
+ }
+ else
+ {
+ // disable user paint
+ maEraseAllInk.reset();
+ maEventMultiplexer.notifyUserPaintDisabled();
+ }
+
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("SwitchPenMode") ))
+ {
+ bool nSwitchPenMode(false);
+ if (rProperty.Value >>= nSwitchPenMode)
+ {
+ OSL_ENSURE( mbMouseVisible,
+ "setProperty(): User paint overrides invisible mouse" );
+
+ if(nSwitchPenMode == true){
+ // Switch to Pen Mode
+ maSwitchPenMode.reset( nSwitchPenMode );
+ maEventMultiplexer.notifySwitchPenMode();
+ }
+ }
+
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+ return true;
+ }
+
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("SwitchEraserMode") ))
+ {
+ bool nSwitchEraserMode(false);
+ if (rProperty.Value >>= nSwitchEraserMode)
+ {
+ OSL_ENSURE( mbMouseVisible,
+ "setProperty(): User paint overrides invisible mouse" );
+ if(nSwitchEraserMode == true){
+ // switch to Eraser mode
+ maSwitchEraserMode.reset( nSwitchEraserMode );
+ maEventMultiplexer.notifySwitchEraserMode();
+ }
+ }
+
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+ return true;
+ }
+
+
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("EraseInk") ))
+ {
+ sal_Int32 nEraseInk(100);
+ if (rProperty.Value >>= nEraseInk)
+ {
+ OSL_ENSURE( mbMouseVisible,
+ "setProperty(): User paint overrides invisible mouse" );
+
+ // enable user paint
+ maEraseInk.reset( nEraseInk );
+ maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
+ }
+ else
+ {
+ // disable user paint
+ maEraseInk.reset();
+ maEventMultiplexer.notifyUserPaintDisabled();
+ }
+
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+
+ return true;
+ }
+
+ // new Property for pen's width
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("UserPaintStrokeWidth") ))
+ {
+ double nWidth(4.0);
+ if (rProperty.Value >>= nWidth)
+ {
+ OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
+ // enable user paint stroke width
+ maUserPaintStrokeWidth.reset( nWidth );
+ maEventMultiplexer.notifyUserPaintStrokeWidth( *maUserPaintStrokeWidth );
+ }
+ else
+ {
+ // disable user paint stroke width
+ maUserPaintStrokeWidth.reset();
+ maEventMultiplexer.notifyUserPaintDisabled();
+ }
+ if( mnCurrentCursor == awt::SystemPointer::ARROW )
+ resetCursor();
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("AdvanceOnClick") ))
+ {
+ sal_Bool bAdvanceOnClick = sal_False;
+ if (! (rProperty.Value >>= bAdvanceOnClick))
+ return false;
+ maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("DisableAnimationZOrder") ))
+ {
+ sal_Bool bDisableAnimationZOrder = sal_False;
+ if (! (rProperty.Value >>= bDisableAnimationZOrder))
+ return false;
+ mbDisableAnimationZOrder = bDisableAnimationZOrder == sal_True;
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("ImageAnimationsAllowed") ) )
+ {
+ if (! (rProperty.Value >>= mbImageAnimationsAllowed))
+ return false;
+
+ // TODO(F3): Forward to slides!
+// if( bOldValue != mbImageAnimationsAllowed )
+// {
+// if( mbImageAnimationsAllowed )
+// maEventMultiplexer.notifyIntrinsicAnimationsEnabled();
+// else
+// maEventMultiplexer.notifyIntrinsicAnimationsDisabled();
+// }
+
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("MouseVisible") ))
+ {
+ if (! (rProperty.Value >>= mbMouseVisible))
+ return false;
+
+ requestCursor(mnCurrentCursor);
+
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("ForceManualAdvance") ))
+ {
+ return (rProperty.Value >>= mbForceManualAdvance);
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("RehearseTimings") ))
+ {
+ bool bRehearseTimings = false;
+ if (! (rProperty.Value >>= bRehearseTimings))
+ return false;
+
+ if (bRehearseTimings)
+ {
+ // TODO(Q3): Move to slide
+ mpRehearseTimingsActivity = RehearseTimingsActivity::create(
+ SlideShowContext(
+ mpDummyPtr,
+ maEventQueue,
+ maEventMultiplexer,
+ maScreenUpdater,
+ maActivitiesQueue,
+ maUserEventQueue,
+ *this,
+ maViewContainer,
+ mxComponentContext) );
+ }
+ else if (mpRehearseTimingsActivity)
+ {
+ // removes timer from all views:
+ mpRehearseTimingsActivity->dispose();
+ mpRehearseTimingsActivity.reset();
+ }
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("WaitSymbolBitmap") ))
+ {
+ uno::Reference<rendering::XBitmap> xBitmap;
+ if (! (rProperty.Value >>= xBitmap))
+ return false;
+
+ mpWaitSymbol = WaitSymbol::create( xBitmap,
+ maScreenUpdater,
+ maEventMultiplexer,
+ maViewContainer );
+
+ return true;
+ }
+
+ if (rProperty.Name.equalsAsciiL(
+ RTL_CONSTASCII_STRINGPARAM("NoSlideTransitions") ))
+ {
+ return (rProperty.Value >>= mbNoSlideTransitions);
+ }
+
+ if (rProperty.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsSoundEnabled")))
+ {
+ uno::Sequence<uno::Any> aValues;
+ uno::Reference<presentation::XSlideShowView> xView;
+ sal_Bool bValue (false);
+ if ((rProperty.Value >>= aValues)
+ && aValues.getLength()==2
+ && (aValues[0] >>= xView)
+ && (aValues[1] >>= bValue))
+ {
+ // Look up the view.
+ for (UnoViewVector::const_iterator
+ iView (maViewContainer.begin()),
+ iEnd (maViewContainer.end());
+ iView!=iEnd;
+ ++iView)
+ {
+ if (*iView && (*iView)->getUnoView()==xView)
+ {
+ // Store the flag at the view so that media shapes have
+ // access to it.
+ (*iView)->setIsSoundEnabled(bValue);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void SlideShowImpl::addSlideShowListener(
+ uno::Reference<presentation::XSlideShowListener> const& xListener )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return;
+
+ // container syncs with passed mutex ref
+ maListenerContainer.addInterface(xListener);
+}
+
+void SlideShowImpl::removeSlideShowListener(
+ uno::Reference<presentation::XSlideShowListener> const& xListener )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ // container syncs with passed mutex ref
+ maListenerContainer.removeInterface(xListener);
+}
+
+void SlideShowImpl::addShapeEventListener(
+ uno::Reference<presentation::XShapeEventListener> const& xListener,
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ ShapeEventListenerMap::iterator aIter;
+ if( (aIter=maShapeEventListeners.find( xShape )) ==
+ maShapeEventListeners.end() )
+ {
+ // no entry for this shape -> create one
+ aIter = maShapeEventListeners.insert(
+ ShapeEventListenerMap::value_type(
+ xShape,
+ boost::shared_ptr<cppu::OInterfaceContainerHelper>(
+ new cppu::OInterfaceContainerHelper(m_aMutex)))).first;
+ }
+
+ // add new listener to broadcaster
+ if( aIter->second.get() )
+ aIter->second->addInterface( xListener );
+
+ maEventMultiplexer.notifyShapeListenerAdded(xListener,
+ xShape);
+}
+
+void SlideShowImpl::removeShapeEventListener(
+ uno::Reference<presentation::XShapeEventListener> const& xListener,
+ uno::Reference<drawing::XShape> const& xShape )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ ShapeEventListenerMap::iterator aIter;
+ if( (aIter = maShapeEventListeners.find( xShape )) !=
+ maShapeEventListeners.end() )
+ {
+ // entry for this shape found -> remove listener from
+ // helper object
+ ENSURE_OR_THROW(
+ aIter->second.get(),
+ "SlideShowImpl::removeShapeEventListener(): "
+ "listener map contains NULL broadcast helper" );
+
+ aIter->second->removeInterface( xListener );
+ }
+
+ maEventMultiplexer.notifyShapeListenerRemoved(xListener,
+ xShape);
+}
+
+void SlideShowImpl::setShapeCursor(
+ uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return;
+
+ // precondition: must only be called from the main thread!
+ DBG_TESTSOLARMUTEX();
+
+ ShapeCursorMap::iterator aIter;
+ if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
+ {
+ // no entry for this shape -> create one
+ if( nPointerShape != awt::SystemPointer::ARROW )
+ {
+ // add new entry, unless shape shall display
+ // normal pointer arrow -> no need to handle that
+ // case
+ maShapeCursors.insert(
+ ShapeCursorMap::value_type(xShape,
+ nPointerShape) );
+ }
+ }
+ else if( nPointerShape == awt::SystemPointer::ARROW )
+ {
+ // shape shall display normal cursor -> can disable
+ // the cursor and clear the entry
+ maShapeCursors.erase( xShape );
+ }
+ else
+ {
+ // existing entry found, update with new cursor ID
+ aIter->second = nPointerShape;
+ }
+
+ maEventMultiplexer.notifyShapeCursorChange(xShape,
+ nPointerShape);
+}
+
+bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape )
+{
+ mnCurrentCursor = nCursorShape;
+
+ const sal_Int16 nActualCursor = calcActiveCursor(mnCurrentCursor);
+
+ // change all views to the requested cursor ID
+ std::for_each( maViewContainer.begin(),
+ maViewContainer.end(),
+ boost::bind( &View::setCursorShape,
+ _1,
+ nActualCursor ));
+
+ return nActualCursor==nCursorShape;
+}
+
+void SlideShowImpl::resetCursor()
+{
+ mnCurrentCursor = awt::SystemPointer::ARROW;
+
+ // change all views to the default cursor ID
+ std::for_each( maViewContainer.begin(),
+ maViewContainer.end(),
+ boost::bind( &View::setCursorShape,
+ _1,
+ calcActiveCursor(mnCurrentCursor) ));
+}
+
+sal_Bool SlideShowImpl::update( double & nNextTimeout )
+ throw (uno::RuntimeException)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ if (isDisposed())
+ return false;
+
+ // precondition: update() must only be called from the
+ // main thread!
+ DBG_TESTSOLARMUTEX();
+
+ if( mbShowPaused )
+ {
+ // commit frame (might be repaints pending)
+ maScreenUpdater.commitUpdates();
+
+ return false;
+ }
+ else
+ {
+ // TODO(F2): re-evaluate whether that timer lagging makes
+ // sense.
+
+ // hold timer, while processing the queues:
+ // 1. when there is more than one active activity this ensures the
+ // same time for all activities and events
+ // 2. processing of events may lead to creation of further events
+ // that have zero delay. While the timer is stopped these events
+ // are processed in the same run.
+ {
+ comphelper::ScopeGuard scopeGuard(
+ boost::bind( &canvas::tools::ElapsedTime::releaseTimer,
+ boost::cref(mpPresTimer) ) );
+ mpPresTimer->holdTimer();
+
+ // process queues
+ maEventQueue.process();
+ maActivitiesQueue.process();
+
+ // commit frame to screen
+ maFrameSynchronization.Synchronize();
+ maScreenUpdater.commitUpdates();
+
+ // TODO(Q3): remove need to call dequeued() from
+ // activities. feels like a wart.
+ //
+ // Rationale for ActivitiesQueue::processDequeued(): when
+ // an activity ends, it usually pushed the end state to
+ // the animated shape in question, and ends the animation
+ // (which, in turn, will usually disable shape sprite
+ // mode). Disabling shape sprite mode causes shape
+ // repaint, which, depending on slide content, takes
+ // considerably more time than sprite updates. Thus, the
+ // last animation step tends to look delayed. To
+ // camouflage this, reaching end position and disabling
+ // sprite mode is split into two (normal Activity::end(),
+ // and Activity::dequeued()). Now, the reason to call
+ // commitUpdates() twice here is caused by the unrelated
+ // fact that during wait cursor display/hide, the screen
+ // is updated, and shows hidden sprites, but, in case of
+ // leaving the second commitUpdates() call out and punting
+ // that to the next round, no updated static slide
+ // content. In short, the last shape animation of a slide
+ // tends to blink at its end.
+
+ // process dequeued activities _after_ commit to screen
+ maActivitiesQueue.processDequeued();
+
+ // commit frame to screen
+ maScreenUpdater.commitUpdates();
+ }
+ // Time held until here
+
+ const bool bActivitiesLeft = (! maActivitiesQueue.isEmpty());
+ const bool bTimerEventsLeft = (! maEventQueue.isEmpty());
+ const bool bRet = (bActivitiesLeft || bTimerEventsLeft);
+
+ if (bRet)
+ {
+ // calc nNextTimeout value:
+ if (bActivitiesLeft)
+ {
+ // Activity queue is not empty. Tell caller that we would
+ // like to render another frame.
+
+ // Return a zero time-out to signal our caller to call us
+ // back as soon as possible. The actual timing, waiting the
+ // appropriate amount of time between frames, is then done
+ // by the maFrameSynchronization object.
+ nNextTimeout = 0;
+ maFrameSynchronization.Activate();
+ }
+ else
+ {
+ // timer events left:
+ // difference from current time (nota bene:
+ // time no longer held here!) to the next event in
+ // the event queue.
+
+ // #i61190# Retrieve next timeout only _after_
+ // processing activity queue
+
+ // ensure positive value:
+ nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() );
+
+ // There is no active animation so the frame rate does not
+ // need to be synchronized.
+ maFrameSynchronization.Deactivate();
+ }
+
+ mbSlideShowIdle = false;
+ }
+
+#if defined(VERBOSE) && defined(DBG_UTIL)
+ // when slideshow is idle, issue an XUpdatable::update() call
+ // exactly once after a previous animation sequence finished -
+ // this might trigger screen dumps on some canvas
+ // implementations
+ if( !mbSlideShowIdle &&
+ (!bRet ||
+ nNextTimeout > 1.0) )
+ {
+ UnoViewVector::const_iterator aCurr(maViewContainer.begin());
+ const UnoViewVector::const_iterator aEnd(maViewContainer.end());
+ while( aCurr != aEnd )
+ {
+ try
+ {
+ uno::Reference< presentation::XSlideShowView > xView( (*aCurr)->getUnoView(),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< util::XUpdatable > xUpdatable( xView->getCanvas(),
+ uno::UNO_QUERY_THROW );
+ xUpdatable->update();
+ }
+ catch( uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch( uno::Exception& )
+ {
+ OSL_ENSURE( false,
+ rtl::OUStringToOString(
+ comphelper::anyToString( cppu::getCaughtException() ),
+ RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+
+ ++aCurr;
+ }
+
+ mbSlideShowIdle = true;
+ }
+#endif
+
+ return bRet;
+ }
+}
+
+void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide )
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ OSL_ENSURE( !isDisposed(), "### already disposed!" );
+ OSL_ENSURE( mpCurrentSlide,
+ "notifySlideTransitionEnded(): Invalid current slide" );
+ if (mpCurrentSlide)
+ {
+ // first init show, to give the animations
+ // the chance to register SlideStartEvents
+ const bool bBackgroundLayerRendered( !bPaintSlide );
+ mpCurrentSlide->show( bBackgroundLayerRendered );
+ maEventMultiplexer.notifySlideStartEvent();
+ }
+}
+
+void queryAutomaticSlideTransition( uno::Reference<drawing::XDrawPage> const& xDrawPage,
+ double& nAutomaticNextSlideTimeout,
+ bool& bHasAutomaticNextSlide )
+{
+ // retrieve slide change parameters from XDrawPage
+ // ===============================================
+
+ uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
+ uno::UNO_QUERY );
+
+ sal_Int32 nChange(0);
+ if( !xPropSet.is() ||
+ !getPropertyValue( nChange,
+ xPropSet,
+ ::rtl::OUString(
+ RTL_CONSTASCII_USTRINGPARAM("Change"))) )
+ {
+ OSL_TRACE(
+ "queryAutomaticSlideTransition(): "
+ "Could not extract slide change mode from XDrawPage - assuming <none>\n" );
+ }
+
+ bHasAutomaticNextSlide = nChange == 1;
+
+ if( !xPropSet.is() ||
+ !getPropertyValue( nAutomaticNextSlideTimeout,
+ xPropSet,
+ ::rtl::OUString(
+ RTL_CONSTASCII_USTRINGPARAM("Duration"))) )
+ {
+ OSL_TRACE(
+ "queryAutomaticSlideTransition(): "
+ "Could not extract slide transition timeout from "
+ "XDrawPage - assuming 1 sec\n" );
+ }
+}
+
+void SlideShowImpl::notifySlideAnimationsEnded()
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ //Draw polygons above animations
+ mpCurrentSlide->drawPolygons();
+
+ OSL_ENSURE( !isDisposed(), "### already disposed!" );
+
+ // This struct will receive the (interruptable) event,
+ // that triggers the notifySlideEnded() method.
+ InterruptableEventPair aNotificationEvents;
+
+ if( maEventMultiplexer.getAutomaticMode() )
+ {
+ OSL_ENSURE( ! mpRehearseTimingsActivity,
+ "unexpected: RehearseTimings mode!" );
+
+ // schedule a slide end event, with automatic mode's
+ // delay
+ aNotificationEvents = makeInterruptableDelay(
+ boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
+ maEventMultiplexer.getAutomaticTimeout() );
+ }
+ else
+ {
+ OSL_ENSURE( mpCurrentSlide,
+ "notifySlideAnimationsEnded(): Invalid current slide!" );
+
+ bool bHasAutomaticNextSlide=false;
+ double nAutomaticNextSlideTimeout=0.0;
+ queryAutomaticSlideTransition(mpCurrentSlide->getXDrawPage(),
+ nAutomaticNextSlideTimeout,
+ bHasAutomaticNextSlide);
+
+ // check whether slide transition should happen
+ // 'automatically'. If yes, simply schedule the
+ // specified timeout.
+ // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
+ // override any individual slide setting, to always
+ // step slides manually.
+ if( !mbForceManualAdvance &&
+ !mpRehearseTimingsActivity &&
+ bHasAutomaticNextSlide )
+ {
+ aNotificationEvents = makeInterruptableDelay(
+ boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
+ nAutomaticNextSlideTimeout);
+
+ // TODO(F2): Provide a mechanism to let the user override
+ // this automatic timeout via next()
+ }
+ else
+ {
+ if (mpRehearseTimingsActivity)
+ mpRehearseTimingsActivity->start();
+
+ // generate click event. Thus, the user must
+ // trigger the actual end of a slide. No need to
+ // generate interruptable event here, there's no
+ // timeout involved.
+ aNotificationEvents.mpImmediateEvent =
+ makeEvent( boost::bind<void>(
+ boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
+ "SlideShowImpl::notifySlideEnded");
+ }
+ }
+
+ // register events on the queues. To make automatic slide
+ // changes interruptable, register the interruption event
+ // as a nextEffectEvent target. Note that the timeout
+ // event is optional (e.g. manual slide changes don't
+ // generate a timeout)
+ maUserEventQueue.registerNextEffectEvent(
+ aNotificationEvents.mpImmediateEvent );
+
+ if( aNotificationEvents.mpTimeoutEvent )
+ maEventQueue.addEvent( aNotificationEvents.mpTimeoutEvent );
+
+ // current slide's main sequence is over. Now should be
+ // the time to prefetch the next slide (if any), and
+ // prepare the initial slide bitmap (speeds up slide
+ // change setup time a lot). Show the wait cursor, this
+ // indeed might take some seconds.
+ {
+ WaitSymbolLock aLock (*this);
+
+ if (! matches( mpPrefetchSlide,
+ mxPrefetchSlide, mxPrefetchAnimationNode ))
+ {
+ mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
+ mxPrefetchAnimationNode );
+ }
+ if (mpPrefetchSlide)
+ {
+ // ignore return value, this is just to populate
+ // Slide's internal bitmap buffer, such that the time
+ // needed to generate the slide bitmap is not spent
+ // when the slide change is requested.
+ mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
+ }
+ } // finally
+
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded ) );
+}
+
+void SlideShowImpl::notifySlideEnded (const bool bReverse)
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ OSL_ENSURE( !isDisposed(), "### already disposed!" );
+
+ if (mpRehearseTimingsActivity && !bReverse)
+ {
+ const double time = mpRehearseTimingsActivity->stop();
+ if (mpRehearseTimingsActivity->hasBeenClicked())
+ {
+ // save time at current drawpage:
+ uno::Reference<beans::XPropertySet> xPropSet(
+ mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
+ OSL_ASSERT( xPropSet.is() );
+ if (xPropSet.is())
+ {
+ xPropSet->setPropertyValue(
+ OUSTR("Change"),
+ uno::Any( static_cast<sal_Int32>(1) ) );
+ xPropSet->setPropertyValue(
+ OUSTR("Duration"),
+ uno::Any( static_cast<sal_Int32>(time) ) );
+ }
+ }
+ }
+
+ if (bReverse)
+ maEventMultiplexer.notifySlideEndEvent();
+
+ stopShow(); // MUST call that: results in
+ // maUserEventQueue.clear(). What's more,
+ // stopShow()'s currSlide->hide() call is
+ // now also required, notifySlideEnded()
+ // relies on that
+ // unconditionally. Otherwise, genuine
+ // shape animations (drawing layer and
+ // GIF) will not be stopped.
+
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::bind<void>(
+ ::boost::mem_fn(&presentation::XSlideShowListener::slideEnded),
+ _1,
+ sal_Bool(bReverse)));
+}
+
+bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString const& hyperLink )
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::bind( &presentation::XSlideShowListener::hyperLinkClicked,
+ _1,
+ boost::cref(hyperLink) ));
+ return true;
+}
+
+/** Notification from eventmultiplexer that an animation event has occoured.
+ This will be forewarded to all registered XSlideShoeListener
+ */
+bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
+{
+ osl::MutexGuard const guard( m_aMutex );
+
+ uno::Reference<animations::XAnimationNode> xNode( rNode->getXAnimationNode() );
+
+ switch( rNode->getState() )
+ {
+ case AnimationNode::ACTIVE:
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::bind( &animations::XAnimationListener::beginEvent,
+ _1,
+ boost::cref(xNode) ));
+ break;
+
+ case AnimationNode::FROZEN:
+ case AnimationNode::ENDED:
+ maListenerContainer.forEach<presentation::XSlideShowListener>(
+ boost::bind( &animations::XAnimationListener::endEvent,
+ _1,
+ boost::cref(xNode) ));
+ if(mpCurrentSlide->isPaintOverlayActive())
+ mpCurrentSlide->drawPolygons();
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+//===== FrameSynchronization ==================================================
+
+FrameSynchronization::FrameSynchronization (const double nFrameDuration)
+ : maTimer(),
+ mnFrameDuration(nFrameDuration),
+ mnNextFrameTargetTime(0),
+ mbIsActive(false)
+{
+ MarkCurrentFrame();
+}
+
+
+
+
+void FrameSynchronization::MarkCurrentFrame (void)
+{
+ mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
+}
+
+
+
+
+void FrameSynchronization::Synchronize (void)
+{
+ if (mbIsActive)
+ {
+ // Do busy waiting for now.
+ while (maTimer.getElapsedTime() < mnNextFrameTargetTime)
+ ;
+ }
+
+ MarkCurrentFrame();
+}
+
+
+
+
+void FrameSynchronization::Activate (void)
+{
+ mbIsActive = true;
+}
+
+
+
+
+void FrameSynchronization::Deactivate (void)
+{
+ mbIsActive = false;
+}
+
+
+
+
+double FrameSynchronization::GetCurrentTime (void) const
+{
+ return maTimer.getElapsedTime();
+}
+
+
+} // anon namespace
+
+namespace sdecl = comphelper::service_decl;
+#if defined (__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ <= 3)
+ sdecl::class_<SlideShowImpl> serviceImpl;
+ const sdecl::ServiceDecl slideShowDecl(
+ serviceImpl,
+#else
+ const sdecl::ServiceDecl slideShowDecl(
+ sdecl::class_<SlideShowImpl>(),
+#endif
+ "com.sun.star.comp.presentation.SlideShow",
+ "com.sun.star.presentation.SlideShow" );
+
+// The C shared lib entry points
+COMPHELPER_SERVICEDECL_EXPORTS1(slideShowDecl)
+