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