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