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
isDisposed() const348 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
matches(SlideSharedPtr const & pSlide,uno::Reference<drawing::XDrawPage> const & xSlide,uno::Reference<animations::XAnimationNode> const & xNode)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:
WaitSymbolLock(SlideShowImpl & rSlideShowImpl)412 WaitSymbolLock(SlideShowImpl& rSlideShowImpl) : mrSlideShowImpl(rSlideShowImpl)
413 { mrSlideShowImpl.requestWaitSymbol(); }
~WaitSymbolLock(void)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
SeparateListenerImpl__anon857254e10111::SlideShowImpl::SeparateListenerImpl519 SeparateListenerImpl( SlideShowImpl& rShow,
520 ScreenUpdater& rScreenUpdater,
521 EventQueue& rEventQueue ) :
522 mrShow( rShow ),
523 mrScreenUpdater( rScreenUpdater ),
524 mrEventQueue( rEventQueue )
525 {}
526
527 // EventHandler
handleEvent__anon857254e10111::SlideShowImpl::SeparateListenerImpl528 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
viewClobbered__anon857254e10111::SlideShowImpl::SeparateListenerImpl546 virtual void viewClobbered( const UnoViewSharedPtr& rView )
547 {
548 // given view needs repaint, request update
549 mrScreenUpdater.notifyUpdate(rView, true);
550 }
551
552 // HyperlinkHandler
handleHyperlink__anon857254e10111::SlideShowImpl::SeparateListenerImpl553 virtual bool handleHyperlink( ::rtl::OUString const& rLink )
554 {
555 return mrShow.notifyHyperLinkClicked(rLink);
556 }
557
558 // AnimationEventHandler
handleAnimationEvent__anon857254e10111::SlideShowImpl::SeparateListenerImpl559 virtual bool handleAnimationEvent( const AnimationNodeSharedPtr& rNode )
560 {
561 return mrShow.handleAnimationEvent(rNode);
562 }
563 };
564
565
SlideShowImpl(uno::Reference<uno::XComponentContext> const & xContext)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)
disposing()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
stopSlideTransitionSound()703 void SlideShowImpl::stopSlideTransitionSound()
704 {
705 if (mpCurrentSlideTransitionSound)
706 {
707 mpCurrentSlideTransitionSound->stopPlayback();
708 mpCurrentSlideTransitionSound->dispose();
709 mpCurrentSlideTransitionSound.reset();
710 }
711 }
712
resetSlideTransitionSound(const uno::Any & rSound,bool bLoopSound)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
createSlideTransition(const uno::Reference<drawing::XDrawPage> & xDrawPage,const SlideSharedPtr & rLeavingSlide,const SlideSharedPtr & rEnteringSlide,const EventSharedPtr & rTransitionEndEvent)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
findPolygons(uno::Reference<drawing::XDrawPage> const & xDrawPage)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
makeSlide(uno::Reference<drawing::XDrawPage> const & xDrawPage,uno::Reference<drawing::XDrawPagesSupplier> const & xDrawPages,uno::Reference<animations::XAnimationNode> const & xRootNode)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
requestWaitSymbol(void)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
releaseWaitSymbol(void)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
calcActiveCursor(sal_Int16 nCursorShape) const982 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
stopShow()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:
PrefetchPropertiesFunc(SlideShowImpl * that_,bool & rbSkipAllMainSequenceEffects,bool & rbSkipSlideTransition)1040 PrefetchPropertiesFunc( SlideShowImpl * that_,
1041 bool& rbSkipAllMainSequenceEffects,
1042 bool& rbSkipSlideTransition)
1043 : mpSlideShowImpl(that_),
1044 mrbSkipAllMainSequenceEffects(rbSkipAllMainSequenceEffects),
1045 mrbSkipSlideTransition(rbSkipSlideTransition)
1046 {}
1047
operator ()(beans::PropertyValue const & rProperty) const1048 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
displaySlide(uno::Reference<drawing::XDrawPage> const & xSlide,uno::Reference<drawing::XDrawPagesSupplier> const & xDrawPages,uno::Reference<animations::XAnimationNode> const & xRootNode,uno::Sequence<beans::PropertyValue> const & rProperties)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
redisplayCurrentSlide(void)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
nextEffect()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
previousEffect()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
rewindEffectToPreviousSlide(void)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
startShapeActivity(uno::Reference<drawing::XShape> const &)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
stopShapeActivity(uno::Reference<drawing::XShape> const &)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
pause(sal_Bool bPauseShow)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
getCurrentSlide()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
addView(uno::Reference<presentation::XSlideShowView> const & xView)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
removeView(uno::Reference<presentation::XSlideShowView> const & xView)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
registerUserPaintPolygons(const uno::Reference<lang::XMultiServiceFactory> & xDocFactory)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
setProperty(beans::PropertyValue const & rProperty)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
addSlideShowListener(uno::Reference<presentation::XSlideShowListener> const & xListener)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
removeSlideShowListener(uno::Reference<presentation::XSlideShowListener> const & xListener)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
addShapeEventListener(uno::Reference<presentation::XShapeEventListener> const & xListener,uno::Reference<drawing::XShape> const & xShape)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
removeShapeEventListener(uno::Reference<presentation::XShapeEventListener> const & xListener,uno::Reference<drawing::XShape> const & xShape)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
setShapeCursor(uno::Reference<drawing::XShape> const & xShape,sal_Int16 nPointerShape)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
requestCursor(sal_Int16 nCursorShape)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
resetCursor()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
update(double & nNextTimeout)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
notifySlideTransitionEnded(bool bPaintSlide)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
queryAutomaticSlideTransition(uno::Reference<drawing::XDrawPage> const & xDrawPage,double & nAutomaticNextSlideTimeout,bool & bHasAutomaticNextSlide)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
notifySlideAnimationsEnded()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
notifySlideEnded(const bool bReverse)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
notifyHyperLinkClicked(rtl::OUString const & hyperLink)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 */
handleAnimationEvent(const AnimationNodeSharedPtr & rNode)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
FrameSynchronization(const double nFrameDuration)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
MarkCurrentFrame(void)2428 void FrameSynchronization::MarkCurrentFrame (void)
2429 {
2430 mnNextFrameTargetTime = maTimer.getElapsedTime() + mnFrameDuration;
2431 }
2432
2433
2434
2435
Synchronize(void)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
Activate(void)2451 void FrameSynchronization::Activate (void)
2452 {
2453 mbIsActive = true;
2454 }
2455
2456
2457
2458
Deactivate(void)2459 void FrameSynchronization::Deactivate (void)
2460 {
2461 mbIsActive = false;
2462 }
2463
2464
2465
2466
GetCurrentTime(void) const2467 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