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