1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_slideshow.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 
34 #include <cppuhelper/basemutex.hxx>
35 #include <cppuhelper/compbase1.hxx>
36 #include <cppuhelper/factory.hxx>
37 #include <cppuhelper/implementationentry.hxx>
38 #include <cppuhelper/compbase2.hxx>
39 #include <cppuhelper/interfacecontainer.h>
40 #include <cppuhelper/exc_hlp.hxx>
41 
42 #include <comphelper/anytostring.hxx>
43 #include <comphelper/make_shared_from_uno.hxx>
44 #include <comphelper/scopeguard.hxx>
45 #include <comphelper/optional.hxx>
46 #include <comphelper/servicedecl.hxx>
47 #include <comphelper/namecontainer.hxx>
48 
49 #include <cppcanvas/spritecanvas.hxx>
50 #include <cppcanvas/vclfactory.hxx>
51 #include <cppcanvas/basegfxfactory.hxx>
52 
53 #include <tools/debug.hxx>
54 
55 #include <basegfx/point/b2dpoint.hxx>
56 #include <basegfx/polygon/b2dpolygon.hxx>
57 #include <basegfx/matrix/b2dhommatrix.hxx>
58 #include <basegfx/polygon/b2dpolygontools.hxx>
59 #include <basegfx/polygon/b2dpolypolygontools.hxx>
60 #include <basegfx/tools/canvastools.hxx>
61 
62 #include <vcl/font.hxx>
63 #include "rtl/ref.hxx"
64 
65 #include <com/sun/star/beans/XPropertySet.hpp>
66 #include <com/sun/star/util/XModifyListener.hpp>
67 #include <com/sun/star/util/XUpdatable.hpp>
68 #include <com/sun/star/awt/XPaintListener.hpp>
69 #include <com/sun/star/awt/SystemPointer.hpp>
70 #include <com/sun/star/animations/TransitionType.hpp>
71 #include <com/sun/star/animations/TransitionSubType.hpp>
72 #include <com/sun/star/presentation/XSlideShow.hpp>
73 #include <com/sun/star/presentation/XSlideShowListener.hpp>
74 #include <com/sun/star/lang/XServiceInfo.hpp>
75 #include <com/sun/star/lang/XServiceName.hpp>
76 #include <com/sun/star/lang/XComponent.hpp>
77 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
78 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
79 #include <com/sun/star/drawing/PointSequence.hpp>
80 #include <com/sun/star/drawing/XLayer.hpp>
81 #include <com/sun/star/drawing/XLayerSupplier.hpp>
82 #include <com/sun/star/drawing/XLayerManager.hpp>
83 #include <com/sun/star/container/XNameAccess.hpp>
84 
85 #include "com/sun/star/uno/Reference.hxx"
86 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
87 
88 #include "unoviewcontainer.hxx"
89 #include "transitionfactory.hxx"
90 #include "eventmultiplexer.hxx"
91 #include "usereventqueue.hxx"
92 #include "eventqueue.hxx"
93 #include "cursormanager.hxx"
94 #include "slideshowcontext.hxx"
95 #include "activitiesqueue.hxx"
96 #include "activitiesfactory.hxx"
97 #include "interruptabledelayevent.hxx"
98 #include "slide.hxx"
99 #include "shapemaps.hxx"
100 #include "slideview.hxx"
101 #include "tools.hxx"
102 #include "unoview.hxx"
103 #include "slidebitmap.hxx"
104 #include "rehearsetimingsactivity.hxx"
105 #include "waitsymbol.hxx"
106 #include "effectrewinder.hxx"
107 #include "framerate.hxx"
108 
109 #include <boost/noncopyable.hpp>
110 #include <boost/bind.hpp>
111 
112 #include <map>
113 #include <vector>
114 #include <iterator>
115 #include <string>
116 #include <algorithm>
117 #include <stdio.h>
118 #include <iostream>
119 
120 using namespace com::sun::star;
121 using namespace ::slideshow::internal;
122 
123 namespace {
124 
125 /** During animations the update() method tells its caller to call it as
126     soon as possible.  This gives us more time to render the next frame and
127     still maintain a steady frame rate.  This class is responsible for
128     synchronizing the display of new frames and thus keeping the frame rate
129     steady.
130 */
131 class FrameSynchronization
132 {
133 public:
134     /** Create new object with a predefined duration between two frames.
135         @param nFrameDuration
136             The preferred duration between the display of two frames in
137             seconds.
138     */
139     FrameSynchronization (const double nFrameDuration);
140 
141     /** Set the current time as the time at which the current frame is
142         displayed.  From this the target time of the next frame is derived.
143     */
144     void MarkCurrentFrame (void);
145 
146     /** When there is time left until the next frame is due then wait.
147         Otherwise return without delay.
148     */
149     void Synchronize (void);
150 
151     /** Activate frame synchronization when an animation is active and
152         frames are to be displayed in a steady rate.  While active
153         Synchronize() will wait until the frame duration time has passed.
154     */
155     void Activate (void);
156 
157     /** Deactivate frame sychronization when no animation is active and the
158         time between frames depends on user actions and other external
159         sources.  While deactivated Synchronize() will return without delay.
160     */
161     void Deactivate (void);
162 
163     /** Return the current time of the timer.  It is not synchronized with
164         any other timer so its absolute values are of no concern.  Typically
165         used during debugging to measure durations.
166     */
167     double GetCurrentTime (void) const;
168 
169 private:
170     /** The timer that is used for synchronization is independent from the
171         one used by SlideShowImpl: it is not paused or modified by
172         animations.
173     */
174     canvas::tools::ElapsedTime maTimer;
175     /** Time between the display of frames.  Enforced only when mbIsActive
176         is <TRUE/>.
177     */
178     const double mnFrameDuration;
179     /** Time (of maTimer) when the next frame shall be displayed.
180         Synchronize() will wait until this time.
181     */
182     double mnNextFrameTargetTime;
183     /** Synchronize() will wait only when this flag is <TRUE/>.  Otherwise
184         it returns immediately.
185     */
186     bool mbIsActive;
187 };
188 
189 
190 
191 
192 /******************************************************************************
193 
194    SlideShowImpl
195 
196    This class encapsulates the slideshow presentation viewer.
197 
198    With an instance of this class, it is possible to statically
199    and dynamically show a presentation, as defined by the
200    constructor-provided draw model (represented by a sequence
201    of ::com::sun::star::drawing::XDrawPage objects).
202 
203    It is possible to show the presentation on multiple views
204    simultaneously (e.g. for a multi-monitor setup). Since this
205    class also relies on user interaction, the corresponding
206    XSlideShowView interface provides means to register some UI
207    event listeners (mostly borrowed from awt::XWindow interface).
208 
209    Since currently (mid 2004), OOo isn't very well suited to
210    multi-threaded rendering, this class relies on <em>very
211    frequent</em> external update() calls, which will render the
212    next frame of animations. This works as follows: after the
213    displaySlide() has been successfully called (which setup and
214    starts an actual slide show), the update() method must be
215    called until it returns false.
216    Effectively, this puts the burden of providing
217    concurrency to the clients of this class, which, as noted
218    above, is currently unavoidable with the current state of
219    affairs (I've actually tried threading here, but failed
220    miserably when using the VCL canvas as the render backend -
221    deadlocked).
222 
223  ******************************************************************************/
224 
225 typedef cppu::WeakComponentImplHelper1<presentation::XSlideShow> SlideShowImplBase;
226 
227 typedef ::std::vector< ::cppcanvas::PolyPolygonSharedPtr> PolyPolygonVector;
228 
229 /// Maps XDrawPage for annotations persistence
230 typedef ::std::map< ::com::sun::star::uno::Reference<
231                                     ::com::sun::star::drawing::XDrawPage>,
232                                     PolyPolygonVector>  PolygonMap;
233 
234 class SlideShowImpl : private cppu::BaseMutex,
235                       public CursorManager,
236                       public SlideShowImplBase
237 {
238 public:
239     explicit SlideShowImpl(
240         uno::Reference<uno::XComponentContext> const& xContext );
241 
242     /** Notify that the transition phase of the current slide
243         has ended.
244 
245         The life of a slide has three phases: the transition
246         phase, when the previous slide vanishes, and the
247         current slide becomes visible, the shape animation
248         phase, when shape effects are running, and the phase
249         after the last shape animation has ended, but before
250         the next slide transition starts.
251 
252         This method notifies the end of the first phase.
253 
254         @param bPaintSlide
255         When true, Slide::show() is passed a true as well, denoting
256         explicit paint of slide content. Pass false here, if e.g. a
257         slide transition has already rendered the initial slide image.
258     */
259     void notifySlideTransitionEnded( bool bPaintSlide );
260 
261     /** Notify that the shape animation phase of the current slide
262         has ended.
263 
264         The life of a slide has three phases: the transition
265         phase, when the previous slide vanishes, and the
266         current slide becomes visible, the shape animation
267         phase, when shape effects are running, and the phase
268         after the last shape animation has ended, but before
269         the next slide transition starts.
270 
271         This method notifies the end of the second phase.
272     */
273     void notifySlideAnimationsEnded();
274 
275     /** Notify that the slide has ended.
276 
277         The life of a slide has three phases: the transition
278         phase, when the previous slide vanishes, and the
279         current slide becomes visible, the shape animation
280         phase, when shape effects are running, and the phase
281         after the last shape animation has ended, but before
282         the next slide transition starts.
283 
284         This method notifies the end of the third phase.
285     */
286     void notifySlideEnded (const bool bReverse);
287 
288     /** Notification from eventmultiplexer that a hyperlink
289         has been clicked.
290     */
291     bool notifyHyperLinkClicked( rtl::OUString const& hyperLink );
292 
293     /** Notification from eventmultiplexer that an animation event has occoured.
294 		This will be forewarded to all registered XSlideShowListener
295      */
296 	bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode );
297 
298 private:
299     // XSlideShow:
300     virtual sal_Bool SAL_CALL nextEffect() throw (uno::RuntimeException);
301     virtual sal_Bool SAL_CALL previousEffect() throw (uno::RuntimeException);
302     virtual sal_Bool SAL_CALL startShapeActivity(
303         uno::Reference<drawing::XShape> const& xShape )
304         throw (uno::RuntimeException);
305     virtual sal_Bool SAL_CALL stopShapeActivity(
306         uno::Reference<drawing::XShape> const& xShape )
307         throw (uno::RuntimeException);
308     virtual sal_Bool SAL_CALL pause( sal_Bool bPauseShow )
309         throw (uno::RuntimeException);
310     virtual uno::Reference<drawing::XDrawPage> SAL_CALL getCurrentSlide()
311         throw (uno::RuntimeException);
312     virtual void SAL_CALL displaySlide(
313         uno::Reference<drawing::XDrawPage> const& xSlide,
314         uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
315         uno::Reference<animations::XAnimationNode> const& xRootNode,
316         uno::Sequence<beans::PropertyValue> const& rProperties )
317         throw (uno::RuntimeException);
318     virtual void SAL_CALL registerUserPaintPolygons( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xDocFactory ) throw (::com::sun::star::uno::RuntimeException);
319     virtual sal_Bool SAL_CALL setProperty(
320         beans::PropertyValue const& rProperty ) throw (uno::RuntimeException);
321     virtual sal_Bool SAL_CALL addView(
322         uno::Reference<presentation::XSlideShowView> const& xView )
323         throw (uno::RuntimeException);
324     virtual sal_Bool SAL_CALL removeView(
325         uno::Reference<presentation::XSlideShowView> const& xView )
326         throw (uno::RuntimeException);
327     virtual sal_Bool SAL_CALL update( double & nNextTimeout )
328         throw (uno::RuntimeException);
329     virtual void SAL_CALL addSlideShowListener(
330         uno::Reference<presentation::XSlideShowListener> const& xListener )
331         throw (uno::RuntimeException);
332     virtual void SAL_CALL removeSlideShowListener(
333         uno::Reference<presentation::XSlideShowListener> const& xListener )
334         throw (uno::RuntimeException);
335     virtual void SAL_CALL addShapeEventListener(
336         uno::Reference<presentation::XShapeEventListener> const& xListener,
337         uno::Reference<drawing::XShape> const& xShape )
338         throw (uno::RuntimeException);
339     virtual void SAL_CALL removeShapeEventListener(
340         uno::Reference<presentation::XShapeEventListener> const& xListener,
341         uno::Reference<drawing::XShape> const& xShape )
342         throw (uno::RuntimeException);
343     virtual void SAL_CALL setShapeCursor(
344         uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
345         throw (uno::RuntimeException);
346 
347 
348     // CursorManager
349     // -----------------------------------------------------------
350 
351     virtual bool requestCursor( sal_Int16 nCursorShape );
352     virtual void resetCursor();
353 
354     /** This is somewhat similar to displaySlide when called for the current
355         slide.  It has been simplified to take advantage of that no slide
356         change takes place.  Furthermore it does not show the slide
357         transition.
358     */
359     void redisplayCurrentSlide (void);
360 
361 protected:
362     // WeakComponentImplHelperBase
363     virtual void SAL_CALL disposing();
364 
365     bool isDisposed() const
366     {
367         return (rBHelper.bDisposed || rBHelper.bInDispose);
368     }
369 
370 private:
371     struct SeparateListenerImpl; friend struct SeparateListenerImpl;
372     struct PrefetchPropertiesFunc; friend struct PrefetchPropertiesFunc;
373 
374     /// Stop currently running show.
375     void stopShow();
376 
377     ///Find a polygons vector in maPolygons (map)
378     PolygonMap::iterator findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage);
379 
380     /// Creates a new slide.
381     SlideSharedPtr makeSlide(
382         uno::Reference<drawing::XDrawPage> const& xDrawPage,
383         uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
384         uno::Reference<animations::XAnimationNode> const& xRootNode );
385 
386     /// Checks whether the given slide/animation node matches mpPrefetchSlide
387     static bool matches(
388         SlideSharedPtr const& pSlide,
389         uno::Reference<drawing::XDrawPage> const& xSlide,
390         uno::Reference<animations::XAnimationNode> const& xNode )
391     {
392         if (pSlide)
393             return (pSlide->getXDrawPage() == xSlide &&
394                     pSlide->getXAnimationNode() == xNode);
395         else
396             return (!xSlide.is() && !xNode.is());
397     }
398 
399     /// Resets the current slide transition sound object with a new one:
400     SoundPlayerSharedPtr resetSlideTransitionSound(
401 		uno::Any const& url = uno::Any(), bool bLoopSound = false );
402 
403 	/// stops the current slide transition sound
404 	void stopSlideTransitionSound();
405 
406     /** Prepare a slide transition
407 
408         This method registers all necessary events and
409         activities for a slide transition.
410 
411         @return the slide change activity, or NULL for no transition effect
412     */
413     ActivitySharedPtr createSlideTransition(
414         const uno::Reference< drawing::XDrawPage >&    xDrawPage,
415         const SlideSharedPtr&                          rLeavingSlide,
416         const SlideSharedPtr&                          rEnteringSlide,
417         const EventSharedPtr&                          rTransitionEndEvent );
418 
419     /** Request/release the wait symbol.  The wait symbol is displayed when
420         there are more requests then releases.  Locking the wait symbol
421         helps to avoid intermediate repaints.
422 
423         Do not call this method directly.  Use WaitSymbolLock instead.
424     */
425     void requestWaitSymbol (void);
426     void releaseWaitSymbol (void);
427 
428     class WaitSymbolLock {public:
429         WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
430             { mrSlideShowImpl.requestWaitSymbol(); }
431         ~WaitSymbolLock(void)
432             { mrSlideShowImpl.releaseWaitSymbol(); }
433     private: SlideShowImpl& mrSlideShowImpl;
434     };
435 
436 
437     /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
438     sal_Int16 calcActiveCursor( sal_Int16 nCursorShape ) const;
439 
440     /** This method is called asynchronously to finish the rewinding of an
441         effect to the previous slide that was initiated earlier.
442     */
443     void rewindEffectToPreviousSlide (void);
444 
445     /// all registered views
446     UnoViewContainer                        maViewContainer;
447 
448     /// all registered slide show listeners
449     cppu::OInterfaceContainerHelper         maListenerContainer;
450 
451     /// map of vectors, containing all registered listeners for a shape
452     ShapeEventListenerMap                   maShapeEventListeners;
453     /// map of sal_Int16 values, specifying the mouse cursor for every shape
454     ShapeCursorMap                          maShapeCursors;
455 
456     //map of vector of Polygons, containing polygons drawn on each slide.
457     PolygonMap                              maPolygons;
458 
459     boost::optional<RGBColor>               maUserPaintColor;
460 
461 	double									maUserPaintStrokeWidth;
462 
463     //changed for the eraser project
464     boost::optional<bool>		    maEraseAllInk;
465     boost::optional<bool>		    maSwitchPenMode;
466     boost::optional<bool>		    maSwitchEraserMode;
467     boost::optional<sal_Int32>		    maEraseInk;
468     //end changed
469 
470     boost::shared_ptr<canvas::tools::ElapsedTime> mpPresTimer;
471     ScreenUpdater                           maScreenUpdater;
472     EventQueue                              maEventQueue;
473     EventMultiplexer                        maEventMultiplexer;
474     ActivitiesQueue                         maActivitiesQueue;
475     UserEventQueue                          maUserEventQueue;
476     SubsettableShapeManagerSharedPtr        mpDummyPtr;
477 
478     boost::shared_ptr<SeparateListenerImpl> mpListener;
479 
480     boost::shared_ptr<RehearseTimingsActivity> mpRehearseTimingsActivity;
481     boost::shared_ptr<WaitSymbol>           mpWaitSymbol;
482 
483     /// the current slide transition sound object:
484     SoundPlayerSharedPtr                    mpCurrentSlideTransitionSound;
485 
486     uno::Reference<uno::XComponentContext>  mxComponentContext;
487     uno::Reference<
488         presentation::XTransitionFactory>   mxOptionalTransitionFactory;
489 
490     /// the previously running slide
491     SlideSharedPtr                          mpPreviousSlide;
492     /// the currently running slide
493     SlideSharedPtr                          mpCurrentSlide;
494     /// the already prefetched slide: best candidate for upcoming slide
495     SlideSharedPtr                          mpPrefetchSlide;
496     /// slide to be prefetched: best candidate for upcoming slide
497     uno::Reference<drawing::XDrawPage>      mxPrefetchSlide;
498     ///  save the XDrawPagesSupplier to retieve polygons
499     uno::Reference<drawing::XDrawPagesSupplier>  mxDrawPagesSupplier;
500     /// slide animation to be prefetched:
501     uno::Reference<animations::XAnimationNode> mxPrefetchAnimationNode;
502 
503     sal_Int16                               mnCurrentCursor;
504 
505     sal_Int32                               mnWaitSymbolRequestCount;
506     bool                                    mbAutomaticAdvancementMode;
507     bool                                    mbImageAnimationsAllowed;
508     bool                                    mbNoSlideTransitions;
509     bool                                    mbMouseVisible;
510     bool                                    mbForceManualAdvance;
511     bool                                    mbShowPaused;
512     bool                                    mbSlideShowIdle;
513     bool                                    mbDisableAnimationZOrder;
514 
515     EffectRewinder                          maEffectRewinder;
516     FrameSynchronization                    maFrameSynchronization;
517 };
518 
519 
520 /** Separate event listener for animation, view and hyperlink events.
521 
522     This handler is registered for slide animation end, view and
523     hyperlink events at the global EventMultiplexer, and forwards
524     notifications to the SlideShowImpl
525 */
526 struct SlideShowImpl::SeparateListenerImpl : public EventHandler,
527                                              public ViewRepaintHandler,
528                                              public HyperlinkHandler,
529 											 public AnimationEventHandler,
530                                              private boost::noncopyable
531 {
532     SlideShowImpl& mrShow;
533     ScreenUpdater& mrScreenUpdater;
534     EventQueue&    mrEventQueue;
535 
536     SeparateListenerImpl( SlideShowImpl& rShow,
537                           ScreenUpdater& rScreenUpdater,
538                           EventQueue&    rEventQueue ) :
539         mrShow( rShow ),
540         mrScreenUpdater( rScreenUpdater ),
541         mrEventQueue( rEventQueue )
542     {}
543 
544     // EventHandler
545     virtual bool handleEvent()
546     {
547         // DON't call notifySlideAnimationsEnded()
548         // directly, but queue an event. handleEvent()
549         // might be called from e.g.
550         // showNext(), and notifySlideAnimationsEnded() must not be called
551         // in recursion.  Note that the event is scheduled for the next
552         // frame so that its expensive execution does not come in between
553         // sprite hiding and shape redraw (at the end of the animation of a
554         // shape), which would cause a flicker.
555         mrEventQueue.addEventForNextRound(
556             makeEvent(
557                 boost::bind( &SlideShowImpl::notifySlideAnimationsEnded, boost::ref(mrShow) ),
558                 "SlideShowImpl::notifySlideAnimationsEnded"));
559         return true;
560     }
561 
562     // ViewRepaintHandler
563     virtual void viewClobbered( const UnoViewSharedPtr& rView )
564     {
565         // given view needs repaint, request update
566         mrScreenUpdater.notifyUpdate(rView, true);
567     }
568 
569     // HyperlinkHandler
570     virtual bool handleHyperlink( ::rtl::OUString const& rLink )
571     {
572         return mrShow.notifyHyperLinkClicked(rLink);
573     }
574 
575 	// AnimationEventHandler
576     virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
577 	{
578 		return mrShow.handleAnimationEvent(rNode);
579 	}
580 };
581 
582 
583 SlideShowImpl::SlideShowImpl(
584     uno::Reference<uno::XComponentContext> const& xContext )
585     : SlideShowImplBase(m_aMutex),
586       maViewContainer(),
587       maListenerContainer( m_aMutex ),
588       maShapeEventListeners(),
589       maShapeCursors(),
590       maUserPaintColor(),
591       maUserPaintStrokeWidth(4.0),
592       mpPresTimer( new canvas::tools::ElapsedTime ),
593       maScreenUpdater(maViewContainer),
594       maEventQueue( mpPresTimer ),
595       maEventMultiplexer( maEventQueue,
596                           maViewContainer ),
597       maActivitiesQueue( mpPresTimer ),
598       maUserEventQueue( maEventMultiplexer,
599                         maEventQueue,
600                         *this ),
601       mpDummyPtr(),
602       mpListener(),
603       mpRehearseTimingsActivity(),
604       mpWaitSymbol(),
605       mpCurrentSlideTransitionSound(),
606       mxComponentContext( xContext ),
607       mxOptionalTransitionFactory(),
608       mpCurrentSlide(),
609       mpPrefetchSlide(),
610       mxPrefetchSlide(),
611       mxDrawPagesSupplier(),
612       mxPrefetchAnimationNode(),
613       mnCurrentCursor(awt::SystemPointer::ARROW),
614       mnWaitSymbolRequestCount(0),
615       mbAutomaticAdvancementMode(false),
616       mbImageAnimationsAllowed( true ),
617       mbNoSlideTransitions( false ),
618       mbMouseVisible( true ),
619       mbForceManualAdvance( false ),
620       mbShowPaused( false ),
621       mbSlideShowIdle( true ),
622       mbDisableAnimationZOrder( false ),
623       maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue),
624       maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond)
625 
626 {
627     // keep care not constructing any UNO references to this inside ctor,
628     // shift that code to create()!
629 
630     uno::Reference<lang::XMultiComponentFactory> xFactory(
631         mxComponentContext->getServiceManager() );
632 
633     if( xFactory.is() )
634     {
635         try
636 	{
637             // #i82460# try to retrieve special transition factory
638             mxOptionalTransitionFactory.set(
639                 xFactory->createInstanceWithContext(
640                     ::rtl::OUString::createFromAscii( "com.sun.star.presentation.TransitionFactory" ),
641                     mxComponentContext ),
642                 uno::UNO_QUERY );
643         }
644         catch (loader::CannotActivateFactoryException const&)
645 	{
646 	}
647     }
648 
649     mpListener.reset( new SeparateListenerImpl(
650                           *this,
651                           maScreenUpdater,
652                           maEventQueue ));
653     maEventMultiplexer.addSlideAnimationsEndHandler( mpListener );
654     maEventMultiplexer.addViewRepaintHandler( mpListener );
655     maEventMultiplexer.addHyperlinkHandler( mpListener, 0.0 );
656 	maEventMultiplexer.addAnimationStartHandler( mpListener );
657 	maEventMultiplexer.addAnimationEndHandler( mpListener );
658 }
659 
660 // we are about to be disposed (someone call dispose() on us)
661 void SlideShowImpl::disposing()
662 {
663     osl::MutexGuard const guard( m_aMutex );
664 
665     maEffectRewinder.dispose();
666 
667 	// stop slide transition sound, if any:
668     stopSlideTransitionSound();
669 
670     mxComponentContext.clear();
671 
672     if( mpCurrentSlideTransitionSound )
673     {
674         mpCurrentSlideTransitionSound->dispose();
675         mpCurrentSlideTransitionSound.reset();
676     }
677 
678     mpWaitSymbol.reset();
679 
680     if( mpRehearseTimingsActivity )
681     {
682         mpRehearseTimingsActivity->dispose();
683         mpRehearseTimingsActivity.reset();
684     }
685 
686     if( mpListener )
687     {
688         maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
689         maEventMultiplexer.removeViewRepaintHandler(mpListener);
690         maEventMultiplexer.removeHyperlinkHandler(mpListener);
691 		maEventMultiplexer.removeAnimationStartHandler( mpListener );
692 		maEventMultiplexer.removeAnimationEndHandler( mpListener );
693 
694         mpListener.reset();
695     }
696 
697     maUserEventQueue.clear();
698     maActivitiesQueue.clear();
699     maEventMultiplexer.clear();
700     maEventQueue.clear();
701     mpPresTimer.reset();
702     maShapeCursors.clear();
703     maShapeEventListeners.clear();
704 
705     // send all listeners a disposing() that we are going down:
706     maListenerContainer.disposeAndClear(
707         lang::EventObject( static_cast<cppu::OWeakObject *>(this) ) );
708 
709     maViewContainer.dispose();
710 
711     // release slides:
712     mxPrefetchAnimationNode.clear();
713     mxPrefetchSlide.clear();
714     mpPrefetchSlide.reset();
715     mpCurrentSlide.reset();
716     mpPreviousSlide.reset();
717 }
718 
719 /// stops the current slide transition sound
720 void SlideShowImpl::stopSlideTransitionSound()
721 {
722     if (mpCurrentSlideTransitionSound)
723     {
724         mpCurrentSlideTransitionSound->stopPlayback();
725         mpCurrentSlideTransitionSound->dispose();
726         mpCurrentSlideTransitionSound.reset();
727     }
728  }
729 
730 SoundPlayerSharedPtr SlideShowImpl::resetSlideTransitionSound( const uno::Any& rSound, bool bLoopSound )
731 {
732 	sal_Bool bStopSound = sal_False;
733 	rtl::OUString url;
734 
735 	if( !(rSound >>= bStopSound) )
736 		bStopSound = sal_False;
737 	rSound >>= url;
738 
739 	if( !bStopSound && (url.getLength() == 0) )
740 		return SoundPlayerSharedPtr();
741 
742 	stopSlideTransitionSound();
743 
744     if (url.getLength() > 0)
745     {
746         try
747         {
748             mpCurrentSlideTransitionSound = SoundPlayer::create(
749                 maEventMultiplexer, url, mxComponentContext );
750 			mpCurrentSlideTransitionSound->setPlaybackLoop( bLoopSound );
751         }
752         catch (lang::NoSupportException const&)
753         {
754             // catch possible exceptions from SoundPlayer, since
755             // being not able to playback the sound is not a hard
756             // error here (still, the slide transition should be
757             // shown).
758         }
759     }
760     return mpCurrentSlideTransitionSound;
761 }
762 
763 ActivitySharedPtr SlideShowImpl::createSlideTransition(
764     const uno::Reference< drawing::XDrawPage >& xDrawPage,
765     const SlideSharedPtr&                       rLeavingSlide,
766     const SlideSharedPtr&                       rEnteringSlide,
767     const EventSharedPtr&                       rTransitionEndEvent)
768 {
769     ENSURE_OR_THROW( !maViewContainer.empty(),
770                       "createSlideTransition(): No views" );
771     ENSURE_OR_THROW( rEnteringSlide,
772                       "createSlideTransition(): No entering slide" );
773 
774     // return empty transition, if slide transitions
775     // are disabled.
776     if (mbNoSlideTransitions)
777         return ActivitySharedPtr();
778 
779     // retrieve slide change parameters from XDrawPage
780     uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
781                                                     uno::UNO_QUERY );
782 
783     if( !xPropSet.is() )
784     {
785         OSL_TRACE( "createSlideTransition(): "
786                    "Slide has no PropertySet - assuming no transition\n" );
787         return ActivitySharedPtr();
788     }
789 
790     sal_Int16 nTransitionType(0);
791     if( !getPropertyValue( nTransitionType,
792                            xPropSet,
793                            OUSTR("TransitionType" )) )
794     {
795         OSL_TRACE( "createSlideTransition(): "
796                    "Could not extract slide transition type from XDrawPage - assuming no transition\n" );
797         return ActivitySharedPtr();
798     }
799 
800     sal_Int16 nTransitionSubType(0);
801     if( !getPropertyValue( nTransitionSubType,
802                            xPropSet,
803                            OUSTR("TransitionSubtype" )) )
804     {
805         OSL_TRACE( "createSlideTransition(): "
806                    "Could not extract slide transition subtype from XDrawPage - assuming no transition\n" );
807         return ActivitySharedPtr();
808     }
809 
810     bool bTransitionDirection(false);
811     if( !getPropertyValue( bTransitionDirection,
812                            xPropSet,
813                            OUSTR("TransitionDirection")) )
814     {
815         OSL_TRACE( "createSlideTransition(): "
816                    "Could not extract slide transition direction from XDrawPage - assuming default direction\n" );
817     }
818 
819     sal_Int32 aUnoColor(0);
820     if( !getPropertyValue( aUnoColor,
821                            xPropSet,
822                            OUSTR("TransitionFadeColor")) )
823     {
824         OSL_TRACE( "createSlideTransition(): "
825                    "Could not extract slide transition fade color from XDrawPage - assuming black\n" );
826     }
827 
828     const RGBColor aTransitionFadeColor( unoColor2RGBColor( aUnoColor ));
829 
830 	uno::Any aSound;
831 	sal_Bool bLoopSound = sal_False;
832 
833 	if( !getPropertyValue( aSound, xPropSet, OUSTR("Sound")) )
834 		OSL_TRACE( "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound\n" );
835 
836 	if( !getPropertyValue( bLoopSound, xPropSet, OUSTR("LoopSound") ) )
837 		OSL_TRACE( "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound\n" );
838 
839     NumberAnimationSharedPtr pTransition(
840         TransitionFactory::createSlideTransition(
841             rLeavingSlide,
842             rEnteringSlide,
843             maViewContainer,
844             maScreenUpdater,
845             maEventMultiplexer,
846             mxOptionalTransitionFactory,
847             nTransitionType,
848             nTransitionSubType,
849             bTransitionDirection,
850             aTransitionFadeColor,
851             resetSlideTransitionSound( aSound, bLoopSound ) ));
852 
853     if( !pTransition )
854         return ActivitySharedPtr(); // no transition effect has been
855                                     // generated. Normally, that means
856                                     // that simply no transition is
857                                     // set on this slide.
858 
859     double nTransitionDuration(0.0);
860     if( !getPropertyValue( nTransitionDuration,
861                            xPropSet,
862                            OUSTR("TransitionDuration")) )
863     {
864         OSL_TRACE( "createSlideTransition(): "
865                    "Could not extract slide transition duration from XDrawPage - assuming no transition\n" );
866         return ActivitySharedPtr();
867     }
868 
869     sal_Int32 nMinFrames(5);
870     if( !getPropertyValue( nMinFrames,
871                            xPropSet,
872                            OUSTR("MinimalFrameNumber")) )
873     {
874         OSL_TRACE( "createSlideTransition(): "
875                    "No minimal number of frames given - assuming 5\n" );
876     }
877 
878     // prefetch slide transition bitmaps, but postpone it after
879     // displaySlide() has finished - sometimes, view size has not yet
880     // reached final size
881     maEventQueue.addEvent(
882         makeEvent(
883             boost::bind(
884                 &::slideshow::internal::Animation::prefetch,
885                 pTransition,
886                 AnimatableShapeSharedPtr(),
887                 ShapeAttributeLayerSharedPtr()),
888             "Animation::prefetch"));
889 
890     return ActivitySharedPtr(
891         ActivitiesFactory::createSimpleActivity(
892             ActivitiesFactory::CommonParameters(
893                 rTransitionEndEvent,
894                 maEventQueue,
895                 maActivitiesQueue,
896                 nTransitionDuration,
897                 nMinFrames,
898                 false,
899                 boost::optional<double>(1.0),
900                 0.0,
901                 0.0,
902                 ShapeSharedPtr(),
903                 rEnteringSlide->getSlideSize() ),
904             pTransition,
905             true ));
906 }
907 
908 PolygonMap::iterator SlideShowImpl::findPolygons( uno::Reference<drawing::XDrawPage> const& xDrawPage)
909 {
910     // TODO(P2) : Optimze research in the map.
911     bool bFound = false;
912     PolygonMap::iterator aIter=maPolygons.begin();
913 
914 
915     while(aIter!=maPolygons.end() && !bFound)
916     {
917         if(aIter->first == xDrawPage)
918             bFound = true;
919         else
920             aIter++;
921     }
922 
923     return aIter;
924 }
925 
926 SlideSharedPtr SlideShowImpl::makeSlide(
927     uno::Reference<drawing::XDrawPage> const&          xDrawPage,
928     uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
929     uno::Reference<animations::XAnimationNode> const&  xRootNode )
930 {
931     if( !xDrawPage.is() )
932         return SlideSharedPtr();
933 
934     //Retrieve polygons for the current slide
935     PolygonMap::iterator aIter;
936     aIter = findPolygons(xDrawPage);
937 
938     const SlideSharedPtr pSlide( createSlide(xDrawPage,
939                                              xDrawPages,
940                                              xRootNode,
941                                              maEventQueue,
942                                              maEventMultiplexer,
943                                              maScreenUpdater,
944                                              maActivitiesQueue,
945                                              maUserEventQueue,
946                                              *this,
947                                              maViewContainer,
948                                              mxComponentContext,
949                                              maShapeEventListeners,
950                                              maShapeCursors,
951                                              (aIter != maPolygons.end()) ? aIter->second :  PolyPolygonVector(),
952                                              maUserPaintColor ? *maUserPaintColor : RGBColor(),
953 											 maUserPaintStrokeWidth,
954                                              !!maUserPaintColor,
955                                              mbImageAnimationsAllowed,
956                                              mbDisableAnimationZOrder) );
957 
958     // prefetch show content (reducing latency for slide
959     // bitmap and effect start later on)
960     pSlide->prefetch();
961 
962     return pSlide;
963 }
964 
965 void SlideShowImpl::requestWaitSymbol (void)
966 {
967     ++mnWaitSymbolRequestCount;
968     OSL_ASSERT(mnWaitSymbolRequestCount>0);
969 
970     if (mnWaitSymbolRequestCount == 1)
971     {
972         if( !mpWaitSymbol )
973         {
974             // fall back to cursor
975             requestCursor(calcActiveCursor(mnCurrentCursor));
976         }
977         else
978             mpWaitSymbol->show();
979     }
980 }
981 
982 void SlideShowImpl::releaseWaitSymbol (void)
983 {
984     --mnWaitSymbolRequestCount;
985     OSL_ASSERT(mnWaitSymbolRequestCount>=0);
986 
987     if (mnWaitSymbolRequestCount == 0)
988     {
989         if( !mpWaitSymbol )
990         {
991             // fall back to cursor
992             requestCursor(calcActiveCursor(mnCurrentCursor));
993         }
994         else
995             mpWaitSymbol->hide();
996     }
997 }
998 
999 sal_Int16 SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape ) const
1000 {
1001     if( mnWaitSymbolRequestCount>0 && !mpWaitSymbol ) // enforce wait cursor
1002         nCursorShape = awt::SystemPointer::WAIT;
1003     else if( !mbMouseVisible ) // enforce INVISIBLE
1004         nCursorShape = awt::SystemPointer::INVISIBLE;
1005     else if( maUserPaintColor &&
1006              nCursorShape == awt::SystemPointer::ARROW )
1007         nCursorShape = awt::SystemPointer::PEN;
1008 
1009     return nCursorShape;
1010 }
1011 
1012 
1013 void SlideShowImpl::stopShow()
1014 {
1015     // Force-end running animation
1016     // ===========================
1017     if (mpCurrentSlide)
1018     {
1019         mpCurrentSlide->hide();
1020         //Register polygons in the map
1021         if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1022             maPolygons.erase(mpCurrentSlide->getXDrawPage());
1023 
1024         maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1025     }
1026 
1027     // clear all queues
1028     maEventQueue.clear();
1029     maActivitiesQueue.clear();
1030 
1031     // Attention: we MUST clear the user event queue here,
1032     // this is because the current slide might have registered
1033     // shape events (click or enter/leave), which might
1034     // otherwise dangle forever in the queue (because of the
1035     // shared ptr nature). If someone needs to change this:
1036     // somehow unregister those shapes at the user event queue
1037     // on notifySlideEnded().
1038     maUserEventQueue.clear();
1039 
1040     // re-enable automatic effect advancement
1041     // (maEventQueue.clear() above might have killed
1042     // maEventMultiplexer's tick events)
1043     if (mbAutomaticAdvancementMode)
1044     {
1045         // toggle automatic mode (enabling just again is
1046         // ignored by EventMultiplexer)
1047         maEventMultiplexer.setAutomaticMode( false );
1048         maEventMultiplexer.setAutomaticMode( true );
1049     }
1050 }
1051 
1052 
1053 
1054 class SlideShowImpl::PrefetchPropertiesFunc
1055 {
1056 public:
1057     PrefetchPropertiesFunc( SlideShowImpl * that_,
1058         bool& rbSkipAllMainSequenceEffects,
1059         bool& rbSkipSlideTransition)
1060         : mpSlideShowImpl(that_),
1061           mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1062           mrbSkipSlideTransition(rbSkipSlideTransition)
1063     {}
1064 
1065     void operator()( beans::PropertyValue const& rProperty ) const {
1066         if (rProperty.Name.equalsAsciiL(
1067                 RTL_CONSTASCII_STRINGPARAM("Prefetch") ))
1068         {
1069             uno::Sequence<uno::Any> seq;
1070             if ((rProperty.Value >>= seq) && seq.getLength() == 2)
1071             {
1072                 seq[0] >>= mpSlideShowImpl->mxPrefetchSlide;
1073                 seq[1] >>= mpSlideShowImpl->mxPrefetchAnimationNode;
1074             }
1075         }
1076         else if (rProperty.Name.equalsAsciiL(
1077                 RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") ))
1078         {
1079             rProperty.Value >>= mrbSkipAllMainSequenceEffects;
1080         }
1081         else if (rProperty.Name.equalsAsciiL(
1082                 RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") ))
1083         {
1084             rProperty.Value >>= mrbSkipSlideTransition;
1085         }
1086         else
1087         {
1088             OSL_ENSURE( false, rtl::OUStringToOString(
1089                             rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() );
1090         }
1091     }
1092 private:
1093     SlideShowImpl *const mpSlideShowImpl;
1094     bool& mrbSkipAllMainSequenceEffects;
1095     bool& mrbSkipSlideTransition;
1096 };
1097 
1098 void SlideShowImpl::displaySlide(
1099     uno::Reference<drawing::XDrawPage> const& xSlide,
1100     uno::Reference<drawing::XDrawPagesSupplier> const& xDrawPages,
1101     uno::Reference<animations::XAnimationNode> const& xRootNode,
1102     uno::Sequence<beans::PropertyValue> const& rProperties )
1103     throw (uno::RuntimeException)
1104 {
1105     osl::MutexGuard const guard( m_aMutex );
1106 
1107     if (isDisposed())
1108         return;
1109 
1110     maEffectRewinder.setRootAnimationNode(xRootNode);
1111 
1112     // precondition: must only be called from the main thread!
1113     DBG_TESTSOLARMUTEX();
1114 
1115     mxDrawPagesSupplier = xDrawPages;
1116 
1117     stopShow();  // MUST call that: results in
1118     // maUserEventQueue.clear(). What's more,
1119     // stopShow()'s currSlide->hide() call is
1120     // now also required, notifySlideEnded()
1121     // relies on that
1122     // unconditionally. Otherwise, genuine
1123     // shape animations (drawing layer and
1124     // GIF) will not be stopped.
1125 
1126     bool bSkipAllMainSequenceEffects (false);
1127     bool bSkipSlideTransition (false);
1128     std::for_each( rProperties.getConstArray(),
1129                    rProperties.getConstArray() + rProperties.getLength(),
1130         PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects, bSkipSlideTransition) );
1131 
1132     OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1133     if (maViewContainer.empty())
1134         return;
1135 
1136     // this here might take some time
1137     {
1138         WaitSymbolLock aLock (*this);
1139 
1140         mpPreviousSlide = mpCurrentSlide;
1141         mpCurrentSlide.reset();
1142 
1143         if (matches( mpPrefetchSlide, xSlide, xRootNode ))
1144         {
1145             // prefetched slide matches:
1146             mpCurrentSlide = mpPrefetchSlide;
1147         }
1148         else
1149             mpCurrentSlide = makeSlide( xSlide, xDrawPages, xRootNode );
1150 
1151         OSL_ASSERT( mpCurrentSlide );
1152         if (mpCurrentSlide)
1153         {
1154             basegfx::B2DSize oldSlideSize;
1155             if( mpPreviousSlide )
1156                 oldSlideSize = mpPreviousSlide->getSlideSize();
1157 
1158             basegfx::B2DSize const slideSize( mpCurrentSlide->getSlideSize() );
1159 
1160             // push new transformation to all views, if size changed
1161             if( !mpPreviousSlide || oldSlideSize != slideSize )
1162             {
1163                 std::for_each( maViewContainer.begin(),
1164                                maViewContainer.end(),
1165                                boost::bind( &View::setViewSize, _1,
1166                                             boost::cref(slideSize) ));
1167 
1168                 // explicitly notify view change here,
1169                 // because transformation might have changed:
1170                 // optimization, this->notifyViewChange() would
1171                 // repaint slide which is not necessary.
1172                 maEventMultiplexer.notifyViewsChanged();
1173             }
1174 
1175             // create slide transition, and add proper end event
1176             // (which then starts the slide effects
1177             // via CURRENT_SLIDE.show())
1178             ActivitySharedPtr pSlideChangeActivity (
1179                 createSlideTransition(
1180                     mpCurrentSlide->getXDrawPage(),
1181                     mpPreviousSlide,
1182                     mpCurrentSlide,
1183                     makeEvent(
1184                         boost::bind(
1185                             &SlideShowImpl::notifySlideTransitionEnded,
1186                             this,
1187                             false ),
1188                         "SlideShowImpl::notifySlideTransitionEnded")));
1189 
1190             if (bSkipSlideTransition)
1191             {
1192                 // The transition activity was created for the side effects
1193                 // (like sound transitions).  Because we want to skip the
1194                 // acutual transition animation we do not need the activity
1195                 // anymore.
1196                 pSlideChangeActivity.reset();
1197             }
1198 
1199             if (pSlideChangeActivity)
1200             {
1201                 // factory generated a slide transition - activate it!
1202                 maActivitiesQueue.addActivity( pSlideChangeActivity );
1203             }
1204             else
1205             {
1206                 // no transition effect on this slide - schedule slide
1207                 // effect start event right away.
1208                 maEventQueue.addEvent(
1209                     makeEvent(
1210                         boost::bind(
1211                             &SlideShowImpl::notifySlideTransitionEnded,
1212                             this,
1213                             true ),
1214                         "SlideShowImpl::notifySlideTransitionEnded"));
1215             }
1216         }
1217     } // finally
1218 
1219     maEventMultiplexer.notifySlideTransitionStarted();
1220     maListenerContainer.forEach<presentation::XSlideShowListener>(
1221         boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
1222 
1223     // We are currently rewinding an effect.  This lead us from the next
1224     // slide to this one.  To complete this we have to play back all main
1225     // sequence effects on this slide.
1226     if (bSkipAllMainSequenceEffects)
1227         maEffectRewinder.skipAllMainSequenceEffects();
1228 }
1229 
1230 void SlideShowImpl::redisplayCurrentSlide (void)
1231 {
1232     osl::MutexGuard const guard( m_aMutex );
1233 
1234     if (isDisposed())
1235         return;
1236 
1237     // precondition: must only be called from the main thread!
1238     DBG_TESTSOLARMUTEX();
1239     stopShow();
1240 
1241     OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1242     if (maViewContainer.empty())
1243         return;
1244 
1245     // No transition effect on this slide - schedule slide
1246     // effect start event right away.
1247     maEventQueue.addEvent(
1248         makeEvent(
1249             boost::bind(
1250                 &SlideShowImpl::notifySlideTransitionEnded,
1251                 this,
1252                 true ),
1253             "SlideShowImpl::notifySlideTransitionEnded"));
1254 
1255     maEventMultiplexer.notifySlideTransitionStarted();
1256     maListenerContainer.forEach<presentation::XSlideShowListener>(
1257         boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted ) );
1258 }
1259 
1260 sal_Bool SlideShowImpl::nextEffect() throw (uno::RuntimeException)
1261 {
1262     osl::MutexGuard const guard( m_aMutex );
1263 
1264     if (isDisposed())
1265         return false;
1266 
1267     // precondition: must only be called from the main thread!
1268     DBG_TESTSOLARMUTEX();
1269 
1270     if (mbShowPaused)
1271         return true;
1272     else
1273         return maEventMultiplexer.notifyNextEffect();
1274 }
1275 
1276 
1277 sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException)
1278 {
1279     osl::MutexGuard const guard( m_aMutex );
1280 
1281     if (isDisposed())
1282         return false;
1283 
1284     // precondition: must only be called from the main thread!
1285     DBG_TESTSOLARMUTEX();
1286 
1287     if (mbShowPaused)
1288         return true;
1289     else
1290     {
1291         return maEffectRewinder.rewind(
1292             maScreenUpdater.createLock(false),
1293             ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::redisplayCurrentSlide), this),
1294             ::boost::bind<void>(::boost::mem_fn(&SlideShowImpl::rewindEffectToPreviousSlide), this));
1295     }
1296 }
1297 
1298 void SlideShowImpl::rewindEffectToPreviousSlide (void)
1299 {
1300     // Show the wait symbol now and prevent it from showing temporary slide
1301     // content while effects are played back.
1302     WaitSymbolLock aLock (*this);
1303 
1304     // A previous call to EffectRewinder::Rewind could not rewind the current
1305     // effect because there are no effects on the current slide or none has
1306     // yet been displayed.  Go to the previous slide.
1307     notifySlideEnded(true);
1308 
1309     // Process pending events once more in order to have the following
1310     // screen update show the last effect.  Not sure whether this should be
1311     // necessary.
1312     maEventQueue.forceEmpty();
1313 
1314     // We have to call the screen updater before the wait symbol is turned
1315     // off.  Otherwise the wait symbol would force the display of an
1316     // intermediate state of the slide (before the effects are replayed.)
1317     maScreenUpdater.commitUpdates();
1318 }
1319 
1320 sal_Bool SlideShowImpl::startShapeActivity(
1321     uno::Reference<drawing::XShape> const& /*xShape*/ )
1322     throw (uno::RuntimeException)
1323 {
1324     osl::MutexGuard const guard( m_aMutex );
1325 
1326     // precondition: must only be called from the main thread!
1327     DBG_TESTSOLARMUTEX();
1328 
1329     // TODO(F3): NYI
1330     OSL_ENSURE( false, "not yet implemented!" );
1331     return false;
1332 }
1333 
1334 sal_Bool SlideShowImpl::stopShapeActivity(
1335     uno::Reference<drawing::XShape> const& /*xShape*/ )
1336     throw (uno::RuntimeException)
1337 {
1338     osl::MutexGuard const guard( m_aMutex );
1339 
1340     // precondition: must only be called from the main thread!
1341     DBG_TESTSOLARMUTEX();
1342 
1343     // TODO(F3): NYI
1344     OSL_ENSURE( false, "not yet implemented!" );
1345     return false;
1346 }
1347 
1348 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1349     throw (uno::RuntimeException)
1350 {
1351     osl::MutexGuard const guard( m_aMutex );
1352 
1353     if (isDisposed())
1354         return false;
1355 
1356     // precondition: must only be called from the main thread!
1357     DBG_TESTSOLARMUTEX();
1358 
1359 
1360     if (bPauseShow)
1361         mpPresTimer->pauseTimer();
1362     else
1363         mpPresTimer->continueTimer();
1364 
1365     maEventMultiplexer.notifyPauseMode(bPauseShow);
1366 
1367     mbShowPaused = bPauseShow;
1368     return true;
1369 }
1370 
1371 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1372     throw (uno::RuntimeException)
1373 {
1374     osl::MutexGuard const guard( m_aMutex );
1375 
1376     if (isDisposed())
1377         return uno::Reference<drawing::XDrawPage>();
1378 
1379     // precondition: must only be called from the main thread!
1380     DBG_TESTSOLARMUTEX();
1381 
1382     if (mpCurrentSlide)
1383         return mpCurrentSlide->getXDrawPage();
1384     else
1385         return uno::Reference<drawing::XDrawPage>();
1386 }
1387 
1388 sal_Bool SlideShowImpl::addView(
1389     uno::Reference<presentation::XSlideShowView> const& xView )
1390     throw (uno::RuntimeException)
1391 {
1392     osl::MutexGuard const guard( m_aMutex );
1393 
1394     if (isDisposed())
1395         return false;
1396 
1397     // precondition: must only be called from the main thread!
1398     DBG_TESTSOLARMUTEX();
1399 
1400     // first of all, check if view has a valid canvas
1401     ENSURE_OR_RETURN_FALSE( xView.is(), "addView(): Invalid view" );
1402     ENSURE_OR_RETURN_FALSE( xView->getCanvas().is(),
1403                        "addView(): View does not provide a valid canvas" );
1404 
1405     UnoViewSharedPtr const pView( createSlideView(
1406                                       xView,
1407                                       maEventQueue,
1408                                       maEventMultiplexer ));
1409     if (!maViewContainer.addView( pView ))
1410         return false; // view already added
1411 
1412     // initialize view content
1413     // =======================
1414 
1415     if (mpCurrentSlide)
1416     {
1417         // set view transformation
1418         const basegfx::B2ISize slideSize = mpCurrentSlide->getSlideSize();
1419         pView->setViewSize( basegfx::B2DSize( slideSize.getX(),
1420                                               slideSize.getY() ) );
1421     }
1422 
1423     // clear view area (since its newly added,
1424     // we need a clean slate)
1425     pView->clearAll();
1426 
1427     // broadcast newly added view
1428     maEventMultiplexer.notifyViewAdded( pView );
1429 
1430     // set current mouse ptr
1431     pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1432 
1433     return true;
1434 }
1435 
1436 sal_Bool SlideShowImpl::removeView(
1437     uno::Reference<presentation::XSlideShowView> const& xView )
1438     throw (uno::RuntimeException)
1439 {
1440     osl::MutexGuard const guard( m_aMutex );
1441 
1442     // precondition: must only be called from the main thread!
1443     DBG_TESTSOLARMUTEX();
1444 
1445     ENSURE_OR_RETURN_FALSE( xView.is(), "removeView(): Invalid view" );
1446 
1447     UnoViewSharedPtr const pView( maViewContainer.removeView( xView ) );
1448     if( !pView )
1449         return false; // view was not added in the first place
1450 
1451     // remove view from EventMultiplexer (mouse events etc.)
1452     maEventMultiplexer.notifyViewRemoved( pView );
1453 
1454     pView->_dispose();
1455 
1456     return true;
1457 }
1458 
1459 void SlideShowImpl::registerUserPaintPolygons( const uno::Reference< lang::XMultiServiceFactory >& xDocFactory ) throw (uno::RuntimeException)
1460 {
1461     //Retrieve Polygons if user ends presentation by context menu
1462     if (mpCurrentSlide)
1463     {
1464         if(findPolygons(mpCurrentSlide->getXDrawPage()) != maPolygons.end())
1465             maPolygons.erase(mpCurrentSlide->getXDrawPage());
1466 
1467         maPolygons.insert(make_pair(mpCurrentSlide->getXDrawPage(),mpCurrentSlide->getPolygons()));
1468     }
1469 
1470     //Creating the layer for shapes
1471     // query for the XLayerManager
1472     uno::Reference< drawing::XLayerSupplier > xLayerSupplier(xDocFactory, uno::UNO_QUERY);
1473     uno::Reference< container::XNameAccess > xNameAccess = xLayerSupplier->getLayerManager();
1474 
1475     uno::Reference< drawing::XLayerManager > xLayerManager(xNameAccess, uno::UNO_QUERY);
1476     // create a layer and set its properties
1477     uno::Reference< drawing::XLayer > xDrawnInSlideshow = xLayerManager->insertNewByIndex(xLayerManager->getCount());
1478     uno::Reference< beans::XPropertySet > xLayerPropSet(xDrawnInSlideshow, uno::UNO_QUERY);
1479 
1480     //Layer Name which enables to catch annotations
1481     rtl::OUString layerName = rtl::OUString::createFromAscii("DrawnInSlideshow");
1482     uno::Any aPropLayer;
1483 
1484     aPropLayer <<= layerName;
1485     xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("Name"), aPropLayer);
1486 
1487     aPropLayer <<= true;
1488     xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("IsVisible"), aPropLayer);
1489 
1490     aPropLayer <<= false;
1491     xLayerPropSet->setPropertyValue(rtl::OUString::createFromAscii("IsLocked"), aPropLayer);
1492 
1493     PolygonMap::iterator aIter=maPolygons.begin();
1494 
1495     PolyPolygonVector aPolygons;
1496     ::cppcanvas::PolyPolygonSharedPtr pPolyPoly;
1497     ::basegfx::B2DPolyPolygon b2DPolyPoly;
1498 
1499     //Register polygons for each slide
1500     while(aIter!=maPolygons.end())
1501     {
1502         aPolygons = aIter->second;
1503         //Get shapes for the slide
1504         ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes > Shapes(aIter->first, ::com::sun::star::uno::UNO_QUERY);
1505         //Retrieve polygons for one slide
1506         for( PolyPolygonVector::iterator aIterPoly=aPolygons.begin(),
1507                  aEnd=aPolygons.end();
1508              aIterPoly!=aEnd; ++aIterPoly )
1509         {
1510             pPolyPoly = (*aIterPoly);
1511             b2DPolyPoly = ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(pPolyPoly->getUNOPolyPolygon());
1512 
1513             //Normally there is only one polygon
1514             for(sal_uInt32 i=0; i< b2DPolyPoly.count();i++)
1515             {
1516                 const ::basegfx::B2DPolygon& aPoly =  b2DPolyPoly.getB2DPolygon(i);
1517                 sal_uInt32 nPoints = aPoly.count();
1518 
1519                 if( nPoints > 1)
1520                 {
1521                     //create the PolyLineShape
1522                     uno::Reference< uno::XInterface > polyshape(xDocFactory->createInstance(
1523                                                                     rtl::OUString::createFromAscii("com.sun.star.drawing.PolyLineShape") ) );
1524                     uno::Reference< drawing::XShape > rPolyShape(polyshape, uno::UNO_QUERY);
1525 
1526                     //Add the shape to the slide
1527                     Shapes->add(rPolyShape);
1528 
1529                     //Retrieve shape properties
1530                     uno::Reference< beans::XPropertySet > aXPropSet = uno::Reference< beans::XPropertySet >( rPolyShape, uno::UNO_QUERY );
1531                     //Construct a sequence of points sequence
1532                     drawing::PointSequenceSequence aRetval;
1533                     //Create only one sequence for one polygon
1534                     aRetval.realloc( 1 );
1535                     // Retrieve the sequence of points from aRetval
1536                     drawing::PointSequence* pOuterSequence = aRetval.getArray();
1537                     // Create 2 points in this sequence
1538                     pOuterSequence->realloc(nPoints);
1539                     // Get these points which are in an array
1540                     awt::Point* pInnerSequence = pOuterSequence->getArray();
1541                     for( sal_uInt32 n = 0; n < nPoints; n++ )
1542                     {
1543                         //Create a point from the polygon
1544                         *pInnerSequence++ = awt::Point(
1545                             basegfx::fround(aPoly.getB2DPoint(n).getX()),
1546                             basegfx::fround(aPoly.getB2DPoint(n).getY()));
1547                     }
1548 
1549                     //Fill the properties
1550                     //Give the built PointSequenceSequence.
1551                     uno::Any aParam;
1552                     aParam <<= aRetval;
1553                     aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("PolyPolygon"), aParam );
1554 
1555                     //LineStyle : SOLID by default
1556                     uno::Any			aAny;
1557                     drawing::LineStyle	eLS;
1558                     eLS = drawing::LineStyle_SOLID;
1559                     aAny <<= eLS;
1560                     aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineStyle"), aAny );
1561 
1562                     //LineColor
1563                     sal_uInt32			nLineColor;
1564                     nLineColor = pPolyPoly->getRGBALineColor();
1565                     //Transform polygon color from RRGGBBAA to AARRGGBB
1566                     aAny <<= RGBAColor2UnoColor(nLineColor);
1567                     aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineColor"), aAny );
1568 
1569                     //LineWidth
1570                     double				fLineWidth;
1571                     fLineWidth = pPolyPoly->getStrokeWidth();
1572                     aAny <<= (sal_Int32)fLineWidth;
1573                     aXPropSet->setPropertyValue( rtl::OUString::createFromAscii("LineWidth"), aAny );
1574 
1575                     // make polygons special
1576                     xLayerManager->attachShapeToLayer(rPolyShape, xDrawnInSlideshow);
1577                 }
1578             }
1579         }
1580         ++aIter;
1581     }
1582 }
1583 
1584 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1585     throw (uno::RuntimeException)
1586 {
1587     osl::MutexGuard const guard( m_aMutex );
1588 
1589     if (isDisposed())
1590         return false;
1591 
1592     // precondition: must only be called from the main thread!
1593     DBG_TESTSOLARMUTEX();
1594 
1595     if (rProperty.Name.equalsAsciiL(
1596             RTL_CONSTASCII_STRINGPARAM("AutomaticAdvancement") ))
1597     {
1598         double nTimeout(0.0);
1599         mbAutomaticAdvancementMode = (rProperty.Value >>= nTimeout);
1600         if (mbAutomaticAdvancementMode)
1601         {
1602             maEventMultiplexer.setAutomaticTimeout( nTimeout );
1603         }
1604         maEventMultiplexer.setAutomaticMode( mbAutomaticAdvancementMode );
1605         return true;
1606     }
1607 
1608     if (rProperty.Name.equalsAsciiL(
1609             RTL_CONSTASCII_STRINGPARAM("UserPaintColor") ))
1610     {
1611         sal_Int32 nColor(0);
1612         if (rProperty.Value >>= nColor)
1613         {
1614             OSL_ENSURE( mbMouseVisible,
1615                         "setProperty(): User paint overrides invisible mouse" );
1616 
1617             // enable user paint
1618             maUserPaintColor.reset( unoColor2RGBColor( nColor ) );
1619 			if( mpCurrentSlide && !mpCurrentSlide->isPaintOverlayActive() )
1620 				mpCurrentSlide->enablePaintOverlay();
1621 
1622 			maEventMultiplexer.notifyUserPaintColor( *maUserPaintColor );
1623         }
1624         else
1625         {
1626             // disable user paint
1627             maUserPaintColor.reset();
1628             maEventMultiplexer.notifyUserPaintDisabled();
1629 			if( mpCurrentSlide )
1630 				mpCurrentSlide->disablePaintOverlay();
1631         }
1632 
1633         resetCursor();
1634 
1635         return true;
1636     }
1637 
1638 	//adding support for erasing features in UserPaintOverlay
1639 	if (rProperty.Name.equalsAsciiL(
1640             RTL_CONSTASCII_STRINGPARAM("EraseAllInk") ))
1641 	{
1642 		bool nEraseAllInk(false);
1643 		if (rProperty.Value >>= nEraseAllInk)
1644 		{
1645 		    OSL_ENSURE( mbMouseVisible,
1646                         "setProperty(): User paint overrides invisible mouse" );
1647 
1648 		    // enable user paint
1649 		    maEraseAllInk.reset( nEraseAllInk );
1650 		    maEventMultiplexer.notifyEraseAllInk( *maEraseAllInk );
1651 	    }
1652 
1653 	    return true;
1654 	}
1655 
1656 	if (rProperty.Name.equalsAsciiL(
1657             RTL_CONSTASCII_STRINGPARAM("SwitchPenMode") ))
1658 	{
1659 		bool nSwitchPenMode(false);
1660 		if (rProperty.Value >>= nSwitchPenMode)
1661 		{
1662 		    OSL_ENSURE( mbMouseVisible,
1663                         "setProperty(): User paint overrides invisible mouse" );
1664 
1665 		    if(nSwitchPenMode == true){
1666 			// Switch to Pen Mode
1667 			maSwitchPenMode.reset( nSwitchPenMode );
1668 			maEventMultiplexer.notifySwitchPenMode();
1669 		    }
1670 		}
1671 		return true;
1672 	}
1673 
1674 
1675 	if (rProperty.Name.equalsAsciiL(
1676             RTL_CONSTASCII_STRINGPARAM("SwitchEraserMode") ))
1677 	{
1678 		bool nSwitchEraserMode(false);
1679 		if (rProperty.Value >>= nSwitchEraserMode)
1680 		{
1681 		    OSL_ENSURE( mbMouseVisible,
1682                         "setProperty(): User paint overrides invisible mouse" );
1683 		    if(nSwitchEraserMode == true){
1684 			// switch to Eraser mode
1685 			maSwitchEraserMode.reset( nSwitchEraserMode );
1686 			maEventMultiplexer.notifySwitchEraserMode();
1687 		    }
1688 		}
1689 
1690 		return true;
1691 	}
1692 
1693 
1694 
1695 	if (rProperty.Name.equalsAsciiL(
1696             RTL_CONSTASCII_STRINGPARAM("EraseInk") ))
1697 	{
1698 		sal_Int32 nEraseInk(100);
1699 		if (rProperty.Value >>= nEraseInk)
1700 		{
1701 		    OSL_ENSURE( mbMouseVisible,
1702                         "setProperty(): User paint overrides invisible mouse" );
1703 
1704 		    // enable user paint
1705 		    maEraseInk.reset( nEraseInk );
1706 		    maEventMultiplexer.notifyEraseInkWidth( *maEraseInk );
1707 		}
1708 
1709 		return true;
1710 	}
1711 
1712 	// new Property for pen's width
1713     if (rProperty.Name.equalsAsciiL(
1714             RTL_CONSTASCII_STRINGPARAM("UserPaintStrokeWidth") ))
1715     {
1716         double nWidth(4.0);
1717         if (rProperty.Value >>= nWidth)
1718         {
1719             OSL_ENSURE( mbMouseVisible,"setProperty(): User paint overrides invisible mouse" );
1720             // enable user paint stroke width
1721             maUserPaintStrokeWidth = nWidth;
1722             maEventMultiplexer.notifyUserPaintStrokeWidth( maUserPaintStrokeWidth );
1723         }
1724 
1725         return true;
1726     }
1727 
1728     if (rProperty.Name.equalsAsciiL(
1729             RTL_CONSTASCII_STRINGPARAM("AdvanceOnClick") ))
1730     {
1731         sal_Bool bAdvanceOnClick = sal_False;
1732         if (! (rProperty.Value >>= bAdvanceOnClick))
1733             return false;
1734         maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1735         return true;
1736     }
1737 
1738     if (rProperty.Name.equalsAsciiL(
1739             RTL_CONSTASCII_STRINGPARAM("DisableAnimationZOrder") ))
1740     {
1741         sal_Bool bDisableAnimationZOrder = sal_False;
1742         if (! (rProperty.Value >>= bDisableAnimationZOrder))
1743             return false;
1744         mbDisableAnimationZOrder = bDisableAnimationZOrder == sal_True;
1745         return true;
1746     }
1747 
1748     if (rProperty.Name.equalsAsciiL(
1749             RTL_CONSTASCII_STRINGPARAM("ImageAnimationsAllowed") ) )
1750     {
1751         if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1752             return false;
1753 
1754         // TODO(F3): Forward to slides!
1755 //         if( bOldValue != mbImageAnimationsAllowed )
1756 //         {
1757 //             if( mbImageAnimationsAllowed )
1758 //                 maEventMultiplexer.notifyIntrinsicAnimationsEnabled();
1759 //             else
1760 //                 maEventMultiplexer.notifyIntrinsicAnimationsDisabled();
1761 //         }
1762 
1763         return true;
1764     }
1765 
1766     if (rProperty.Name.equalsAsciiL(
1767             RTL_CONSTASCII_STRINGPARAM("MouseVisible") ))
1768     {
1769         if (! (rProperty.Value >>= mbMouseVisible))
1770             return false;
1771 
1772         requestCursor(mnCurrentCursor);
1773 
1774         return true;
1775     }
1776 
1777     if (rProperty.Name.equalsAsciiL(
1778             RTL_CONSTASCII_STRINGPARAM("ForceManualAdvance") ))
1779     {
1780         return (rProperty.Value >>= mbForceManualAdvance);
1781     }
1782 
1783     if (rProperty.Name.equalsAsciiL(
1784             RTL_CONSTASCII_STRINGPARAM("RehearseTimings") ))
1785     {
1786         bool bRehearseTimings = false;
1787         if (! (rProperty.Value >>= bRehearseTimings))
1788             return false;
1789 
1790         if (bRehearseTimings)
1791         {
1792             // TODO(Q3): Move to slide
1793             mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1794                 SlideShowContext(
1795                     mpDummyPtr,
1796                     maEventQueue,
1797                     maEventMultiplexer,
1798                     maScreenUpdater,
1799                     maActivitiesQueue,
1800                     maUserEventQueue,
1801                     *this,
1802                     maViewContainer,
1803                     mxComponentContext) );
1804         }
1805         else if (mpRehearseTimingsActivity)
1806         {
1807             // removes timer from all views:
1808             mpRehearseTimingsActivity->dispose();
1809             mpRehearseTimingsActivity.reset();
1810         }
1811         return true;
1812     }
1813 
1814     if (rProperty.Name.equalsAsciiL(
1815             RTL_CONSTASCII_STRINGPARAM("WaitSymbolBitmap") ))
1816     {
1817         uno::Reference<rendering::XBitmap> xBitmap;
1818         if (! (rProperty.Value >>= xBitmap))
1819             return false;
1820 
1821         mpWaitSymbol = WaitSymbol::create( xBitmap,
1822                                            maScreenUpdater,
1823                                            maEventMultiplexer,
1824                                            maViewContainer );
1825 
1826         return true;
1827     }
1828 
1829     if (rProperty.Name.equalsAsciiL(
1830             RTL_CONSTASCII_STRINGPARAM("NoSlideTransitions") ))
1831     {
1832         return (rProperty.Value >>= mbNoSlideTransitions);
1833     }
1834 
1835     if (rProperty.Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsSoundEnabled")))
1836     {
1837         uno::Sequence<uno::Any> aValues;
1838         uno::Reference<presentation::XSlideShowView> xView;
1839         sal_Bool bValue (false);
1840         if ((rProperty.Value >>= aValues)
1841             && aValues.getLength()==2
1842             && (aValues[0] >>= xView)
1843             && (aValues[1] >>= bValue))
1844         {
1845             // Look up the view.
1846             for (UnoViewVector::const_iterator
1847                      iView (maViewContainer.begin()),
1848                      iEnd (maViewContainer.end());
1849                  iView!=iEnd;
1850                  ++iView)
1851             {
1852                 if (*iView && (*iView)->getUnoView()==xView)
1853                 {
1854                     // Store the flag at the view so that media shapes have
1855                     // access to it.
1856                     (*iView)->setIsSoundEnabled(bValue);
1857                     return true;
1858                 }
1859             }
1860         }
1861     }
1862 
1863     return false;
1864 }
1865 
1866 void SlideShowImpl::addSlideShowListener(
1867     uno::Reference<presentation::XSlideShowListener> const& xListener )
1868     throw (uno::RuntimeException)
1869 {
1870     osl::MutexGuard const guard( m_aMutex );
1871 
1872     if (isDisposed())
1873         return;
1874 
1875     // container syncs with passed mutex ref
1876     maListenerContainer.addInterface(xListener);
1877 }
1878 
1879 void SlideShowImpl::removeSlideShowListener(
1880     uno::Reference<presentation::XSlideShowListener> const& xListener )
1881     throw (uno::RuntimeException)
1882 {
1883     osl::MutexGuard const guard( m_aMutex );
1884 
1885     // container syncs with passed mutex ref
1886     maListenerContainer.removeInterface(xListener);
1887 }
1888 
1889 void SlideShowImpl::addShapeEventListener(
1890     uno::Reference<presentation::XShapeEventListener> const& xListener,
1891     uno::Reference<drawing::XShape> const& xShape )
1892     throw (uno::RuntimeException)
1893 {
1894     osl::MutexGuard const guard( m_aMutex );
1895 
1896     if (isDisposed())
1897         return;
1898 
1899     // precondition: must only be called from the main thread!
1900     DBG_TESTSOLARMUTEX();
1901 
1902     ShapeEventListenerMap::iterator aIter;
1903     if( (aIter=maShapeEventListeners.find( xShape )) ==
1904         maShapeEventListeners.end() )
1905     {
1906         // no entry for this shape -> create one
1907         aIter = maShapeEventListeners.insert(
1908             ShapeEventListenerMap::value_type(
1909                 xShape,
1910                 boost::shared_ptr<cppu::OInterfaceContainerHelper>(
1911                     new cppu::OInterfaceContainerHelper(m_aMutex)))).first;
1912     }
1913 
1914     // add new listener to broadcaster
1915     if( aIter->second.get() )
1916         aIter->second->addInterface( xListener );
1917 
1918     maEventMultiplexer.notifyShapeListenerAdded(xListener,
1919                                                 xShape);
1920 }
1921 
1922 void SlideShowImpl::removeShapeEventListener(
1923     uno::Reference<presentation::XShapeEventListener> const& xListener,
1924     uno::Reference<drawing::XShape> const& xShape )
1925     throw (uno::RuntimeException)
1926 {
1927     osl::MutexGuard const guard( m_aMutex );
1928 
1929     // precondition: must only be called from the main thread!
1930     DBG_TESTSOLARMUTEX();
1931 
1932     ShapeEventListenerMap::iterator aIter;
1933     if( (aIter = maShapeEventListeners.find( xShape )) !=
1934         maShapeEventListeners.end() )
1935     {
1936         // entry for this shape found -> remove listener from
1937         // helper object
1938         ENSURE_OR_THROW(
1939             aIter->second.get(),
1940             "SlideShowImpl::removeShapeEventListener(): "
1941             "listener map contains NULL broadcast helper" );
1942 
1943         aIter->second->removeInterface( xListener );
1944     }
1945 
1946     maEventMultiplexer.notifyShapeListenerRemoved(xListener,
1947                                                   xShape);
1948 }
1949 
1950 void SlideShowImpl::setShapeCursor(
1951     uno::Reference<drawing::XShape> const& xShape, sal_Int16 nPointerShape )
1952     throw (uno::RuntimeException)
1953 {
1954     osl::MutexGuard const guard( m_aMutex );
1955 
1956     if (isDisposed())
1957         return;
1958 
1959     // precondition: must only be called from the main thread!
1960     DBG_TESTSOLARMUTEX();
1961 
1962     ShapeCursorMap::iterator aIter;
1963     if( (aIter=maShapeCursors.find( xShape )) == maShapeCursors.end() )
1964     {
1965         // no entry for this shape -> create one
1966         if( nPointerShape != awt::SystemPointer::ARROW )
1967         {
1968             // add new entry, unless shape shall display
1969             // normal pointer arrow -> no need to handle that
1970             // case
1971             maShapeCursors.insert(
1972                 ShapeCursorMap::value_type(xShape,
1973                                            nPointerShape) );
1974         }
1975     }
1976     else if( nPointerShape == awt::SystemPointer::ARROW )
1977     {
1978         // shape shall display normal cursor -> can disable
1979         // the cursor and clear the entry
1980         maShapeCursors.erase( xShape );
1981     }
1982     else
1983     {
1984         // existing entry found, update with new cursor ID
1985         aIter->second = nPointerShape;
1986     }
1987 
1988     maEventMultiplexer.notifyShapeCursorChange(xShape,
1989                                                nPointerShape);
1990 }
1991 
1992 bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape )
1993 {
1994     mnCurrentCursor = nCursorShape;
1995 
1996     const sal_Int16 nActualCursor = calcActiveCursor(mnCurrentCursor);
1997 
1998     // change all views to the requested cursor ID
1999     std::for_each( maViewContainer.begin(),
2000                    maViewContainer.end(),
2001                    boost::bind( &View::setCursorShape,
2002                                 _1,
2003                                 nActualCursor ));
2004 
2005     return nActualCursor==nCursorShape;
2006 }
2007 
2008 void SlideShowImpl::resetCursor()
2009 {
2010     mnCurrentCursor = awt::SystemPointer::ARROW;
2011 
2012     // change all views to the default cursor ID
2013     std::for_each( maViewContainer.begin(),
2014                    maViewContainer.end(),
2015                    boost::bind( &View::setCursorShape,
2016                                 _1,
2017                                 calcActiveCursor(mnCurrentCursor) ));
2018 }
2019 
2020 sal_Bool SlideShowImpl::update( double & nNextTimeout )
2021     throw (uno::RuntimeException)
2022 {
2023     osl::MutexGuard const guard( m_aMutex );
2024 
2025     if (isDisposed())
2026         return false;
2027 
2028     // precondition: update() must only be called from the
2029     // main thread!
2030     DBG_TESTSOLARMUTEX();
2031 
2032     if( mbShowPaused )
2033     {
2034         // commit frame (might be repaints pending)
2035         maScreenUpdater.commitUpdates();
2036 
2037         return false;
2038     }
2039     else
2040     {
2041         // TODO(F2): re-evaluate whether that timer lagging makes
2042         // sense.
2043 
2044         // hold timer, while processing the queues:
2045         // 1. when there is more than one active activity this ensures the
2046         //    same time for all activities and events
2047         // 2. processing of events may lead to creation of further events
2048         //    that have zero delay.  While the timer is stopped these events
2049         //    are processed in the same run.
2050         {
2051             comphelper::ScopeGuard scopeGuard(
2052                 boost::bind( &canvas::tools::ElapsedTime::releaseTimer,
2053                              boost::cref(mpPresTimer) ) );
2054             mpPresTimer->holdTimer();
2055 
2056             // process queues
2057             maEventQueue.process();
2058             maActivitiesQueue.process();
2059 
2060             // commit frame to screen
2061             maFrameSynchronization.Synchronize();
2062             maScreenUpdater.commitUpdates();
2063 
2064             // TODO(Q3): remove need to call dequeued() from
2065             // activities. feels like a wart.
2066             //
2067             // Rationale for ActivitiesQueue::processDequeued(): when
2068             // an activity ends, it usually pushed the end state to
2069             // the animated shape in question, and ends the animation
2070             // (which, in turn, will usually disable shape sprite
2071             // mode). Disabling shape sprite mode causes shape
2072             // repaint, which, depending on slide content, takes
2073             // considerably more time than sprite updates. Thus, the
2074             // last animation step tends to look delayed. To
2075             // camouflage this, reaching end position and disabling
2076             // sprite mode is split into two (normal Activity::end(),
2077             // and Activity::dequeued()). Now, the reason to call
2078             // commitUpdates() twice here is caused by the unrelated
2079             // fact that during wait cursor display/hide, the screen
2080             // is updated, and shows hidden sprites, but, in case of
2081             // leaving the second commitUpdates() call out and punting
2082             // that to the next round, no updated static slide
2083             // content. In short, the last shape animation of a slide
2084             // tends to blink at its end.
2085 
2086             // process dequeued activities _after_ commit to screen
2087             maActivitiesQueue.processDequeued();
2088 
2089             // commit frame to screen
2090             maScreenUpdater.commitUpdates();
2091         }
2092         // Time held until here
2093 
2094         const bool bActivitiesLeft = (! maActivitiesQueue.isEmpty());
2095         const bool bTimerEventsLeft = (! maEventQueue.isEmpty());
2096         const bool bRet = (bActivitiesLeft || bTimerEventsLeft);
2097 
2098         if (bRet)
2099         {
2100             // calc nNextTimeout value:
2101             if (bActivitiesLeft)
2102             {
2103                 // Activity queue is not empty.  Tell caller that we would
2104                 // like to render another frame.
2105 
2106                 // Return a zero time-out to signal our caller to call us
2107                 // back as soon as possible.  The actual timing, waiting the
2108                 // appropriate amount of time between frames, is then done
2109                 // by the maFrameSynchronization object.
2110                 nNextTimeout = 0;
2111                 maFrameSynchronization.Activate();
2112             }
2113             else
2114             {
2115                 // timer events left:
2116                 // difference from current time (nota bene:
2117                 // time no longer held here!) to the next event in
2118                 // the event queue.
2119 
2120                 // #i61190# Retrieve next timeout only _after_
2121                 // processing activity queue
2122 
2123                 // ensure positive value:
2124                 nNextTimeout = std::max( 0.0, maEventQueue.nextTimeout() );
2125 
2126                 // There is no active animation so the frame rate does not
2127                 // need to be synchronized.
2128                 maFrameSynchronization.Deactivate();
2129             }
2130 
2131             mbSlideShowIdle = false;
2132         }
2133 
2134 #if defined(VERBOSE) && defined(DBG_UTIL)
2135         // when slideshow is idle, issue an XUpdatable::update() call
2136         // exactly once after a previous animation sequence finished -
2137         // this might trigger screen dumps on some canvas
2138         // implementations
2139         if( !mbSlideShowIdle &&
2140             (!bRet ||
2141              nNextTimeout > 1.0) )
2142         {
2143             UnoViewVector::const_iterator       aCurr(maViewContainer.begin());
2144             const UnoViewVector::const_iterator aEnd(maViewContainer.end());
2145             while( aCurr != aEnd )
2146             {
2147                 try
2148                 {
2149                     uno::Reference< presentation::XSlideShowView > xView( (*aCurr)->getUnoView(),
2150                                                                           uno::UNO_QUERY_THROW );
2151                     uno::Reference< util::XUpdatable >             xUpdatable( xView->getCanvas(),
2152                                                                                uno::UNO_QUERY_THROW );
2153                     xUpdatable->update();
2154                 }
2155                 catch( uno::RuntimeException& )
2156                 {
2157                     throw;
2158                 }
2159                 catch( uno::Exception& )
2160                 {
2161                     OSL_ENSURE( false,
2162                                 rtl::OUStringToOString(
2163                                     comphelper::anyToString( cppu::getCaughtException() ),
2164                                     RTL_TEXTENCODING_UTF8 ).getStr() );
2165                 }
2166 
2167                 ++aCurr;
2168             }
2169 
2170             mbSlideShowIdle = true;
2171         }
2172 #endif
2173 
2174         return bRet;
2175     }
2176 }
2177 
2178 void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide )
2179 {
2180     osl::MutexGuard const guard( m_aMutex );
2181 
2182     OSL_ENSURE( !isDisposed(), "### already disposed!" );
2183     OSL_ENSURE( mpCurrentSlide,
2184                 "notifySlideTransitionEnded(): Invalid current slide" );
2185     if (mpCurrentSlide)
2186     {
2187 		mpCurrentSlide->update_settings( !!maUserPaintColor, maUserPaintColor ? *maUserPaintColor : RGBColor(), maUserPaintStrokeWidth );
2188 
2189 		// first init show, to give the animations
2190         // the chance to register SlideStartEvents
2191         const bool bBackgroundLayerRendered( !bPaintSlide );
2192         mpCurrentSlide->show( bBackgroundLayerRendered );
2193         maEventMultiplexer.notifySlideStartEvent();
2194     }
2195 }
2196 
2197 void queryAutomaticSlideTransition( uno::Reference<drawing::XDrawPage> const& xDrawPage,
2198                                     double&                                   nAutomaticNextSlideTimeout,
2199                                     bool&                                     bHasAutomaticNextSlide )
2200 {
2201     // retrieve slide change parameters from XDrawPage
2202     // ===============================================
2203 
2204     uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
2205                                                     uno::UNO_QUERY );
2206 
2207     sal_Int32 nChange(0);
2208     if( !xPropSet.is() ||
2209         !getPropertyValue( nChange,
2210                            xPropSet,
2211                            ::rtl::OUString(
2212                                RTL_CONSTASCII_USTRINGPARAM("Change"))) )
2213     {
2214         OSL_TRACE(
2215             "queryAutomaticSlideTransition(): "
2216             "Could not extract slide change mode from XDrawPage - assuming <none>\n" );
2217     }
2218 
2219     bHasAutomaticNextSlide = nChange == 1;
2220 
2221     if( !xPropSet.is() ||
2222         !getPropertyValue( nAutomaticNextSlideTimeout,
2223                            xPropSet,
2224                            ::rtl::OUString(
2225                                RTL_CONSTASCII_USTRINGPARAM("Duration"))) )
2226     {
2227         OSL_TRACE(
2228             "queryAutomaticSlideTransition(): "
2229             "Could not extract slide transition timeout from "
2230             "XDrawPage - assuming 1 sec\n" );
2231     }
2232 }
2233 
2234 void SlideShowImpl::notifySlideAnimationsEnded()
2235 {
2236     osl::MutexGuard const guard( m_aMutex );
2237 
2238     //Draw polygons above animations
2239     mpCurrentSlide->drawPolygons();
2240 
2241     OSL_ENSURE( !isDisposed(), "### already disposed!" );
2242 
2243     // This struct will receive the (interruptable) event,
2244     // that triggers the notifySlideEnded() method.
2245     InterruptableEventPair aNotificationEvents;
2246 
2247     if( maEventMultiplexer.getAutomaticMode() )
2248     {
2249         OSL_ENSURE( ! mpRehearseTimingsActivity,
2250                     "unexpected: RehearseTimings mode!" );
2251 
2252         // schedule a slide end event, with automatic mode's
2253         // delay
2254         aNotificationEvents = makeInterruptableDelay(
2255             boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
2256             maEventMultiplexer.getAutomaticTimeout() );
2257     }
2258     else
2259     {
2260         OSL_ENSURE( mpCurrentSlide,
2261                     "notifySlideAnimationsEnded(): Invalid current slide!" );
2262 
2263         bool   bHasAutomaticNextSlide=false;
2264         double nAutomaticNextSlideTimeout=0.0;
2265         queryAutomaticSlideTransition(mpCurrentSlide->getXDrawPage(),
2266                                       nAutomaticNextSlideTimeout,
2267                                       bHasAutomaticNextSlide);
2268 
2269         // check whether slide transition should happen
2270         // 'automatically'. If yes, simply schedule the
2271         // specified timeout.
2272         // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
2273         // override any individual slide setting, to always
2274         // step slides manually.
2275         if( !mbForceManualAdvance &&
2276             !mpRehearseTimingsActivity &&
2277             bHasAutomaticNextSlide )
2278         {
2279             aNotificationEvents = makeInterruptableDelay(
2280                 boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
2281                 nAutomaticNextSlideTimeout);
2282 
2283             // TODO(F2): Provide a mechanism to let the user override
2284             // this automatic timeout via next()
2285         }
2286         else
2287         {
2288             if (mpRehearseTimingsActivity)
2289                 mpRehearseTimingsActivity->start();
2290 
2291             // generate click event. Thus, the user must
2292             // trigger the actual end of a slide. No need to
2293             // generate interruptable event here, there's no
2294             // timeout involved.
2295             aNotificationEvents.mpImmediateEvent =
2296                 makeEvent( boost::bind<void>(
2297                     boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
2298                     "SlideShowImpl::notifySlideEnded");
2299         }
2300     }
2301 
2302     // register events on the queues. To make automatic slide
2303     // changes interruptable, register the interruption event
2304     // as a nextEffectEvent target. Note that the timeout
2305     // event is optional (e.g. manual slide changes don't
2306     // generate a timeout)
2307     maUserEventQueue.registerNextEffectEvent(
2308         aNotificationEvents.mpImmediateEvent );
2309 
2310     if( aNotificationEvents.mpTimeoutEvent )
2311         maEventQueue.addEvent( aNotificationEvents.mpTimeoutEvent );
2312 
2313     // current slide's main sequence is over. Now should be
2314     // the time to prefetch the next slide (if any), and
2315     // prepare the initial slide bitmap (speeds up slide
2316     // change setup time a lot). Show the wait cursor, this
2317     // indeed might take some seconds.
2318     {
2319         WaitSymbolLock aLock (*this);
2320 
2321         if (! matches( mpPrefetchSlide,
2322                        mxPrefetchSlide, mxPrefetchAnimationNode ))
2323         {
2324             mpPrefetchSlide = makeSlide( mxPrefetchSlide, mxDrawPagesSupplier,
2325                                          mxPrefetchAnimationNode );
2326         }
2327         if (mpPrefetchSlide)
2328         {
2329             // ignore return value, this is just to populate
2330             // Slide's internal bitmap buffer, such that the time
2331             // needed to generate the slide bitmap is not spent
2332             // when the slide change is requested.
2333             mpPrefetchSlide->getCurrentSlideBitmap( *maViewContainer.begin() );
2334         }
2335     } // finally
2336 
2337     maListenerContainer.forEach<presentation::XSlideShowListener>(
2338         boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded ) );
2339 }
2340 
2341 void SlideShowImpl::notifySlideEnded (const bool bReverse)
2342 {
2343     osl::MutexGuard const guard( m_aMutex );
2344 
2345     OSL_ENSURE( !isDisposed(), "### already disposed!" );
2346 
2347     if (mpRehearseTimingsActivity && !bReverse)
2348     {
2349         const double time = mpRehearseTimingsActivity->stop();
2350         if (mpRehearseTimingsActivity->hasBeenClicked())
2351         {
2352             // save time at current drawpage:
2353             uno::Reference<beans::XPropertySet> xPropSet(
2354                 mpCurrentSlide->getXDrawPage(), uno::UNO_QUERY );
2355             OSL_ASSERT( xPropSet.is() );
2356             if (xPropSet.is())
2357             {
2358                 xPropSet->setPropertyValue(
2359                     OUSTR("Change"),
2360                     uno::Any( static_cast<sal_Int32>(1) ) );
2361                 xPropSet->setPropertyValue(
2362                     OUSTR("Duration"),
2363                     uno::Any( static_cast<sal_Int32>(time) ) );
2364             }
2365         }
2366     }
2367 
2368     if (bReverse)
2369         maEventMultiplexer.notifySlideEndEvent();
2370 
2371     stopShow();  // MUST call that: results in
2372                  // maUserEventQueue.clear(). What's more,
2373                  // stopShow()'s currSlide->hide() call is
2374                  // now also required, notifySlideEnded()
2375                  // relies on that
2376                  // unconditionally. Otherwise, genuine
2377                  // shape animations (drawing layer and
2378                  // GIF) will not be stopped.
2379 
2380     maListenerContainer.forEach<presentation::XSlideShowListener>(
2381         boost::bind<void>(
2382             ::boost::mem_fn(&presentation::XSlideShowListener::slideEnded),
2383             _1,
2384             sal_Bool(bReverse)));
2385 }
2386 
2387 bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString const& hyperLink )
2388 {
2389     osl::MutexGuard const guard( m_aMutex );
2390 
2391     maListenerContainer.forEach<presentation::XSlideShowListener>(
2392         boost::bind( &presentation::XSlideShowListener::hyperLinkClicked,
2393                      _1,
2394                      boost::cref(hyperLink) ));
2395     return true;
2396 }
2397 
2398 /** Notification from eventmultiplexer that an animation event has occoured.
2399 	This will be forewarded to all registered XSlideShoeListener
2400  */
2401 bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
2402 {
2403     osl::MutexGuard const guard( m_aMutex );
2404 
2405 	uno::Reference<animations::XAnimationNode> xNode( rNode->getXAnimationNode() );
2406 
2407 	switch( rNode->getState() )
2408 	{
2409 	case AnimationNode::ACTIVE:
2410 	    maListenerContainer.forEach<presentation::XSlideShowListener>(
2411 		    boost::bind( &animations::XAnimationListener::beginEvent,
2412 			             _1,
2413 				         boost::cref(xNode) ));
2414 		break;
2415 
2416 	case AnimationNode::FROZEN:
2417 	case AnimationNode::ENDED:
2418 	    maListenerContainer.forEach<presentation::XSlideShowListener>(
2419 		    boost::bind( &animations::XAnimationListener::endEvent,
2420 			             _1,
2421 				         boost::cref(xNode) ));
2422         if(mpCurrentSlide->isPaintOverlayActive())
2423            mpCurrentSlide->drawPolygons();
2424 		break;
2425 	default:
2426 		break;
2427 	}
2428 
2429 	return true;
2430 }
2431 
2432 
2433 //===== FrameSynchronization ==================================================
2434 
2435 FrameSynchronization::FrameSynchronization (const double nFrameDuration)
2436     : maTimer(),
2437       mnFrameDuration(nFrameDuration),
2438       mnNextFrameTargetTime(0),
2439       mbIsActive(false)
2440 {
2441     MarkCurrentFrame();
2442 }
2443 
2444 
2445 
2446 
2447 void FrameSynchronization::MarkCurrentFrame (void)
2448 {
2449     mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2450 }
2451 
2452 
2453 
2454 
2455 void FrameSynchronization::Synchronize (void)
2456 {
2457     if (mbIsActive)
2458     {
2459         // Do busy waiting for now.
2460         while (maTimer.getElapsedTime() < mnNextFrameTargetTime)
2461             ;
2462     }
2463 
2464     MarkCurrentFrame();
2465 }
2466 
2467 
2468 
2469 
2470 void FrameSynchronization::Activate (void)
2471 {
2472     mbIsActive = true;
2473 }
2474 
2475 
2476 
2477 
2478 void FrameSynchronization::Deactivate (void)
2479 {
2480     mbIsActive = false;
2481 }
2482 
2483 
2484 
2485 
2486 double FrameSynchronization::GetCurrentTime (void) const
2487 {
2488     return maTimer.getElapsedTime();
2489 }
2490 
2491 
2492 } // anon namespace
2493 
2494 namespace sdecl = comphelper::service_decl;
2495 #if defined (__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ <= 3)
2496  sdecl::class_<SlideShowImpl> serviceImpl;
2497  const sdecl::ServiceDecl slideShowDecl(
2498      serviceImpl,
2499 #else
2500  const sdecl::ServiceDecl slideShowDecl(
2501      sdecl::class_<SlideShowImpl>(),
2502 #endif
2503     "com.sun.star.comp.presentation.SlideShow",
2504     "com.sun.star.presentation.SlideShow" );
2505 
2506 // The C shared lib entry points
2507 COMPHELPER_SERVICEDECL_EXPORTS1(slideShowDecl)
2508 
2509