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