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