Update ooo320-m1
[ooovba.git] / slideshow / source / engine / slideshowimpl.cxx
blob8de2ac3b479426fb7e71f257b0a831f913a94fcc
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
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"
88 #include "slide.hxx"
89 #include "shapemaps.hxx"
90 #include "slideview.hxx"
91 #include "tools.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>
102 #include <map>
103 #include <vector>
104 #include <iterator>
105 #include <algorithm>
106 #include <stdio.h>
108 using namespace com::sun::star;
109 using namespace ::slideshow::internal;
111 namespace {
113 /******************************************************************************
115 SlideShowImpl
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 -
142 deadlocked).
144 ******************************************************************************/
146 typedef cppu::WeakComponentImplHelper1<presentation::XSlideShow> SlideShowImplBase;
148 class SlideShowImpl : private cppu::BaseMutex,
149 public CursorManager,
150 public SlideShowImplBase
152 public:
153 explicit SlideShowImpl(
154 uno::Reference<uno::XComponentContext> const& xContext );
156 /** Notify that the transition phase of the current slide
157 has ended.
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.
168 @param bPaintSlide
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
176 has ended.
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
203 has been clicked.
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 );
212 private:
213 // XSlideShow:
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);
260 // CursorManager
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
269 transition.
271 void redisplayCurrentSlide (void);
273 protected:
274 // WeakComponentImplHelperBase
275 virtual void SAL_CALL disposing();
277 bool isDisposed() const
279 return (rBHelper.bDisposed || rBHelper.bInDispose);
282 private:
283 struct SeparateListenerImpl; friend struct SeparateListenerImpl;
284 struct PrefetchPropertiesFunc; friend struct PrefetchPropertiesFunc;
286 /// Stop currently running show.
287 void stopShow();
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
295 static bool matches(
296 SlideSharedPtr const& pSlide,
297 uno::Reference<drawing::XDrawPage> const& xSlide,
298 uno::Reference<animations::XAnimationNode> const& xNode )
300 if (pSlide)
301 return (pSlide->getXDrawPage() == xSlide &&
302 pSlide->getXAnimationNode() == xNode);
303 else
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;
385 uno::Reference<
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;
405 bool mbMouseVisible;
406 bool mbForceManualAdvance;
407 bool mbShowPaused;
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 ) :
434 mrShow( rShow ),
435 mrScreenUpdater( rScreenUpdater ),
436 mrEventQueue( rEventQueue )
439 // EventHandler
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
446 // in recursion.
447 mrEventQueue.addEvent(
448 makeEvent( boost::bind( &SlideShowImpl::notifySlideAnimationsEnded,
449 boost::ref(mrShow) )));
450 return true;
453 // ViewRepaintHandler
454 virtual void viewClobbered( const UnoViewSharedPtr& rView )
456 // given view needs repaint, request update
457 mrScreenUpdater.notifyUpdate(rView, true);
460 // HyperlinkHandler
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),
477 maViewContainer(),
478 maListenerContainer( m_aMutex ),
479 maShapeEventListeners(),
480 maShapeCursors(),
481 maUserPaintColor(),
482 maUserPaintStrokeWidth(4.0),
483 mpPresTimer( new canvas::tools::ElapsedTime ),
484 maScreenUpdater(maViewContainer),
485 maEventQueue( mpPresTimer ),
486 maEventMultiplexer( maEventQueue,
487 maViewContainer ),
488 maActivitiesQueue( mpPresTimer ),
489 maUserEventQueue( maEventMultiplexer,
490 maEventQueue,
491 *this ),
492 mpDummyPtr(),
493 mpListener(),
494 mpRehearseTimingsActivity(),
495 mpWaitSymbol(),
496 mpCurrentSlideTransitionSound(),
497 mxComponentContext( xContext ),
498 mxOptionalTransitionFactory(),
499 mpCurrentSlide(),
500 mpPrefetchSlide(),
501 mxPrefetchSlide(),
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() );
521 if( xFactory.is() )
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 ),
530 uno::UNO_QUERY );
532 catch (loader::CannotActivateFactoryException const&)
537 mpListener.reset( new SeparateListenerImpl(
538 *this,
539 maScreenUpdater,
540 maEventQueue ));
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();
574 if( mpListener )
576 maEventMultiplexer.removeSlideAnimationsEndHandler(mpListener);
577 maEventMultiplexer.removeViewRepaintHandler(mpListener);
578 maEventMultiplexer.removeHyperlinkHandler(mpListener);
579 maEventMultiplexer.removeAnimationStartHandler( mpListener );
580 maEventMultiplexer.removeAnimationEndHandler( mpListener );
582 mpListener.reset();
585 maUserEventQueue.clear();
586 maActivitiesQueue.clear();
587 maEventMultiplexer.clear();
588 maEventQueue.clear();
589 mpPresTimer.reset();
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();
599 // release slides:
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;
621 rtl::OUString url;
623 if( !(rSound >>= bStopSound) )
624 bStopSound = sal_False;
625 rSound >>= url;
627 if( !bStopSound && (url.getLength() == 0) )
628 return SoundPlayerSharedPtr();
630 stopSlideTransitionSound();
632 if (url.getLength() > 0)
634 try
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
645 // shown).
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
663 // are disabled.
664 if (mbNoSlideTransitions)
665 return ActivitySharedPtr();
667 // retrieve slide change parameters from XDrawPage
668 uno::Reference< beans::XPropertySet > xPropSet( xDrawPage,
669 uno::UNO_QUERY );
671 if( !xPropSet.is() )
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,
680 xPropSet,
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,
690 xPropSet,
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,
700 xPropSet,
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,
709 xPropSet,
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 ));
718 uno::Any aSound;
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(
729 rLeavingSlide,
730 rEnteringSlide,
731 maViewContainer,
732 maScreenUpdater,
733 maEventMultiplexer,
734 mxOptionalTransitionFactory,
735 nTransitionType,
736 nTransitionSubType,
737 bTransitionDirection,
738 aTransitionFadeColor,
739 resetSlideTransitionSound( aSound, bLoopSound ) ));
741 if( !pTransition )
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,
749 xPropSet,
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,
759 xPropSet,
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(
770 makeEvent(
771 boost::bind(
772 &::slideshow::internal::Animation::prefetch,
773 pTransition,
774 AnimatableShapeSharedPtr(),
775 ShapeAttributeLayerSharedPtr())));
777 return ActivitySharedPtr(
778 ActivitiesFactory::createSimpleActivity(
779 ActivitiesFactory::CommonParameters(
780 rTransitionEndEvent,
781 maEventQueue,
782 maActivitiesQueue,
783 nTransitionDuration,
784 nMinFrames,
785 false,
786 boost::optional<double>(1.0),
787 0.0,
788 0.0,
789 ShapeSharedPtr(),
790 rEnteringSlide->getSlideSize() ),
791 pTransition,
792 true ));
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,
803 xRootNode,
804 maEventQueue,
805 maEventMultiplexer,
806 maScreenUpdater,
807 maActivitiesQueue,
808 maUserEventQueue,
809 *this,
810 maViewContainer,
811 mxComponentContext,
812 maShapeEventListeners,
813 maShapeCursors,
814 maUserPaintColor ? *maUserPaintColor : RGBColor(),
815 *maUserPaintStrokeWidth,
816 !!maUserPaintColor,
817 mbImageAnimationsAllowed,
818 mbDisableAnimationZOrder) );
820 // prefetch show content (reducing latency for slide
821 // bitmap and effect start later on)
822 pSlide->prefetch();
824 return pSlide;
827 void SlideShowImpl::requestWaitSymbol (void)
829 ++mnWaitSymbolRequestCount;
830 OSL_ASSERT(mnWaitSymbolRequestCount>0);
832 if (mnWaitSymbolRequestCount == 1)
834 if( !mpWaitSymbol )
836 // fall back to cursor
837 requestCursor(calcActiveCursor(mnCurrentCursor));
839 else
840 mpWaitSymbol->show();
844 void SlideShowImpl::releaseWaitSymbol (void)
846 --mnWaitSymbolRequestCount;
847 OSL_ASSERT(mnWaitSymbolRequestCount>=0);
849 if (mnWaitSymbolRequestCount == 0)
851 if( !mpWaitSymbol )
853 // fall back to cursor
854 requestCursor(calcActiveCursor(mnCurrentCursor));
856 else
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;
871 return nCursorShape;
875 void SlideShowImpl::stopShow()
877 // Force-end running animation
878 // ===========================
879 if (mpCurrentSlide)
880 mpCurrentSlide->hide();
882 // clear all queues
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
911 public:
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;
941 else
943 OSL_ENSURE( false, rtl::OUStringToOString(
944 rProperty.Name, RTL_TEXTENCODING_UTF8 ).getStr() );
947 private:
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 );
961 if (isDisposed())
962 return;
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()
973 // relies on that
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())
986 return;
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;
1000 else
1002 mpCurrentSlide = makeSlide( xSlide, xRootNode );
1005 OSL_ASSERT( mpCurrentSlide );
1006 if (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(),
1035 mpPreviousSlide,
1036 mpCurrentSlide,
1037 makeEvent(
1038 boost::bind(
1039 &SlideShowImpl::notifySlideTransitionEnded,
1040 this,
1041 false ))));
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
1048 // anymore.
1049 pSlideChangeActivity.reset();
1052 if (pSlideChangeActivity)
1054 // factory generated a slide transition - activate it!
1055 maActivitiesQueue.addActivity( pSlideChangeActivity );
1057 else
1059 // no transition effect on this slide - schedule slide
1060 // effect start event right away.
1061 maEventQueue.addEvent(
1062 makeEvent(
1063 boost::bind(
1064 &SlideShowImpl::notifySlideTransitionEnded,
1065 this,
1066 true )));
1069 } // finally
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 );
1086 if (isDisposed())
1087 return;
1089 // precondition: must only be called from the main thread!
1090 DBG_TESTSOLARMUTEX();
1091 stopShow();
1093 OSL_ENSURE( !maViewContainer.empty(), "### no views!" );
1094 if (maViewContainer.empty())
1095 return;
1097 // No transition effect on this slide - schedule slide
1098 // effect start event right away.
1099 maEventQueue.addEvent(
1100 makeEvent(
1101 boost::bind(
1102 &SlideShowImpl::notifySlideTransitionEnded,
1103 this,
1104 true )));
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 );
1115 if (isDisposed())
1116 return false;
1118 // precondition: must only be called from the main thread!
1119 DBG_TESTSOLARMUTEX();
1121 if (mbShowPaused)
1122 return true;
1123 else
1124 return maEventMultiplexer.notifyNextEffect();
1128 sal_Bool SlideShowImpl::previousEffect() throw (uno::RuntimeException)
1130 osl::MutexGuard const guard( m_aMutex );
1132 if (isDisposed())
1133 return false;
1135 // precondition: must only be called from the main thread!
1136 DBG_TESTSOLARMUTEX();
1138 if (mbShowPaused)
1139 return true;
1140 else
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
1162 // necessary.
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();
1180 // TODO(F3): NYI
1181 OSL_ENSURE( false, "not yet implemented!" );
1182 return false;
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();
1194 // TODO(F3): NYI
1195 OSL_ENSURE( false, "not yet implemented!" );
1196 return false;
1199 sal_Bool SlideShowImpl::pause( sal_Bool bPauseShow )
1200 throw (uno::RuntimeException)
1202 osl::MutexGuard const guard( m_aMutex );
1204 if (isDisposed())
1205 return false;
1207 // precondition: must only be called from the main thread!
1208 DBG_TESTSOLARMUTEX();
1211 if (bPauseShow)
1212 mpPresTimer->pauseTimer();
1213 else
1214 mpPresTimer->continueTimer();
1216 maEventMultiplexer.notifyPauseMode(bPauseShow);
1218 mbShowPaused = bPauseShow;
1219 return true;
1222 uno::Reference<drawing::XDrawPage> SlideShowImpl::getCurrentSlide()
1223 throw (uno::RuntimeException)
1225 osl::MutexGuard const guard( m_aMutex );
1227 if (isDisposed())
1228 return uno::Reference<drawing::XDrawPage>();
1230 // precondition: must only be called from the main thread!
1231 DBG_TESTSOLARMUTEX();
1233 if (mpCurrentSlide)
1234 return mpCurrentSlide->getXDrawPage();
1235 else
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 );
1245 if (isDisposed())
1246 return false;
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(
1257 xView,
1258 maEventQueue,
1259 maEventMultiplexer ));
1260 if (!maViewContainer.addView( pView ))
1261 return false; // view already added
1263 // initialize view content
1264 // =======================
1266 if (mpCurrentSlide)
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)
1276 pView->clearAll();
1278 // broadcast newly added view
1279 maEventMultiplexer.notifyViewAdded( pView );
1281 // set current mouse ptr
1282 pView->setCursorShape( calcActiveCursor(mnCurrentCursor) );
1284 return true;
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 ) );
1299 if( !pView )
1300 return false; // view was not added in the first place
1302 // remove view from EventMultiplexer (mouse events etc.)
1303 maEventMultiplexer.notifyViewRemoved( pView );
1305 pView->_dispose();
1307 return true;
1310 sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty )
1311 throw (uno::RuntimeException)
1313 osl::MutexGuard const guard( m_aMutex );
1315 if (isDisposed())
1316 return false;
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 );
1331 return true;
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 );
1347 else
1349 // disable user paint
1350 maUserPaintColor.reset();
1351 maEventMultiplexer.notifyUserPaintDisabled();
1354 if( mnCurrentCursor == awt::SystemPointer::ARROW )
1355 resetCursor();
1357 return true;
1360 // new Property for pen's width
1361 if (rProperty.Name.equalsAsciiL(
1362 RTL_CONSTASCII_STRINGPARAM("UserPaintStrokeWidth") ))
1364 double nWidth(4.0);
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 );
1374 else
1376 // disable user paint stroke width
1377 maUserPaintStrokeWidth.reset();
1378 maEventMultiplexer.notifyUserPaintDisabled();
1381 if( mnCurrentCursor == awt::SystemPointer::ARROW )
1382 resetCursor();
1384 return true;
1388 if (rProperty.Name.equalsAsciiL(
1389 RTL_CONSTASCII_STRINGPARAM("AdvanceOnClick") ))
1391 sal_Bool bAdvanceOnClick = sal_False;
1392 if (! (rProperty.Value >>= bAdvanceOnClick))
1393 return false;
1394 maUserEventQueue.setAdvanceOnClick( bAdvanceOnClick );
1395 return true;
1398 if (rProperty.Name.equalsAsciiL(
1399 RTL_CONSTASCII_STRINGPARAM("DisableAnimationZOrder") ))
1401 sal_Bool bDisableAnimationZOrder = sal_False;
1402 if (! (rProperty.Value >>= bDisableAnimationZOrder))
1403 return false;
1404 mbDisableAnimationZOrder = bDisableAnimationZOrder == sal_True;
1405 return true;
1408 if (rProperty.Name.equalsAsciiL(
1409 RTL_CONSTASCII_STRINGPARAM("ImageAnimationsAllowed") ) )
1411 if (! (rProperty.Value >>= mbImageAnimationsAllowed))
1412 return false;
1414 // TODO(F3): Forward to slides!
1415 // if( bOldValue != mbImageAnimationsAllowed )
1416 // {
1417 // if( mbImageAnimationsAllowed )
1418 // maEventMultiplexer.notifyIntrinsicAnimationsEnabled();
1419 // else
1420 // maEventMultiplexer.notifyIntrinsicAnimationsDisabled();
1421 // }
1423 return true;
1426 if (rProperty.Name.equalsAsciiL(
1427 RTL_CONSTASCII_STRINGPARAM("MouseVisible") ))
1429 if (! (rProperty.Value >>= mbMouseVisible))
1430 return false;
1432 requestCursor(mnCurrentCursor);
1434 return true;
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))
1448 return false;
1450 if (bRehearseTimings)
1452 // TODO(Q3): Move to slide
1453 mpRehearseTimingsActivity = RehearseTimingsActivity::create(
1454 SlideShowContext(
1455 mpDummyPtr,
1456 maEventQueue,
1457 maEventMultiplexer,
1458 maScreenUpdater,
1459 maActivitiesQueue,
1460 maUserEventQueue,
1461 *this,
1462 maViewContainer,
1463 mxComponentContext) );
1465 else if (mpRehearseTimingsActivity)
1467 // removes timer from all views:
1468 mpRehearseTimingsActivity->dispose();
1469 mpRehearseTimingsActivity.reset();
1471 return true;
1474 if (rProperty.Name.equalsAsciiL(
1475 RTL_CONSTASCII_STRINGPARAM("WaitSymbolBitmap") ))
1477 uno::Reference<rendering::XBitmap> xBitmap;
1478 if (! (rProperty.Value >>= xBitmap))
1479 return false;
1481 mpWaitSymbol = WaitSymbol::create( xBitmap,
1482 maScreenUpdater,
1483 maEventMultiplexer,
1484 maViewContainer );
1486 return true;
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());
1509 iView!=iEnd;
1510 ++iView)
1512 if (*iView && (*iView)->getUnoView()==xView)
1514 // Store the flag at the view so that media shapes have
1515 // access to it.
1516 (*iView)->setIsSoundEnabled(bValue);
1517 return true;
1523 return false;
1526 void SlideShowImpl::addSlideShowListener(
1527 uno::Reference<presentation::XSlideShowListener> const& xListener )
1528 throw (uno::RuntimeException)
1530 osl::MutexGuard const guard( m_aMutex );
1532 if (isDisposed())
1533 return;
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 );
1556 if (isDisposed())
1557 return;
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(
1569 xShape,
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,
1579 xShape);
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
1597 // helper object
1598 ENSURE_OR_THROW(
1599 aIter->second.get(),
1600 "SlideShowImpl::removeShapeEventListener(): "
1601 "listener map contains NULL broadcast helper" );
1603 aIter->second->removeInterface( xListener );
1606 maEventMultiplexer.notifyShapeListenerRemoved(xListener,
1607 xShape);
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 );
1616 if (isDisposed())
1617 return;
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
1630 // case
1631 maShapeCursors.insert(
1632 ShapeCursorMap::value_type(xShape,
1633 nPointerShape) );
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 );
1642 else
1644 // existing entry found, update with new cursor ID
1645 aIter->second = nPointerShape;
1648 maEventMultiplexer.notifyShapeCursorChange(xShape,
1649 nPointerShape);
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,
1662 _1,
1663 nActualCursor ));
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,
1676 _1,
1677 calcActiveCursor(mnCurrentCursor) ));
1680 sal_Bool SlideShowImpl::update( double & nNextTimeout )
1681 throw (uno::RuntimeException)
1683 osl::MutexGuard const guard( m_aMutex );
1685 if (isDisposed())
1686 return false;
1688 // precondition: update() must only be called from the
1689 // main thread!
1690 DBG_TESTSOLARMUTEX();
1692 if( mbShowPaused )
1694 // commit frame (might be repaints pending)
1695 maScreenUpdater.commitUpdates();
1697 return false;
1699 else
1701 // TODO(F2): re-evaluate whether that timer lagging makes
1702 // sense.
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();
1715 else
1716 scopeGuard.dismiss(); // we're not holding the timer
1718 // process queues
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);
1759 if (bRet)
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;
1768 else
1770 // timer events left:
1771 // difference from current time (nota bene:
1772 // time no longer held here!) to the next event in
1773 // the event queue.
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
1789 // implementations
1790 if( !mbSlideShowIdle &&
1791 (!bRet ||
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& )
1808 throw;
1810 catch( uno::Exception& )
1812 OSL_ENSURE( false,
1813 rtl::OUStringToOString(
1814 comphelper::anyToString( cppu::getCaughtException() ),
1815 RTL_TEXTENCODING_UTF8 ).getStr() );
1818 ++aCurr;
1821 mbSlideShowIdle = true;
1823 #endif
1825 return bRet;
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" );
1836 if (mpCurrentSlide)
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,
1854 uno::UNO_QUERY );
1856 sal_Int32 nChange(0);
1857 if( !xPropSet.is() ||
1858 !getPropertyValue( nChange,
1859 xPropSet,
1860 ::rtl::OUString(
1861 RTL_CONSTASCII_USTRINGPARAM("Change"))) )
1863 OSL_TRACE(
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,
1872 xPropSet,
1873 ::rtl::OUString(
1874 RTL_CONSTASCII_USTRINGPARAM("Duration"))) )
1876 OSL_TRACE(
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
1899 // delay
1900 aNotificationEvents = makeInterruptableDelay(
1901 boost::bind<void>( boost::mem_fn(&SlideShowImpl::notifySlideEnded), this, false ),
1902 maEventMultiplexer.getAutomaticTimeout() );
1904 else
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()
1932 else
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() );
1980 } // finally
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() );
2001 if (xPropSet.is())
2003 xPropSet->setPropertyValue(
2004 OUSTR("Change"),
2005 uno::Any( static_cast<sal_Int32>(1) ) );
2006 xPropSet->setPropertyValue(
2007 OUSTR("Duration"),
2008 uno::Any( static_cast<sal_Int32>(time) ) );
2013 if (bReverse)
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()
2020 // relies on that
2021 // unconditionally. Otherwise, genuine
2022 // shape animations (drawing layer and
2023 // GIF) will not be stopped.
2025 maListenerContainer.forEach<presentation::XSlideShowListener>(
2026 boost::bind(
2027 &presentation::XSlideShowListener::slideEnded,
2029 bReverse) );
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,
2038 _1,
2039 boost::cref(hyperLink) ));
2040 return true;
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,
2057 _1,
2058 boost::cref(xNode) ));
2059 break;
2061 case AnimationNode::FROZEN:
2062 case AnimationNode::ENDED:
2063 maListenerContainer.forEach<presentation::XSlideShowListener>(
2064 boost::bind( &animations::XAnimationListener::endEvent,
2065 _1,
2066 boost::cref(xNode) ));
2067 break;
2068 default:
2069 break;
2072 return true;
2075 } // anon namespace
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(
2081 serviceImpl,
2082 #else
2083 const sdecl::ServiceDecl slideShowDecl(
2084 sdecl::class_<SlideShowImpl>(),
2085 #endif
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)