1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: slideshowimpl.cxx,v $
10 * $Revision: 1.10.16.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_slideshow.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
37 #include <cppuhelper/basemutex.hxx>
38 #include <cppuhelper/compbase1.hxx>
39 #include <cppuhelper/factory.hxx>
40 #include <cppuhelper/implementationentry.hxx>
41 #include <cppuhelper/compbase2.hxx>
42 #include <cppuhelper/interfacecontainer.h>
43 #include <cppuhelper/exc_hlp.hxx>
45 #include <comphelper/anytostring.hxx>
46 #include <comphelper/make_shared_from_uno.hxx>
47 #include <comphelper/scopeguard.hxx>
48 #include <comphelper/optional.hxx>
49 #include <comphelper/servicedecl.hxx>
51 #include <cppcanvas/spritecanvas.hxx>
52 #include <cppcanvas/vclfactory.hxx>
53 #include <cppcanvas/basegfxfactory.hxx>
55 #include <tools/debug.hxx>
57 #include <basegfx/point/b2dpoint.hxx>
58 #include <basegfx/polygon/b2dpolygon.hxx>
59 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include <basegfx/polygon/b2dpolygontools.hxx>
61 #include <basegfx/polygon/b2dpolypolygontools.hxx>
62 #include <basegfx/tools/canvastools.hxx>
64 #include <vcl/font.hxx>
66 #include <com/sun/star/beans/XPropertySet.hpp>
67 #include <com/sun/star/util/XModifyListener.hpp>
68 #include <com/sun/star/util/XUpdatable.hpp>
69 #include <com/sun/star/awt/XPaintListener.hpp>
70 #include <com/sun/star/awt/SystemPointer.hpp>
71 #include <com/sun/star/animations/TransitionType.hpp>
72 #include <com/sun/star/animations/TransitionSubType.hpp>
73 #include <com/sun/star/presentation/XSlideShow.hpp>
74 #include <com/sun/star/lang/XServiceInfo.hpp>
75 #include <com/sun/star/lang/XServiceName.hpp>
76 #include <com/sun/star/loader/CannotActivateFactoryException.hpp>
78 #include "unoviewcontainer.hxx"
79 #include "transitionfactory.hxx"
80 #include "eventmultiplexer.hxx"
81 #include "usereventqueue.hxx"
82 #include "eventqueue.hxx"
83 #include "cursormanager.hxx"
84 #include "slideshowcontext.hxx"
85 #include "activitiesqueue.hxx"
86 #include "activitiesfactory.hxx"
87 #include "interruptabledelayevent.hxx"
89 #include "shapemaps.hxx"
90 #include "slideview.hxx"
92 #include "unoview.hxx"
93 #include "slidebitmap.hxx"
94 #include "rehearsetimingsactivity.hxx"
95 #include "waitsymbol.hxx"
96 #include "effectrewinder.hxx"
97 #include "framerate.hxx"
99 #include <boost/noncopyable.hpp>
100 #include <boost/bind.hpp>
108 using namespace com::sun::star
;
109 using namespace ::slideshow::internal
;
113 /******************************************************************************
117 This class encapsulates the slideshow presentation viewer.
119 With an instance of this class, it is possible to statically
120 and dynamically show a presentation, as defined by the
121 constructor-provided draw model (represented by a sequence
122 of ::com::sun::star::drawing::XDrawPage objects).
124 It is possible to show the presentation on multiple views
125 simultaneously (e.g. for a multi-monitor setup). Since this
126 class also relies on user interaction, the corresponding
127 XSlideShowView interface provides means to register some UI
128 event listeners (mostly borrowed from awt::XWindow interface).
130 Since currently (mid 2004), OOo isn't very well suited to
131 multi-threaded rendering, this class relies on <em>very
132 frequent</em> external update() calls, which will render the
133 next frame of animations. This works as follows: after the
134 displaySlide() has been successfully called (which setup and
135 starts an actual slide show), the update() method must be
136 called until it returns false.
137 Effectively, this puts the burden of providing
138 concurrency to the clients of this class, which, as noted
139 above, is currently unavoidable with the current state of
140 affairs (I've actually tried threading here, but failed
141 miserably when using the VCL canvas as the render backend -
144 ******************************************************************************/
146 typedef cppu::WeakComponentImplHelper1
<presentation::XSlideShow
> SlideShowImplBase
;
148 class SlideShowImpl
: private cppu::BaseMutex
,
149 public CursorManager
,
150 public SlideShowImplBase
153 explicit SlideShowImpl(
154 uno::Reference
<uno::XComponentContext
> const& xContext
);
156 /** Notify that the transition phase of the current slide
159 The life of a slide has three phases: the transition
160 phase, when the previous slide vanishes, and the
161 current slide becomes visible, the shape animation
162 phase, when shape effects are running, and the phase
163 after the last shape animation has ended, but before
164 the next slide transition starts.
166 This method notifies the end of the first phase.
169 When true, Slide::show() is passed a true as well, denoting
170 explicit paint of slide content. Pass false here, if e.g. a
171 slide transition has already rendered the initial slide image.
173 void notifySlideTransitionEnded( bool bPaintSlide
);
175 /** Notify that the shape animation phase of the current slide
178 The life of a slide has three phases: the transition
179 phase, when the previous slide vanishes, and the
180 current slide becomes visible, the shape animation
181 phase, when shape effects are running, and the phase
182 after the last shape animation has ended, but before
183 the next slide transition starts.
185 This method notifies the end of the second phase.
187 void notifySlideAnimationsEnded();
189 /** Notify that the slide has ended.
191 The life of a slide has three phases: the transition
192 phase, when the previous slide vanishes, and the
193 current slide becomes visible, the shape animation
194 phase, when shape effects are running, and the phase
195 after the last shape animation has ended, but before
196 the next slide transition starts.
198 This method notifies the end of the third phase.
200 void notifySlideEnded (const bool bReverse
);
202 /** Notification from eventmultiplexer that a hyperlink
205 bool notifyHyperLinkClicked( rtl::OUString
const& hyperLink
);
207 /** Notification from eventmultiplexer that an animation event has occoured.
208 This will be forewarded to all registered XSlideShowListener
210 bool handleAnimationEvent( const AnimationNodeSharedPtr
& rNode
);
214 virtual sal_Bool SAL_CALL
nextEffect() throw (uno::RuntimeException
);
215 virtual sal_Bool SAL_CALL
previousEffect() throw (uno::RuntimeException
);
216 virtual sal_Bool SAL_CALL
startShapeActivity(
217 uno::Reference
<drawing::XShape
> const& xShape
)
218 throw (uno::RuntimeException
);
219 virtual sal_Bool SAL_CALL
stopShapeActivity(
220 uno::Reference
<drawing::XShape
> const& xShape
)
221 throw (uno::RuntimeException
);
222 virtual sal_Bool SAL_CALL
pause( sal_Bool bPauseShow
)
223 throw (uno::RuntimeException
);
224 virtual uno::Reference
<drawing::XDrawPage
> SAL_CALL
getCurrentSlide()
225 throw (uno::RuntimeException
);
226 virtual void SAL_CALL
displaySlide(
227 uno::Reference
<drawing::XDrawPage
> const& xSlide
,
228 uno::Reference
<animations::XAnimationNode
> const& xRootNode
,
229 uno::Sequence
<beans::PropertyValue
> const& rProperties
)
230 throw (uno::RuntimeException
);
231 virtual sal_Bool SAL_CALL
setProperty(
232 beans::PropertyValue
const& rProperty
) throw (uno::RuntimeException
);
233 virtual sal_Bool SAL_CALL
addView(
234 uno::Reference
<presentation::XSlideShowView
> const& xView
)
235 throw (uno::RuntimeException
);
236 virtual sal_Bool SAL_CALL
removeView(
237 uno::Reference
<presentation::XSlideShowView
> const& xView
)
238 throw (uno::RuntimeException
);
239 virtual sal_Bool SAL_CALL
update( double & nNextTimeout
)
240 throw (uno::RuntimeException
);
241 virtual void SAL_CALL
addSlideShowListener(
242 uno::Reference
<presentation::XSlideShowListener
> const& xListener
)
243 throw (uno::RuntimeException
);
244 virtual void SAL_CALL
removeSlideShowListener(
245 uno::Reference
<presentation::XSlideShowListener
> const& xListener
)
246 throw (uno::RuntimeException
);
247 virtual void SAL_CALL
addShapeEventListener(
248 uno::Reference
<presentation::XShapeEventListener
> const& xListener
,
249 uno::Reference
<drawing::XShape
> const& xShape
)
250 throw (uno::RuntimeException
);
251 virtual void SAL_CALL
removeShapeEventListener(
252 uno::Reference
<presentation::XShapeEventListener
> const& xListener
,
253 uno::Reference
<drawing::XShape
> const& xShape
)
254 throw (uno::RuntimeException
);
255 virtual void SAL_CALL
setShapeCursor(
256 uno::Reference
<drawing::XShape
> const& xShape
, sal_Int16 nPointerShape
)
257 throw (uno::RuntimeException
);
261 // -----------------------------------------------------------
263 virtual bool requestCursor( sal_Int16 nCursorShape
);
264 virtual void resetCursor();
266 /** This is somewhat similar to displaySlide when called for the current
267 slide. It has been simplified to take advantage of that no slide
268 change takes place. Furthermore it does not show the slide
271 void redisplayCurrentSlide (void);
274 // WeakComponentImplHelperBase
275 virtual void SAL_CALL
disposing();
277 bool isDisposed() const
279 return (rBHelper
.bDisposed
|| rBHelper
.bInDispose
);
283 struct SeparateListenerImpl
; friend struct SeparateListenerImpl
;
284 struct PrefetchPropertiesFunc
; friend struct PrefetchPropertiesFunc
;
286 /// Stop currently running show.
289 /// Creates a new slide.
290 SlideSharedPtr
makeSlide(
291 uno::Reference
<drawing::XDrawPage
> const& xDrawPage
,
292 uno::Reference
<animations::XAnimationNode
> const& xRootNode
);
294 /// Checks whether the given slide/animation node matches mpPrefetchSlide
296 SlideSharedPtr
const& pSlide
,
297 uno::Reference
<drawing::XDrawPage
> const& xSlide
,
298 uno::Reference
<animations::XAnimationNode
> const& xNode
)
301 return (pSlide
->getXDrawPage() == xSlide
&&
302 pSlide
->getXAnimationNode() == xNode
);
304 return (!xSlide
.is() && !xNode
.is());
307 /// Resets the current slide transition sound object with a new one:
308 SoundPlayerSharedPtr
resetSlideTransitionSound(
309 uno::Any
const& url
= uno::Any(), bool bLoopSound
= false );
311 /// stops the current slide transition sound
312 void stopSlideTransitionSound();
314 /** Prepare a slide transition
316 This method registers all necessary events and
317 activities for a slide transition.
319 @return the slide change activity, or NULL for no transition effect
321 ActivitySharedPtr
createSlideTransition(
322 const uno::Reference
< drawing::XDrawPage
>& xDrawPage
,
323 const SlideSharedPtr
& rLeavingSlide
,
324 const SlideSharedPtr
& rEnteringSlide
,
325 const EventSharedPtr
& rTransitionEndEvent
);
327 /** Request/release the wait symbol. The wait symbol is displayed when
328 there are more requests then releases. Locking the wait symbol
329 helps to avoid intermediate repaints.
331 Do not call this method directly. Use WaitSymbolLock instead.
333 void requestWaitSymbol (void);
334 void releaseWaitSymbol (void);
336 class WaitSymbolLock
{public:
337 WaitSymbolLock(SlideShowImpl
& rSlideShowImpl
) : mrSlideShowImpl(rSlideShowImpl
)
338 { mrSlideShowImpl
.requestWaitSymbol(); }
339 ~WaitSymbolLock(void)
340 { mrSlideShowImpl
.releaseWaitSymbol(); }
341 private: SlideShowImpl
& mrSlideShowImpl
;
345 /// Filter requested cursor shape against hard slideshow cursors (wait, etc.)
346 sal_Int16
calcActiveCursor( sal_Int16 nCursorShape
) const;
348 /** This method is called asynchronously to finish the rewinding of an
349 effect to the previous slide that was initiated earlier.
351 void rewindEffectToPreviousSlide (void);
353 /// all registered views
354 UnoViewContainer maViewContainer
;
356 /// all registered slide show listeners
357 cppu::OInterfaceContainerHelper maListenerContainer
;
359 /// map of vectors, containing all registered listeners for a shape
360 ShapeEventListenerMap maShapeEventListeners
;
361 /// map of sal_Int16 values, specifying the mouse cursor for every shape
362 ShapeCursorMap maShapeCursors
;
364 boost::optional
<RGBColor
> maUserPaintColor
;
366 boost::optional
<double> maUserPaintStrokeWidth
;
368 boost::shared_ptr
<canvas::tools::ElapsedTime
> mpPresTimer
;
369 ScreenUpdater maScreenUpdater
;
370 EventQueue maEventQueue
;
371 EventMultiplexer maEventMultiplexer
;
372 ActivitiesQueue maActivitiesQueue
;
373 UserEventQueue maUserEventQueue
;
374 SubsettableShapeManagerSharedPtr mpDummyPtr
;
376 boost::shared_ptr
<SeparateListenerImpl
> mpListener
;
378 boost::shared_ptr
<RehearseTimingsActivity
> mpRehearseTimingsActivity
;
379 boost::shared_ptr
<WaitSymbol
> mpWaitSymbol
;
381 /// the current slide transition sound object:
382 SoundPlayerSharedPtr mpCurrentSlideTransitionSound
;
384 uno::Reference
<uno::XComponentContext
> mxComponentContext
;
386 presentation::XTransitionFactory
> mxOptionalTransitionFactory
;
388 /// the previously running slide
389 SlideSharedPtr mpPreviousSlide
;
390 /// the currently running slide
391 SlideSharedPtr mpCurrentSlide
;
392 /// the already prefetched slide: best candidate for upcoming slide
393 SlideSharedPtr mpPrefetchSlide
;
394 /// slide to be prefetched: best candidate for upcoming slide
395 uno::Reference
<drawing::XDrawPage
> mxPrefetchSlide
;
396 /// slide animation to be prefetched:
397 uno::Reference
<animations::XAnimationNode
> mxPrefetchAnimationNode
;
399 sal_Int16 mnCurrentCursor
;
401 sal_Int32 mnWaitSymbolRequestCount
;
402 bool mbAutomaticAdvancementMode
;
403 bool mbImageAnimationsAllowed
;
404 bool mbNoSlideTransitions
;
406 bool mbForceManualAdvance
;
408 bool mbSlideShowIdle
;
409 bool mbDisableAnimationZOrder
;
411 EffectRewinder maEffectRewinder
;
415 /** Separate event listener for animation, view and hyperlink events.
417 This handler is registered for slide animation end, view and
418 hyperlink events at the global EventMultiplexer, and forwards
419 notifications to the SlideShowImpl
421 struct SlideShowImpl::SeparateListenerImpl
: public EventHandler
,
422 public ViewRepaintHandler
,
423 public HyperlinkHandler
,
424 public AnimationEventHandler
,
425 private boost::noncopyable
427 SlideShowImpl
& mrShow
;
428 ScreenUpdater
& mrScreenUpdater
;
429 EventQueue
& mrEventQueue
;
431 SeparateListenerImpl( SlideShowImpl
& rShow
,
432 ScreenUpdater
& rScreenUpdater
,
433 EventQueue
& rEventQueue
) :
435 mrScreenUpdater( rScreenUpdater
),
436 mrEventQueue( rEventQueue
)
440 virtual bool handleEvent()
442 // DON't call notifySlideAnimationsEnded()
443 // directly, but queue an event. handleEvent()
444 // might be called from e.g.
445 // showNext(), and notifySlideAnimationsEnded() must not be called
447 mrEventQueue
.addEvent(
448 makeEvent( boost::bind( &SlideShowImpl::notifySlideAnimationsEnded
,
449 boost::ref(mrShow
) )));
453 // ViewRepaintHandler
454 virtual void viewClobbered( const UnoViewSharedPtr
& rView
)
456 // given view needs repaint, request update
457 mrScreenUpdater
.notifyUpdate(rView
, true);
461 virtual bool handleHyperlink( ::rtl::OUString
const& rLink
)
463 return mrShow
.notifyHyperLinkClicked(rLink
);
466 // AnimationEventHandler
467 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr
& rNode
)
469 return mrShow
.handleAnimationEvent(rNode
);
474 SlideShowImpl::SlideShowImpl(
475 uno::Reference
<uno::XComponentContext
> const& xContext
)
476 : SlideShowImplBase(m_aMutex
),
478 maListenerContainer( m_aMutex
),
479 maShapeEventListeners(),
482 maUserPaintStrokeWidth(4.0),
483 mpPresTimer( new canvas::tools::ElapsedTime
),
484 maScreenUpdater(maViewContainer
),
485 maEventQueue( mpPresTimer
),
486 maEventMultiplexer( maEventQueue
,
488 maActivitiesQueue( mpPresTimer
),
489 maUserEventQueue( maEventMultiplexer
,
494 mpRehearseTimingsActivity(),
496 mpCurrentSlideTransitionSound(),
497 mxComponentContext( xContext
),
498 mxOptionalTransitionFactory(),
502 mxPrefetchAnimationNode(),
503 mnCurrentCursor(awt::SystemPointer::ARROW
),
504 mnWaitSymbolRequestCount(0),
505 mbAutomaticAdvancementMode(false),
506 mbImageAnimationsAllowed( true ),
507 mbNoSlideTransitions( false ),
508 mbMouseVisible( true ),
509 mbForceManualAdvance( false ),
510 mbShowPaused( false ),
511 mbSlideShowIdle( true ),
512 mbDisableAnimationZOrder( false ),
513 maEffectRewinder(maEventMultiplexer
, maEventQueue
, maUserEventQueue
)
515 // keep care not constructing any UNO references to this inside ctor,
516 // shift that code to create()!
518 uno::Reference
<lang::XMultiComponentFactory
> xFactory(
519 mxComponentContext
->getServiceManager() );
525 // #i82460# try to retrieve special transition factory
526 mxOptionalTransitionFactory
.set(
527 xFactory
->createInstanceWithContext(
528 ::rtl::OUString::createFromAscii( "com.sun.star.presentation.TransitionFactory" ),
529 mxComponentContext
),
532 catch (loader::CannotActivateFactoryException
const&)
537 mpListener
.reset( new SeparateListenerImpl(
541 maEventMultiplexer
.addSlideAnimationsEndHandler( mpListener
);
542 maEventMultiplexer
.addViewRepaintHandler( mpListener
);
543 maEventMultiplexer
.addHyperlinkHandler( mpListener
, 0.0 );
544 maEventMultiplexer
.addAnimationStartHandler( mpListener
);
545 maEventMultiplexer
.addAnimationEndHandler( mpListener
);
548 // we are about to be disposed (someone call dispose() on us)
549 void SlideShowImpl::disposing()
551 osl::MutexGuard
const guard( m_aMutex
);
553 maEffectRewinder
.dispose();
555 // stop slide transition sound, if any:
556 stopSlideTransitionSound();
558 mxComponentContext
.clear();
560 if( mpCurrentSlideTransitionSound
)
562 mpCurrentSlideTransitionSound
->dispose();
563 mpCurrentSlideTransitionSound
.reset();
566 mpWaitSymbol
.reset();
568 if( mpRehearseTimingsActivity
)
570 mpRehearseTimingsActivity
->dispose();
571 mpRehearseTimingsActivity
.reset();
576 maEventMultiplexer
.removeSlideAnimationsEndHandler(mpListener
);
577 maEventMultiplexer
.removeViewRepaintHandler(mpListener
);
578 maEventMultiplexer
.removeHyperlinkHandler(mpListener
);
579 maEventMultiplexer
.removeAnimationStartHandler( mpListener
);
580 maEventMultiplexer
.removeAnimationEndHandler( mpListener
);
585 maUserEventQueue
.clear();
586 maActivitiesQueue
.clear();
587 maEventMultiplexer
.clear();
588 maEventQueue
.clear();
590 maShapeCursors
.clear();
591 maShapeEventListeners
.clear();
593 // send all listeners a disposing() that we are going down:
594 maListenerContainer
.disposeAndClear(
595 lang::EventObject( static_cast<cppu::OWeakObject
*>(this) ) );
597 maViewContainer
.dispose();
600 mxPrefetchAnimationNode
.clear();
601 mxPrefetchSlide
.clear();
602 mpPrefetchSlide
.reset();
603 mpCurrentSlide
.reset();
604 mpPreviousSlide
.reset();
607 /// stops the current slide transition sound
608 void SlideShowImpl::stopSlideTransitionSound()
610 if (mpCurrentSlideTransitionSound
)
612 mpCurrentSlideTransitionSound
->stopPlayback();
613 mpCurrentSlideTransitionSound
->dispose();
614 mpCurrentSlideTransitionSound
.reset();
618 SoundPlayerSharedPtr
SlideShowImpl::resetSlideTransitionSound( const uno::Any
& rSound
, bool bLoopSound
)
620 sal_Bool bStopSound
= sal_False
;
623 if( !(rSound
>>= bStopSound
) )
624 bStopSound
= sal_False
;
627 if( !bStopSound
&& (url
.getLength() == 0) )
628 return SoundPlayerSharedPtr();
630 stopSlideTransitionSound();
632 if (url
.getLength() > 0)
636 mpCurrentSlideTransitionSound
= SoundPlayer::create(
637 maEventMultiplexer
, url
, mxComponentContext
);
638 mpCurrentSlideTransitionSound
->setPlaybackLoop( bLoopSound
);
640 catch (lang::NoSupportException
const&)
642 // catch possible exceptions from SoundPlayer, since
643 // being not able to playback the sound is not a hard
644 // error here (still, the slide transition should be
648 return mpCurrentSlideTransitionSound
;
651 ActivitySharedPtr
SlideShowImpl::createSlideTransition(
652 const uno::Reference
< drawing::XDrawPage
>& xDrawPage
,
653 const SlideSharedPtr
& rLeavingSlide
,
654 const SlideSharedPtr
& rEnteringSlide
,
655 const EventSharedPtr
& rTransitionEndEvent
)
657 ENSURE_OR_THROW( !maViewContainer
.empty(),
658 "createSlideTransition(): No views" );
659 ENSURE_OR_THROW( rEnteringSlide
,
660 "createSlideTransition(): No entering slide" );
662 // return empty transition, if slide transitions
664 if (mbNoSlideTransitions
)
665 return ActivitySharedPtr();
667 // retrieve slide change parameters from XDrawPage
668 uno::Reference
< beans::XPropertySet
> xPropSet( xDrawPage
,
673 OSL_TRACE( "createSlideTransition(): "
674 "Slide has no PropertySet - assuming no transition\n" );
675 return ActivitySharedPtr();
678 sal_Int16
nTransitionType(0);
679 if( !getPropertyValue( nTransitionType
,
681 OUSTR("TransitionType" )) )
683 OSL_TRACE( "createSlideTransition(): "
684 "Could not extract slide transition type from XDrawPage - assuming no transition\n" );
685 return ActivitySharedPtr();
688 sal_Int16
nTransitionSubType(0);
689 if( !getPropertyValue( nTransitionSubType
,
691 OUSTR("TransitionSubtype" )) )
693 OSL_TRACE( "createSlideTransition(): "
694 "Could not extract slide transition subtype from XDrawPage - assuming no transition\n" );
695 return ActivitySharedPtr();
698 bool bTransitionDirection(false);
699 if( !getPropertyValue( bTransitionDirection
,
701 OUSTR("TransitionDirection")) )
703 OSL_TRACE( "createSlideTransition(): "
704 "Could not extract slide transition direction from XDrawPage - assuming default direction\n" );
707 sal_Int32
aUnoColor(0);
708 if( !getPropertyValue( aUnoColor
,
710 OUSTR("TransitionFadeColor")) )
712 OSL_TRACE( "createSlideTransition(): "
713 "Could not extract slide transition fade color from XDrawPage - assuming black\n" );
716 const RGBColor
aTransitionFadeColor( unoColor2RGBColor( aUnoColor
));
719 sal_Bool bLoopSound
= sal_False
;
721 if( !getPropertyValue( aSound
, xPropSet
, OUSTR("Sound")) )
722 OSL_TRACE( "createSlideTransition(): Could not determine transition sound effect URL from XDrawPage - using no sound\n" );
724 if( !getPropertyValue( bLoopSound
, xPropSet
, OUSTR("LoopSound") ) )
725 OSL_TRACE( "createSlideTransition(): Could not get slide property 'LoopSound' - using no sound\n" );
727 NumberAnimationSharedPtr
pTransition(
728 TransitionFactory::createSlideTransition(
734 mxOptionalTransitionFactory
,
737 bTransitionDirection
,
738 aTransitionFadeColor
,
739 resetSlideTransitionSound( aSound
, bLoopSound
) ));
742 return ActivitySharedPtr(); // no transition effect has been
743 // generated. Normally, that means
744 // that simply no transition is
745 // set on this slide.
747 double nTransitionDuration(0.0);
748 if( !getPropertyValue( nTransitionDuration
,
750 OUSTR("TransitionDuration")) )
752 OSL_TRACE( "createSlideTransition(): "
753 "Could not extract slide transition duration from XDrawPage - assuming no transition\n" );
754 return ActivitySharedPtr();
757 sal_Int32
nMinFrames(5);
758 if( !getPropertyValue( nMinFrames
,
760 OUSTR("MinimalFrameNumber")) )
762 OSL_TRACE( "createSlideTransition(): "
763 "No minimal number of frames given - assuming 5\n" );
766 // prefetch slide transition bitmaps, but postpone it after
767 // displaySlide() has finished - sometimes, view size has not yet
768 // reached final size
769 maEventQueue
.addEvent(
772 &::slideshow::internal::Animation::prefetch
,
774 AnimatableShapeSharedPtr(),
775 ShapeAttributeLayerSharedPtr())));
777 return ActivitySharedPtr(
778 ActivitiesFactory::createSimpleActivity(
779 ActivitiesFactory::CommonParameters(
786 boost::optional
<double>(1.0),
790 rEnteringSlide
->getSlideSize() ),
795 SlideSharedPtr
SlideShowImpl::makeSlide(
796 uno::Reference
<drawing::XDrawPage
> const& xDrawPage
,
797 uno::Reference
<animations::XAnimationNode
> const& xRootNode
)
799 if (! xDrawPage
.is())
800 return SlideSharedPtr();
802 const SlideSharedPtr
pSlide( createSlide(xDrawPage
,
812 maShapeEventListeners
,
814 maUserPaintColor
? *maUserPaintColor
: RGBColor(),
815 *maUserPaintStrokeWidth
,
817 mbImageAnimationsAllowed
,
818 mbDisableAnimationZOrder
) );
820 // prefetch show content (reducing latency for slide
821 // bitmap and effect start later on)
827 void SlideShowImpl::requestWaitSymbol (void)
829 ++mnWaitSymbolRequestCount
;
830 OSL_ASSERT(mnWaitSymbolRequestCount
>0);
832 if (mnWaitSymbolRequestCount
== 1)
836 // fall back to cursor
837 requestCursor(calcActiveCursor(mnCurrentCursor
));
840 mpWaitSymbol
->show();
844 void SlideShowImpl::releaseWaitSymbol (void)
846 --mnWaitSymbolRequestCount
;
847 OSL_ASSERT(mnWaitSymbolRequestCount
>=0);
849 if (mnWaitSymbolRequestCount
== 0)
853 // fall back to cursor
854 requestCursor(calcActiveCursor(mnCurrentCursor
));
857 mpWaitSymbol
->hide();
861 sal_Int16
SlideShowImpl::calcActiveCursor( sal_Int16 nCursorShape
) const
863 if( mnWaitSymbolRequestCount
>0 && !mpWaitSymbol
) // enforce wait cursor
864 nCursorShape
= awt::SystemPointer::WAIT
;
865 else if( !mbMouseVisible
) // enforce INVISIBLE
866 nCursorShape
= awt::SystemPointer::INVISIBLE
;
867 else if( maUserPaintColor
&&
868 nCursorShape
== awt::SystemPointer::ARROW
)
869 nCursorShape
= awt::SystemPointer::PEN
;
875 void SlideShowImpl::stopShow()
877 // Force-end running animation
878 // ===========================
880 mpCurrentSlide
->hide();
883 maEventQueue
.clear();
884 maActivitiesQueue
.clear();
886 // Attention: we MUST clear the user event queue here,
887 // this is because the current slide might have registered
888 // shape events (click or enter/leave), which might
889 // otherwise dangle forever in the queue (because of the
890 // shared ptr nature). If someone needs to change this:
891 // somehow unregister those shapes at the user event queue
892 // on notifySlideEnded().
893 maUserEventQueue
.clear();
895 // re-enable automatic effect advancement
896 // (maEventQueue.clear() above might have killed
897 // maEventMultiplexer's tick events)
898 if (mbAutomaticAdvancementMode
)
900 // toggle automatic mode (enabling just again is
901 // ignored by EventMultiplexer)
902 maEventMultiplexer
.setAutomaticMode( false );
903 maEventMultiplexer
.setAutomaticMode( true );
909 class SlideShowImpl::PrefetchPropertiesFunc
912 PrefetchPropertiesFunc( SlideShowImpl
* that_
,
913 bool& rbSkipAllMainSequenceEffects
,
914 bool& rbSkipSlideTransition
)
915 : mpSlideShowImpl(that_
),
916 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects
),
917 mrbSkipSlideTransition(rbSkipSlideTransition
)
920 void operator()( beans::PropertyValue
const& rProperty
) const {
921 if (rProperty
.Name
.equalsAsciiL(
922 RTL_CONSTASCII_STRINGPARAM("Prefetch") ))
924 uno::Sequence
<uno::Any
> seq
;
925 if ((rProperty
.Value
>>= seq
) && seq
.getLength() == 2)
927 seq
[0] >>= mpSlideShowImpl
->mxPrefetchSlide
;
928 seq
[1] >>= mpSlideShowImpl
->mxPrefetchAnimationNode
;
931 else if (rProperty
.Name
.equalsAsciiL(
932 RTL_CONSTASCII_STRINGPARAM("SkipAllMainSequenceEffects") ))
934 rProperty
.Value
>>= mrbSkipAllMainSequenceEffects
;
936 else if (rProperty
.Name
.equalsAsciiL(
937 RTL_CONSTASCII_STRINGPARAM("SkipSlideTransition") ))
939 rProperty
.Value
>>= mrbSkipSlideTransition
;
943 OSL_ENSURE( false, rtl::OUStringToOString(
944 rProperty
.Name
, RTL_TEXTENCODING_UTF8
).getStr() );
948 SlideShowImpl
*const mpSlideShowImpl
;
949 bool& mrbSkipAllMainSequenceEffects
;
950 bool& mrbSkipSlideTransition
;
953 void SlideShowImpl::displaySlide(
954 uno::Reference
<drawing::XDrawPage
> const& xSlide
,
955 uno::Reference
<animations::XAnimationNode
> const& xRootNode
,
956 uno::Sequence
<beans::PropertyValue
> const& rProperties
)
957 throw (uno::RuntimeException
)
959 osl::MutexGuard
const guard( m_aMutex
);
964 maEffectRewinder
.setRootAnimationNode(xRootNode
);
966 // precondition: must only be called from the main thread!
967 DBG_TESTSOLARMUTEX();
969 stopShow(); // MUST call that: results in
970 // maUserEventQueue.clear(). What's more,
971 // stopShow()'s currSlide->hide() call is
972 // now also required, notifySlideEnded()
974 // unconditionally. Otherwise, genuine
975 // shape animations (drawing layer and
976 // GIF) will not be stopped.
978 bool bSkipAllMainSequenceEffects (false);
979 bool bSkipSlideTransition (false);
980 std::for_each( rProperties
.getConstArray(),
981 rProperties
.getConstArray() + rProperties
.getLength(),
982 PrefetchPropertiesFunc(this, bSkipAllMainSequenceEffects
, bSkipSlideTransition
) );
984 OSL_ENSURE( !maViewContainer
.empty(), "### no views!" );
985 if (maViewContainer
.empty())
988 // this here might take some time
990 WaitSymbolLock
aLock (*this);
992 mpPreviousSlide
= mpCurrentSlide
;
993 mpCurrentSlide
.reset();
995 if (matches( mpPrefetchSlide
, xSlide
, xRootNode
))
997 // prefetched slide matches:
998 mpCurrentSlide
= mpPrefetchSlide
;
1002 mpCurrentSlide
= makeSlide( xSlide
, xRootNode
);
1005 OSL_ASSERT( mpCurrentSlide
);
1008 basegfx::B2DSize oldSlideSize
;
1009 if( mpPreviousSlide
)
1010 oldSlideSize
= mpPreviousSlide
->getSlideSize();
1012 basegfx::B2DSize
const slideSize( mpCurrentSlide
->getSlideSize() );
1014 // push new transformation to all views, if size changed
1015 if( !mpPreviousSlide
|| oldSlideSize
!= slideSize
)
1017 std::for_each( maViewContainer
.begin(),
1018 maViewContainer
.end(),
1019 boost::bind( &View::setViewSize
, _1
,
1020 boost::cref(slideSize
) ));
1022 // explicitly notify view change here,
1023 // because transformation might have changed:
1024 // optimization, this->notifyViewChange() would
1025 // repaint slide which is not necessary.
1026 maEventMultiplexer
.notifyViewsChanged();
1029 // create slide transition, and add proper end event
1030 // (which then starts the slide effects
1031 // via CURRENT_SLIDE.show())
1032 ActivitySharedPtr
pSlideChangeActivity (
1033 createSlideTransition(
1034 mpCurrentSlide
->getXDrawPage(),
1039 &SlideShowImpl::notifySlideTransitionEnded
,
1043 if (bSkipSlideTransition
)
1045 // The transition activity was created for the side effects
1046 // (like sound transitions). Because we want to skip the
1047 // acutual transition animation we do not need the activity
1049 pSlideChangeActivity
.reset();
1052 if (pSlideChangeActivity
)
1054 // factory generated a slide transition - activate it!
1055 maActivitiesQueue
.addActivity( pSlideChangeActivity
);
1059 // no transition effect on this slide - schedule slide
1060 // effect start event right away.
1061 maEventQueue
.addEvent(
1064 &SlideShowImpl::notifySlideTransitionEnded
,
1071 maEventMultiplexer
.notifySlideTransitionStarted();
1072 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
1073 boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted
) );
1075 // We are currently rewinding an effect. This lead us from the next
1076 // slide to this one. To complete this we have to play back all main
1077 // sequence effects on this slide.
1078 if (bSkipAllMainSequenceEffects
)
1079 maEffectRewinder
.skipAllMainSequenceEffects();
1082 void SlideShowImpl::redisplayCurrentSlide (void)
1084 osl::MutexGuard
const guard( m_aMutex
);
1089 // precondition: must only be called from the main thread!
1090 DBG_TESTSOLARMUTEX();
1093 OSL_ENSURE( !maViewContainer
.empty(), "### no views!" );
1094 if (maViewContainer
.empty())
1097 // No transition effect on this slide - schedule slide
1098 // effect start event right away.
1099 maEventQueue
.addEvent(
1102 &SlideShowImpl::notifySlideTransitionEnded
,
1106 maEventMultiplexer
.notifySlideTransitionStarted();
1107 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
1108 boost::mem_fn( &presentation::XSlideShowListener::slideTransitionStarted
) );
1111 sal_Bool
SlideShowImpl::nextEffect() throw (uno::RuntimeException
)
1113 osl::MutexGuard
const guard( m_aMutex
);
1118 // precondition: must only be called from the main thread!
1119 DBG_TESTSOLARMUTEX();
1124 return maEventMultiplexer
.notifyNextEffect();
1128 sal_Bool
SlideShowImpl::previousEffect() throw (uno::RuntimeException
)
1130 osl::MutexGuard
const guard( m_aMutex
);
1135 // precondition: must only be called from the main thread!
1136 DBG_TESTSOLARMUTEX();
1142 return maEffectRewinder
.rewind(
1143 maScreenUpdater
.createLock(false),
1144 ::boost::bind(&SlideShowImpl::redisplayCurrentSlide
, this),
1145 ::boost::bind(&SlideShowImpl::rewindEffectToPreviousSlide
, this));
1149 void SlideShowImpl::rewindEffectToPreviousSlide (void)
1151 // Show the wait symbol now and prevent it from showing temporary slide
1152 // content while effects are played back.
1153 WaitSymbolLock
aLock (*this);
1155 // A previous call to EffectRewinder::Rewind could not rewind the current
1156 // effect because there are no effects on the current slide or none has
1157 // yet been displayed. Go to the previous slide.
1158 notifySlideEnded(true);
1160 // Process pending events once more in order to have the following
1161 // screen update show the last effect. Not sure whether this should be
1163 maEventQueue
.forceEmpty();
1165 // We have to call the screen updater before the wait symbol is turned
1166 // off. Otherwise the wait symbol would force the display of an
1167 // intermediate state of the slide (before the effects are replayed.)
1168 maScreenUpdater
.commitUpdates();
1171 sal_Bool
SlideShowImpl::startShapeActivity(
1172 uno::Reference
<drawing::XShape
> const& /*xShape*/ )
1173 throw (uno::RuntimeException
)
1175 osl::MutexGuard
const guard( m_aMutex
);
1177 // precondition: must only be called from the main thread!
1178 DBG_TESTSOLARMUTEX();
1181 OSL_ENSURE( false, "not yet implemented!" );
1185 sal_Bool
SlideShowImpl::stopShapeActivity(
1186 uno::Reference
<drawing::XShape
> const& /*xShape*/ )
1187 throw (uno::RuntimeException
)
1189 osl::MutexGuard
const guard( m_aMutex
);
1191 // precondition: must only be called from the main thread!
1192 DBG_TESTSOLARMUTEX();
1195 OSL_ENSURE( false, "not yet implemented!" );
1199 sal_Bool
SlideShowImpl::pause( sal_Bool bPauseShow
)
1200 throw (uno::RuntimeException
)
1202 osl::MutexGuard
const guard( m_aMutex
);
1207 // precondition: must only be called from the main thread!
1208 DBG_TESTSOLARMUTEX();
1212 mpPresTimer
->pauseTimer();
1214 mpPresTimer
->continueTimer();
1216 maEventMultiplexer
.notifyPauseMode(bPauseShow
);
1218 mbShowPaused
= bPauseShow
;
1222 uno::Reference
<drawing::XDrawPage
> SlideShowImpl::getCurrentSlide()
1223 throw (uno::RuntimeException
)
1225 osl::MutexGuard
const guard( m_aMutex
);
1228 return uno::Reference
<drawing::XDrawPage
>();
1230 // precondition: must only be called from the main thread!
1231 DBG_TESTSOLARMUTEX();
1234 return mpCurrentSlide
->getXDrawPage();
1236 return uno::Reference
<drawing::XDrawPage
>();
1239 sal_Bool
SlideShowImpl::addView(
1240 uno::Reference
<presentation::XSlideShowView
> const& xView
)
1241 throw (uno::RuntimeException
)
1243 osl::MutexGuard
const guard( m_aMutex
);
1248 // precondition: must only be called from the main thread!
1249 DBG_TESTSOLARMUTEX();
1251 // first of all, check if view has a valid canvas
1252 ENSURE_OR_RETURN( xView
.is(), "addView(): Invalid view" );
1253 ENSURE_OR_RETURN( xView
->getCanvas().is(),
1254 "addView(): View does not provide a valid canvas" );
1256 UnoViewSharedPtr
const pView( createSlideView(
1259 maEventMultiplexer
));
1260 if (!maViewContainer
.addView( pView
))
1261 return false; // view already added
1263 // initialize view content
1264 // =======================
1268 // set view transformation
1269 const basegfx::B2ISize slideSize
= mpCurrentSlide
->getSlideSize();
1270 pView
->setViewSize( basegfx::B2DSize( slideSize
.getX(),
1271 slideSize
.getY() ) );
1274 // clear view area (since its newly added,
1275 // we need a clean slate)
1278 // broadcast newly added view
1279 maEventMultiplexer
.notifyViewAdded( pView
);
1281 // set current mouse ptr
1282 pView
->setCursorShape( calcActiveCursor(mnCurrentCursor
) );
1287 sal_Bool
SlideShowImpl::removeView(
1288 uno::Reference
<presentation::XSlideShowView
> const& xView
)
1289 throw (uno::RuntimeException
)
1291 osl::MutexGuard
const guard( m_aMutex
);
1293 // precondition: must only be called from the main thread!
1294 DBG_TESTSOLARMUTEX();
1296 ENSURE_OR_RETURN( xView
.is(), "removeView(): Invalid view" );
1298 UnoViewSharedPtr
const pView( maViewContainer
.removeView( xView
) );
1300 return false; // view was not added in the first place
1302 // remove view from EventMultiplexer (mouse events etc.)
1303 maEventMultiplexer
.notifyViewRemoved( pView
);
1310 sal_Bool
SlideShowImpl::setProperty( beans::PropertyValue
const& rProperty
)
1311 throw (uno::RuntimeException
)
1313 osl::MutexGuard
const guard( m_aMutex
);
1318 // precondition: must only be called from the main thread!
1319 DBG_TESTSOLARMUTEX();
1321 if (rProperty
.Name
.equalsAsciiL(
1322 RTL_CONSTASCII_STRINGPARAM("AutomaticAdvancement") ))
1324 double nTimeout(0.0);
1325 mbAutomaticAdvancementMode
= (rProperty
.Value
>>= nTimeout
);
1326 if (mbAutomaticAdvancementMode
)
1328 maEventMultiplexer
.setAutomaticTimeout( nTimeout
);
1330 maEventMultiplexer
.setAutomaticMode( mbAutomaticAdvancementMode
);
1334 if (rProperty
.Name
.equalsAsciiL(
1335 RTL_CONSTASCII_STRINGPARAM("UserPaintColor") ))
1337 sal_Int32
nColor(0);
1338 if (rProperty
.Value
>>= nColor
)
1340 OSL_ENSURE( mbMouseVisible
,
1341 "setProperty(): User paint overrides invisible mouse" );
1343 // enable user paint
1344 maUserPaintColor
.reset( unoColor2RGBColor( nColor
) );
1345 maEventMultiplexer
.notifyUserPaintColor( *maUserPaintColor
);
1349 // disable user paint
1350 maUserPaintColor
.reset();
1351 maEventMultiplexer
.notifyUserPaintDisabled();
1354 if( mnCurrentCursor
== awt::SystemPointer::ARROW
)
1360 // new Property for pen's width
1361 if (rProperty
.Name
.equalsAsciiL(
1362 RTL_CONSTASCII_STRINGPARAM("UserPaintStrokeWidth") ))
1365 if (rProperty
.Value
>>= nWidth
)
1367 OSL_ENSURE( mbMouseVisible
,
1368 "setProperty(): User paint overrides invisible mouse" );
1370 // enable user paint stroke width
1371 maUserPaintStrokeWidth
.reset( nWidth
);
1372 maEventMultiplexer
.notifyUserPaintStrokeWidth( *maUserPaintStrokeWidth
);
1376 // disable user paint stroke width
1377 maUserPaintStrokeWidth
.reset();
1378 maEventMultiplexer
.notifyUserPaintDisabled();
1381 if( mnCurrentCursor
== awt::SystemPointer::ARROW
)
1388 if (rProperty
.Name
.equalsAsciiL(
1389 RTL_CONSTASCII_STRINGPARAM("AdvanceOnClick") ))
1391 sal_Bool bAdvanceOnClick
= sal_False
;
1392 if (! (rProperty
.Value
>>= bAdvanceOnClick
))
1394 maUserEventQueue
.setAdvanceOnClick( bAdvanceOnClick
);
1398 if (rProperty
.Name
.equalsAsciiL(
1399 RTL_CONSTASCII_STRINGPARAM("DisableAnimationZOrder") ))
1401 sal_Bool bDisableAnimationZOrder
= sal_False
;
1402 if (! (rProperty
.Value
>>= bDisableAnimationZOrder
))
1404 mbDisableAnimationZOrder
= bDisableAnimationZOrder
== sal_True
;
1408 if (rProperty
.Name
.equalsAsciiL(
1409 RTL_CONSTASCII_STRINGPARAM("ImageAnimationsAllowed") ) )
1411 if (! (rProperty
.Value
>>= mbImageAnimationsAllowed
))
1414 // TODO(F3): Forward to slides!
1415 // if( bOldValue != mbImageAnimationsAllowed )
1417 // if( mbImageAnimationsAllowed )
1418 // maEventMultiplexer.notifyIntrinsicAnimationsEnabled();
1420 // maEventMultiplexer.notifyIntrinsicAnimationsDisabled();
1426 if (rProperty
.Name
.equalsAsciiL(
1427 RTL_CONSTASCII_STRINGPARAM("MouseVisible") ))
1429 if (! (rProperty
.Value
>>= mbMouseVisible
))
1432 requestCursor(mnCurrentCursor
);
1437 if (rProperty
.Name
.equalsAsciiL(
1438 RTL_CONSTASCII_STRINGPARAM("ForceManualAdvance") ))
1440 return (rProperty
.Value
>>= mbForceManualAdvance
);
1443 if (rProperty
.Name
.equalsAsciiL(
1444 RTL_CONSTASCII_STRINGPARAM("RehearseTimings") ))
1446 bool bRehearseTimings
= false;
1447 if (! (rProperty
.Value
>>= bRehearseTimings
))
1450 if (bRehearseTimings
)
1452 // TODO(Q3): Move to slide
1453 mpRehearseTimingsActivity
= RehearseTimingsActivity::create(
1463 mxComponentContext
) );
1465 else if (mpRehearseTimingsActivity
)
1467 // removes timer from all views:
1468 mpRehearseTimingsActivity
->dispose();
1469 mpRehearseTimingsActivity
.reset();
1474 if (rProperty
.Name
.equalsAsciiL(
1475 RTL_CONSTASCII_STRINGPARAM("WaitSymbolBitmap") ))
1477 uno::Reference
<rendering::XBitmap
> xBitmap
;
1478 if (! (rProperty
.Value
>>= xBitmap
))
1481 mpWaitSymbol
= WaitSymbol::create( xBitmap
,
1489 if (rProperty
.Name
.equalsAsciiL(
1490 RTL_CONSTASCII_STRINGPARAM("NoSlideTransitions") ))
1492 return (rProperty
.Value
>>= mbNoSlideTransitions
);
1495 if (rProperty
.Name
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("IsSoundEnabled")))
1497 uno::Sequence
<uno::Any
> aValues
;
1498 uno::Reference
<presentation::XSlideShowView
> xView
;
1499 sal_Bool
bValue (false);
1500 if ((rProperty
.Value
>>= aValues
)
1501 && aValues
.getLength()==2
1502 && (aValues
[0] >>= xView
)
1503 && (aValues
[1] >>= bValue
))
1505 // Look up the view.
1506 for (UnoViewVector::const_iterator
1507 iView (maViewContainer
.begin()),
1508 iEnd (maViewContainer
.end());
1512 if (*iView
&& (*iView
)->getUnoView()==xView
)
1514 // Store the flag at the view so that media shapes have
1516 (*iView
)->setIsSoundEnabled(bValue
);
1526 void SlideShowImpl::addSlideShowListener(
1527 uno::Reference
<presentation::XSlideShowListener
> const& xListener
)
1528 throw (uno::RuntimeException
)
1530 osl::MutexGuard
const guard( m_aMutex
);
1535 // container syncs with passed mutex ref
1536 maListenerContainer
.addInterface(xListener
);
1539 void SlideShowImpl::removeSlideShowListener(
1540 uno::Reference
<presentation::XSlideShowListener
> const& xListener
)
1541 throw (uno::RuntimeException
)
1543 osl::MutexGuard
const guard( m_aMutex
);
1545 // container syncs with passed mutex ref
1546 maListenerContainer
.removeInterface(xListener
);
1549 void SlideShowImpl::addShapeEventListener(
1550 uno::Reference
<presentation::XShapeEventListener
> const& xListener
,
1551 uno::Reference
<drawing::XShape
> const& xShape
)
1552 throw (uno::RuntimeException
)
1554 osl::MutexGuard
const guard( m_aMutex
);
1559 // precondition: must only be called from the main thread!
1560 DBG_TESTSOLARMUTEX();
1562 ShapeEventListenerMap::iterator aIter
;
1563 if( (aIter
=maShapeEventListeners
.find( xShape
)) ==
1564 maShapeEventListeners
.end() )
1566 // no entry for this shape -> create one
1567 aIter
= maShapeEventListeners
.insert(
1568 ShapeEventListenerMap::value_type(
1570 boost::shared_ptr
<cppu::OInterfaceContainerHelper
>(
1571 new cppu::OInterfaceContainerHelper(m_aMutex
)))).first
;
1574 // add new listener to broadcaster
1575 if( aIter
->second
.get() )
1576 aIter
->second
->addInterface( xListener
);
1578 maEventMultiplexer
.notifyShapeListenerAdded(xListener
,
1582 void SlideShowImpl::removeShapeEventListener(
1583 uno::Reference
<presentation::XShapeEventListener
> const& xListener
,
1584 uno::Reference
<drawing::XShape
> const& xShape
)
1585 throw (uno::RuntimeException
)
1587 osl::MutexGuard
const guard( m_aMutex
);
1589 // precondition: must only be called from the main thread!
1590 DBG_TESTSOLARMUTEX();
1592 ShapeEventListenerMap::iterator aIter
;
1593 if( (aIter
= maShapeEventListeners
.find( xShape
)) !=
1594 maShapeEventListeners
.end() )
1596 // entry for this shape found -> remove listener from
1599 aIter
->second
.get(),
1600 "SlideShowImpl::removeShapeEventListener(): "
1601 "listener map contains NULL broadcast helper" );
1603 aIter
->second
->removeInterface( xListener
);
1606 maEventMultiplexer
.notifyShapeListenerRemoved(xListener
,
1610 void SlideShowImpl::setShapeCursor(
1611 uno::Reference
<drawing::XShape
> const& xShape
, sal_Int16 nPointerShape
)
1612 throw (uno::RuntimeException
)
1614 osl::MutexGuard
const guard( m_aMutex
);
1619 // precondition: must only be called from the main thread!
1620 DBG_TESTSOLARMUTEX();
1622 ShapeCursorMap::iterator aIter
;
1623 if( (aIter
=maShapeCursors
.find( xShape
)) == maShapeCursors
.end() )
1625 // no entry for this shape -> create one
1626 if( nPointerShape
!= awt::SystemPointer::ARROW
)
1628 // add new entry, unless shape shall display
1629 // normal pointer arrow -> no need to handle that
1631 maShapeCursors
.insert(
1632 ShapeCursorMap::value_type(xShape
,
1636 else if( nPointerShape
== awt::SystemPointer::ARROW
)
1638 // shape shall display normal cursor -> can disable
1639 // the cursor and clear the entry
1640 maShapeCursors
.erase( xShape
);
1644 // existing entry found, update with new cursor ID
1645 aIter
->second
= nPointerShape
;
1648 maEventMultiplexer
.notifyShapeCursorChange(xShape
,
1652 bool SlideShowImpl::requestCursor( sal_Int16 nCursorShape
)
1654 mnCurrentCursor
= nCursorShape
;
1656 const sal_Int16 nActualCursor
= calcActiveCursor(mnCurrentCursor
);
1658 // change all views to the requested cursor ID
1659 std::for_each( maViewContainer
.begin(),
1660 maViewContainer
.end(),
1661 boost::bind( &View::setCursorShape
,
1665 return nActualCursor
==nCursorShape
;
1668 void SlideShowImpl::resetCursor()
1670 mnCurrentCursor
= awt::SystemPointer::ARROW
;
1672 // change all views to the default cursor ID
1673 std::for_each( maViewContainer
.begin(),
1674 maViewContainer
.end(),
1675 boost::bind( &View::setCursorShape
,
1677 calcActiveCursor(mnCurrentCursor
) ));
1680 sal_Bool
SlideShowImpl::update( double & nNextTimeout
)
1681 throw (uno::RuntimeException
)
1683 osl::MutexGuard
const guard( m_aMutex
);
1688 // precondition: update() must only be called from the
1690 DBG_TESTSOLARMUTEX();
1694 // commit frame (might be repaints pending)
1695 maScreenUpdater
.commitUpdates();
1701 // TODO(F2): re-evaluate whether that timer lagging makes
1704 // hold timer, while processing the queues (ensures
1705 // same time for all activities and events)
1707 comphelper::ScopeGuard
scopeGuard(
1708 boost::bind( &canvas::tools::ElapsedTime::releaseTimer
,
1709 boost::cref(mpPresTimer
) ) );
1711 // no need to hold timer for only one active animation -
1712 // it's only meant to keep multiple ones in sync
1713 if( maActivitiesQueue
.size() > 1 )
1714 mpPresTimer
->holdTimer();
1716 scopeGuard
.dismiss(); // we're not holding the timer
1719 maEventQueue
.process();
1720 maActivitiesQueue
.process();
1722 // commit frame to screen
1723 maScreenUpdater
.commitUpdates();
1725 // TODO(Q3): remove need to call dequeued() from
1726 // activities. feels like a wart.
1728 // Rationale for ActivitiesQueue::processDequeued(): when
1729 // an activity ends, it usually pushed the end state to
1730 // the animated shape in question, and ends the animation
1731 // (which, in turn, will usually disable shape sprite
1732 // mode). Disabling shape sprite mode causes shape
1733 // repaint, which, depending on slide content, takes
1734 // considerably more time than sprite updates. Thus, the
1735 // last animation step tends to look delayed. To
1736 // camouflage this, reaching end position and disabling
1737 // sprite mode is split into two (normal Activity::end(),
1738 // and Activity::dequeued()). Now, the reason to call
1739 // commitUpdates() twice here is caused by the unrelated
1740 // fact that during wait cursor display/hide, the screen
1741 // is updated, and shows hidden sprites, but, in case of
1742 // leaving the second commitUpdates() call out and punting
1743 // that to the next round, no updated static slide
1744 // content. In short, the last shape animation of a slide
1745 // tends to blink at its end.
1747 // process dequeued activities _after_ commit to screen
1748 maActivitiesQueue
.processDequeued();
1750 // commit frame to screen
1751 maScreenUpdater
.commitUpdates();
1753 // Time held until here
1755 const bool bActivitiesLeft
= (! maActivitiesQueue
.isEmpty());
1756 const bool bTimerEventsLeft
= (! maEventQueue
.isEmpty());
1757 const bool bRet
= (bActivitiesLeft
|| bTimerEventsLeft
);
1761 // calc nNextTimeout value:
1762 if (bActivitiesLeft
)
1764 // Activity queue is not empty. Tell caller that we would
1765 // like to render another frame.
1766 nNextTimeout
= 1.0 / FrameRate::PreferredFramesPerSecond
;
1770 // timer events left:
1771 // difference from current time (nota bene:
1772 // time no longer held here!) to the next event in
1775 // #i61190# Retrieve next timeout only _after_
1776 // processing activity queue
1778 // ensure positive value:
1779 nNextTimeout
= std::max( 0.0, maEventQueue
.nextTimeout() );
1782 mbSlideShowIdle
= false;
1785 #if defined(VERBOSE) && defined(DBG_UTIL)
1786 // when slideshow is idle, issue an XUpdatable::update() call
1787 // exactly once after a previous animation sequence finished -
1788 // this might trigger screen dumps on some canvas
1790 if( !mbSlideShowIdle
&&
1792 nNextTimeout
> 1.0) )
1794 UnoViewVector::const_iterator
aCurr(maViewContainer
.begin());
1795 const UnoViewVector::const_iterator
aEnd(maViewContainer
.end());
1796 while( aCurr
!= aEnd
)
1800 uno::Reference
< presentation::XSlideShowView
> xView( (*aCurr
)->getUnoView(),
1801 uno::UNO_QUERY_THROW
);
1802 uno::Reference
< util::XUpdatable
> xUpdatable( xView
->getCanvas(),
1803 uno::UNO_QUERY_THROW
);
1804 xUpdatable
->update();
1806 catch( uno::RuntimeException
& )
1810 catch( uno::Exception
& )
1813 rtl::OUStringToOString(
1814 comphelper::anyToString( cppu::getCaughtException() ),
1815 RTL_TEXTENCODING_UTF8
).getStr() );
1821 mbSlideShowIdle
= true;
1829 void SlideShowImpl::notifySlideTransitionEnded( bool bPaintSlide
)
1831 osl::MutexGuard
const guard( m_aMutex
);
1833 OSL_ENSURE( !isDisposed(), "### already disposed!" );
1834 OSL_ENSURE( mpCurrentSlide
,
1835 "notifySlideTransitionEnded(): Invalid current slide" );
1838 // first init show, to give the animations
1839 // the chance to register SlideStartEvents
1840 const bool bBackgroundLayerRendered( !bPaintSlide
);
1841 mpCurrentSlide
->show( bBackgroundLayerRendered
);
1842 maEventMultiplexer
.notifySlideStartEvent();
1846 void queryAutomaticSlideTransition( uno::Reference
<drawing::XDrawPage
> const& xDrawPage
,
1847 double& nAutomaticNextSlideTimeout
,
1848 bool& bHasAutomaticNextSlide
)
1850 // retrieve slide change parameters from XDrawPage
1851 // ===============================================
1853 uno::Reference
< beans::XPropertySet
> xPropSet( xDrawPage
,
1856 sal_Int32
nChange(0);
1857 if( !xPropSet
.is() ||
1858 !getPropertyValue( nChange
,
1861 RTL_CONSTASCII_USTRINGPARAM("Change"))) )
1864 "queryAutomaticSlideTransition(): "
1865 "Could not extract slide change mode from XDrawPage - assuming <none>\n" );
1868 bHasAutomaticNextSlide
= nChange
== 1;
1870 if( !xPropSet
.is() ||
1871 !getPropertyValue( nAutomaticNextSlideTimeout
,
1874 RTL_CONSTASCII_USTRINGPARAM("Duration"))) )
1877 "queryAutomaticSlideTransition(): "
1878 "Could not extract slide transition timeout from "
1879 "XDrawPage - assuming 1 sec\n" );
1883 void SlideShowImpl::notifySlideAnimationsEnded()
1885 osl::MutexGuard
const guard( m_aMutex
);
1887 OSL_ENSURE( !isDisposed(), "### already disposed!" );
1889 // This struct will receive the (interruptable) event,
1890 // that triggers the notifySlideEnded() method.
1891 InterruptableEventPair aNotificationEvents
;
1893 if( maEventMultiplexer
.getAutomaticMode() )
1895 OSL_ENSURE( ! mpRehearseTimingsActivity
,
1896 "unexpected: RehearseTimings mode!" );
1898 // schedule a slide end event, with automatic mode's
1900 aNotificationEvents
= makeInterruptableDelay(
1901 boost::bind
<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded
), this, false ),
1902 maEventMultiplexer
.getAutomaticTimeout() );
1906 OSL_ENSURE( mpCurrentSlide
,
1907 "notifySlideAnimationsEnded(): Invalid current slide!" );
1909 bool bHasAutomaticNextSlide
=false;
1910 double nAutomaticNextSlideTimeout
=0.0;
1911 queryAutomaticSlideTransition(mpCurrentSlide
->getXDrawPage(),
1912 nAutomaticNextSlideTimeout
,
1913 bHasAutomaticNextSlide
);
1915 // check whether slide transition should happen
1916 // 'automatically'. If yes, simply schedule the
1917 // specified timeout.
1918 // NOTE: mbForceManualAdvance and mpRehearseTimingsActivity
1919 // override any individual slide setting, to always
1920 // step slides manually.
1921 if( !mbForceManualAdvance
&&
1922 !mpRehearseTimingsActivity
&&
1923 bHasAutomaticNextSlide
)
1925 aNotificationEvents
= makeInterruptableDelay(
1926 boost::bind
<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded
), this, false ),
1927 nAutomaticNextSlideTimeout
);
1929 // TODO(F2): Provide a mechanism to let the user override
1930 // this automatic timeout via next()
1934 if (mpRehearseTimingsActivity
)
1935 mpRehearseTimingsActivity
->start();
1937 // generate click event. Thus, the user must
1938 // trigger the actual end of a slide. No need to
1939 // generate interruptable event here, there's no
1940 // timeout involved.
1941 aNotificationEvents
.mpImmediateEvent
=
1942 makeEvent( boost::bind
<void>(
1943 boost::mem_fn(&SlideShowImpl::notifySlideEnded
), this, false ) );
1947 // register events on the queues. To make automatic slide
1948 // changes interruptable, register the interruption event
1949 // as a nextEffectEvent target. Note that the timeout
1950 // event is optional (e.g. manual slide changes don't
1951 // generate a timeout)
1952 maUserEventQueue
.registerNextEffectEvent(
1953 aNotificationEvents
.mpImmediateEvent
);
1955 if( aNotificationEvents
.mpTimeoutEvent
)
1956 maEventQueue
.addEvent( aNotificationEvents
.mpTimeoutEvent
);
1958 // current slide's main sequence is over. Now should be
1959 // the time to prefetch the next slide (if any), and
1960 // prepare the initial slide bitmap (speeds up slide
1961 // change setup time a lot). Show the wait cursor, this
1962 // indeed might take some seconds.
1964 WaitSymbolLock
aLock (*this);
1966 if (! matches( mpPrefetchSlide
,
1967 mxPrefetchSlide
, mxPrefetchAnimationNode
))
1969 mpPrefetchSlide
= makeSlide( mxPrefetchSlide
,
1970 mxPrefetchAnimationNode
);
1972 if (mpPrefetchSlide
)
1974 // ignore return value, this is just to populate
1975 // Slide's internal bitmap buffer, such that the time
1976 // needed to generate the slide bitmap is not spent
1977 // when the slide change is requested.
1978 mpPrefetchSlide
->getCurrentSlideBitmap( *maViewContainer
.begin() );
1982 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
1983 boost::mem_fn( &presentation::XSlideShowListener::slideAnimationsEnded
) );
1986 void SlideShowImpl::notifySlideEnded (const bool bReverse
)
1988 osl::MutexGuard
const guard( m_aMutex
);
1990 OSL_ENSURE( !isDisposed(), "### already disposed!" );
1992 if (mpRehearseTimingsActivity
&& !bReverse
)
1994 const double time
= mpRehearseTimingsActivity
->stop();
1995 if (mpRehearseTimingsActivity
->hasBeenClicked())
1997 // save time at current drawpage:
1998 uno::Reference
<beans::XPropertySet
> xPropSet(
1999 mpCurrentSlide
->getXDrawPage(), uno::UNO_QUERY
);
2000 OSL_ASSERT( xPropSet
.is() );
2003 xPropSet
->setPropertyValue(
2005 uno::Any( static_cast<sal_Int32
>(1) ) );
2006 xPropSet
->setPropertyValue(
2008 uno::Any( static_cast<sal_Int32
>(time
) ) );
2014 maEventMultiplexer
.notifySlideEndEvent();
2016 stopShow(); // MUST call that: results in
2017 // maUserEventQueue.clear(). What's more,
2018 // stopShow()'s currSlide->hide() call is
2019 // now also required, notifySlideEnded()
2021 // unconditionally. Otherwise, genuine
2022 // shape animations (drawing layer and
2023 // GIF) will not be stopped.
2025 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
2027 &presentation::XSlideShowListener::slideEnded
,
2032 bool SlideShowImpl::notifyHyperLinkClicked( rtl::OUString
const& hyperLink
)
2034 osl::MutexGuard
const guard( m_aMutex
);
2036 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
2037 boost::bind( &presentation::XSlideShowListener::hyperLinkClicked
,
2039 boost::cref(hyperLink
) ));
2043 /** Notification from eventmultiplexer that an animation event has occoured.
2044 This will be forewarded to all registered XSlideShoeListener
2046 bool SlideShowImpl::handleAnimationEvent( const AnimationNodeSharedPtr
& rNode
)
2048 osl::MutexGuard
const guard( m_aMutex
);
2050 uno::Reference
<animations::XAnimationNode
> xNode( rNode
->getXAnimationNode() );
2052 switch( rNode
->getState() )
2054 case AnimationNode::ACTIVE
:
2055 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
2056 boost::bind( &animations::XAnimationListener::beginEvent
,
2058 boost::cref(xNode
) ));
2061 case AnimationNode::FROZEN
:
2062 case AnimationNode::ENDED
:
2063 maListenerContainer
.forEach
<presentation::XSlideShowListener
>(
2064 boost::bind( &animations::XAnimationListener::endEvent
,
2066 boost::cref(xNode
) ));
2077 namespace sdecl
= comphelper::service_decl
;
2078 #if defined (__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ <= 3)
2079 sdecl::class_
<SlideShowImpl
> serviceImpl
;
2080 const sdecl::ServiceDecl
slideShowDecl(
2083 const sdecl::ServiceDecl
slideShowDecl(
2084 sdecl::class_
<SlideShowImpl
>(),
2086 "com.sun.star.comp.presentation.SlideShow",
2087 "com.sun.star.presentation.SlideShow" );
2089 // The C shared lib entry points
2090 COMPHELPER_SERVICEDECL_EXPORTS1(slideShowDecl
)