Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sd/source/ui/slideshow/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 116 kB image not shown  

Quelle  slideshowimpl.cxx

  Sprache: C
 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <algorithm>

#include <config_features.h>

#include <com/sun/star/frame/theAutoRecovery.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/SystemPointer.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/util/XURLTransformer.hpp>
#include <com/sun/star/frame/XDispatch.hpp>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <com/sun/star/presentation/SlideShow.hpp>
#include <com/sun/star/media/XPlayer.hpp>
#include <officecfg/Office/Impress.hxx>
#include <officecfg/Office/Recovery.hxx>
#include <svl/stritem.hxx>
#include <svl/urihelper.hxx>
#include <basic/sbstar.hxx>

#include <toolkit/helper/vclunohelper.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/sequence.hxx>

#include <sfx2/infobar.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/app.hxx>
#include <sfx2/viewfrm.hxx>
#include <svx/svdoole2.hxx>
#include <svx/f3dchild.hxx>
#include <svx/imapdlg.hxx>
#include <svx/fontwork.hxx>
#include <svx/SvxColorChildWindow.hxx>
#include <svx/bmpmask.hxx>
#include <svx/srchdlg.hxx>
#include <svx/hyperdlg.hxx>
#include <svx/svxids.hrc>
#include <svx/unoapi.hxx>
#include <AnimationChildWindow.hxx>
#include <notifydocumentevent.hxx>
#include "slideshowimpl.hxx"
#include "slideshowviewimpl.hxx"
#include "PaneHider.hxx"

#include <bitmaps.hlst>
#include <strings.hrc>
#include <sdresid.hxx>
#include <utility>
#include <vcl/canvastools.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/weldutils.hxx>

#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/help.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <rtl/ref.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <avmedia/mediawindow.hxx>
#include <svtools/colrdlg.hxx>
#include <DrawDocShell.hxx>
#include <ViewShellBase.hxx>
#include <PresentationViewShell.hxx>
#include <RemoteServer.hxx>
#include <customshowlist.hxx>
#include <unopage.hxx>
#include <sdpage.hxx>
#include <sdmod.hxx>
#include <app.hrc>
#include <cusshow.hxx>
#include <optsitem.hxx>
#include <unomodel.hxx>

#define CM_SLIDES       21

using ::com::sun::star::animations::XAnimationNode;
using ::com::sun::star::animations::XAnimationListener;
using ::com::sun::star::awt::XWindow;
using namespace ::com::sun::star;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::beans;

namespace sd
{
/** Slots, which will be disabled in the slide show and are managed by Sfx.
    Have to be sorted in the order of the SIDs */

sal_uInt16 const pAllowed[] =
{
    SID_OPENDOC                             , //     5501   ///< that internally jumps work
    SID_JUMPTOMARK                          , //     5598
    SID_OPENHYPERLINK                       , //     6676
    SID_PRESENTATION_END                     //    27218
};

class AnimationSlideController
{
public:
    enum Mode { ALL, FROM, CUSTOM, PREVIEW };

public:
    AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode );

    void setStartSlideNumber( sal_Int32 nSlideNumber ) { mnStartSlideNumber = nSlideNumber; }
    sal_Int32 getStartSlideIndex() const;

    sal_Int32 getCurrentSlideNumber() const;
    sal_Int32 getCurrentSlideIndex() const;

    sal_Int32 getSlideIndexCount() const { return maSlideNumbers.size(); }
    sal_Int32 getSlideNumberCount() const { return mnSlideCount; }

    sal_Int32 getSlideNumber( sal_Int32 nSlideIndex ) const;

    void insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible = true );
    void setPreviewNode( const Reference< XAnimationNode >& xPreviewNode );

    bool jumpToSlideIndex( sal_Int32 nNewSlideIndex );
    bool jumpToSlideNumber( sal_Int32 nNewSlideIndex );

    bool nextSlide();
    bool previousSlide();

    void displayCurrentSlide( const Reference< XSlideShow >& xShow,
                              const Reference< XDrawPagesSupplier>& xDrawPages,
                              const bool bSkipAllMainSequenceEffects );

    sal_Int32 getNextSlideIndex() const;
    sal_Int32 getPreviousSlideIndex() const;

    bool isVisibleSlideNumber( sal_Int32 nSlideNumber ) const;

    Reference< XDrawPage > getSlideByNumber( sal_Int32 nSlideNumber ) const;

    sal_Int32 getNextSlideNumber() const;

    bool hasSlides() const { return !maSlideNumbers.empty(); }

    // for InteractiveSlideShow we need to temporarily change the program
    // and mode, so allow save/restore that settings
    void pushForPreview();
    void popFromPreview();
private:
    bool getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode );
    sal_Int32 findSlideIndex( sal_Int32 nSlideNumber ) const;

    bool isValidIndex( sal_Int32 nIndex ) const { return (nIndex >= 0) && (o3tl::make_unsigned(nIndex) < maSlideNumbers.size()); }
    bool isValidSlideNumber( sal_Int32 nSlideNumber ) const { return (nSlideNumber >= 0) && (nSlideNumber < mnSlideCount); }

private:
    Mode meMode;
    sal_Int32 mnStartSlideNumber;
    std::vector< sal_Int32 > maSlideNumbers;
    std::vector< bool > maSlideVisible;
    std::vector< bool > maSlideVisited;
    Reference< XAnimationNode > mxPreviewNode;
    sal_Int32 mnSlideCount;
    sal_Int32 mnCurrentSlideIndex;
    sal_Int32 mnHiddenSlideNumber;
    Reference< XIndexAccess > mxSlides;

    // IASS data for push/pop
    std::vector< sal_Int32 > maSlideNumbers2;
    std::vector< bool > maSlideVisible2;
    std::vector< bool > maSlideVisited2;
    Reference< XAnimationNode > mxPreviewNode2;
    Mode meMode2;
};

void AnimationSlideController::pushForPreview()
{
    maSlideNumbers2 = maSlideNumbers;
    maSlideVisible2 = maSlideVisible;
    maSlideVisited2 = maSlideVisited;
    maSlideNumbers.clear();
    maSlideVisible.clear();
    maSlideVisited.clear();
    mxPreviewNode2 = mxPreviewNode;
    meMode2 = meMode;
    meMode = AnimationSlideController::PREVIEW;
}

void AnimationSlideController::popFromPreview()
{
    maSlideNumbers = maSlideNumbers2;
    maSlideVisible = maSlideVisible2;
    maSlideVisited = maSlideVisited2;
    maSlideNumbers2.clear();
    maSlideVisible2.clear();
    maSlideVisited2.clear();
    mxPreviewNode = mxPreviewNode2;
    meMode = meMode2;
}

Reference< XDrawPage > AnimationSlideController::getSlideByNumber( sal_Int32 nSlideNumber ) const
{
    Reference< XDrawPage > xSlide;
    if( mxSlides.is() && (nSlideNumber >= 0) && (nSlideNumber < mxSlides->getCount()) )
        mxSlides->getByIndex( nSlideNumber ) >>= xSlide;
    return xSlide;
}

bool AnimationSlideController::isVisibleSlideNumber( sal_Int32 nSlideNumber ) const
{
    sal_Int32 nIndex = findSlideIndex( nSlideNumber );

    if( nIndex != -1 )
        return maSlideVisible[ nIndex ];
    else
        return false;
}

void AnimationSlideController::setPreviewNode( const Reference< XAnimationNode >& xPreviewNode )
{
    mxPreviewNode = xPreviewNode;
}

AnimationSlideController::AnimationSlideController( Reference< XIndexAccess > const &&nbsp;xSlides, Mode eMode  )
:   meMode( eMode )
,   mnStartSlideNumber(-1)
,   mnSlideCount( 0 )
,   mnCurrentSlideIndex(0)
,   mnHiddenSlideNumber( -1 )
,   mxSlides( xSlides )
,   meMode2( eMode )
{
    if( mxSlides.is() )
        mnSlideCount = xSlides->getCount();
}

sal_Int32 AnimationSlideController::getStartSlideIndex() const
{
    if( mnStartSlideNumber >= 0 )
    {
        sal_Int32 nIndex;
        const sal_Int32 nCount = maSlideNumbers.size();

        for( nIndex = 0; nIndex < nCount; nIndex++ )
        {
            if( maSlideNumbers[nIndex] == mnStartSlideNumber )
                return nIndex;
        }
    }

    return 0;
}

sal_Int32 AnimationSlideController::getCurrentSlideNumber() const
{
    if( mnHiddenSlideNumber != -1 )
        return mnHiddenSlideNumber;
    else if( !maSlideNumbers.empty() )
        return maSlideNumbers[mnCurrentSlideIndex];
    else
        return 0;
}

sal_Int32 AnimationSlideController::getCurrentSlideIndex() const
{
    if( mnHiddenSlideNumber != -1 )
        return -1;
    else
        return mnCurrentSlideIndex;
}

bool AnimationSlideController::jumpToSlideIndex( sal_Int32 nNewSlideIndex )
{
    if( isValidIndex( nNewSlideIndex ) )
    {
        mnCurrentSlideIndex = nNewSlideIndex;
        mnHiddenSlideNumber = -1;
        maSlideVisited[mnCurrentSlideIndex] = true;
        return true;
    }
    else
    {
        return false;
    }
}

bool AnimationSlideController::jumpToSlideNumber( sal_Int32 nNewSlideNumber )
{
    sal_Int32 nIndex = findSlideIndex( nNewSlideNumber );
    if( isValidIndex( nIndex ) )
    {
        return jumpToSlideIndex( nIndex );
    }
    else if( (nNewSlideNumber >= 0) && (nNewSlideNumber < mnSlideCount) )
    {
        // jump to a hidden slide
        mnHiddenSlideNumber = nNewSlideNumber;
        return true;
    }
    else
    {
        return false;
    }
}

sal_Int32 AnimationSlideController::getSlideNumber( sal_Int32 nSlideIndex ) const
{
    if( isValidIndex( nSlideIndex ) )
        return maSlideNumbers[nSlideIndex];
    else
        return -1;
}

void AnimationSlideController::insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible /* = true */ )
{
    DBG_ASSERT( isValidSlideNumber( nSlideNumber ), "sd::AnimationSlideController::insertSlideNumber(), illegal index" );
    if( isValidSlideNumber( nSlideNumber ) )
    {
        maSlideNumbers.push_back( nSlideNumber );
        maSlideVisible.push_back( bVisible );
        maSlideVisited.push_back( false );
    }
}

bool AnimationSlideController::getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode )
{
    if( isValidSlideNumber( nSlideNumber ) ) try
    {
        xSlide.set( mxSlides->getByIndex(nSlideNumber), UNO_QUERY_THROW );

        if( meMode == PREVIEW )
        {
            xAnimNode = mxPreviewNode;
        }
        else
        {
            Reference< animations::XAnimationNodeSupplier > xAnimNodeSupplier( xSlide, UNO_QUERY_THROW );
            xAnimNode = xAnimNodeSupplier->getAnimationNode();
        }

        return true;
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::AnimationSlideController::getSlideAPI()" );
    }

    return false;
}

sal_Int32 AnimationSlideController::findSlideIndex( sal_Int32 nSlideNumber ) const
{
    sal_Int32 nIndex;
    const sal_Int32 nCount = maSlideNumbers.size();

    for( nIndex = 0; nIndex < nCount; nIndex++ )
    {
        if( maSlideNumbers[nIndex] == nSlideNumber )
            return nIndex;
    }

    return -1;
}

sal_Int32 AnimationSlideController::getNextSlideIndex() const
{
    switch( meMode )
    {
    case ALL:
        {
            sal_Int32 nNewSlideIndex = mnCurrentSlideIndex + 1;
            if( isValidIndex( nNewSlideIndex ) )
            {
                // if the current slide is not excluded, make sure the
                // next slide is also not excluded.
                // if the current slide is excluded, we want to go
                // to the next slide, even if this is also excluded.
                if( maSlideVisible[mnCurrentSlideIndex] )
                {
                    while( isValidIndex( nNewSlideIndex ) )
                    {
                        if( maSlideVisible[nNewSlideIndex] )
                            break;

                        nNewSlideIndex++;
                    }
                }
            }
            return isValidIndex( nNewSlideIndex ) ? nNewSlideIndex : -1;
        }

    case FROM:
    case CUSTOM:
        return mnHiddenSlideNumber == -1 ? mnCurrentSlideIndex + 1 : mnCurrentSlideIndex;

    default:
    case PREVIEW:
        return -1;

    }
}

sal_Int32 AnimationSlideController::getNextSlideNumber() const
{
    sal_Int32 nNextSlideIndex = getNextSlideIndex();
    if( isValidIndex( nNextSlideIndex ) )
    {
        return maSlideNumbers[nNextSlideIndex];
    }
    else
    {
        return -1;
    }
}

bool AnimationSlideController::nextSlide()
{
    return jumpToSlideIndex( getNextSlideIndex() );
}

sal_Int32 AnimationSlideController::getPreviousSlideIndex() const
{
    sal_Int32 nNewSlideIndex = mnCurrentSlideIndex - 1;

    switch( meMode )
    {
        case ALL:
        {
            // make sure the previous slide is visible
            // or was already visited
            while( isValidIndex( nNewSlideIndex ) )
            {
                if( maSlideVisible[nNewSlideIndex] || maSlideVisited[nNewSlideIndex] )
                    break;

                nNewSlideIndex--;
            }

            break;
        }

        case PREVIEW:
            return -1;

        default:
            break;
    }

    return nNewSlideIndex;
}

bool AnimationSlideController::previousSlide()
{
    return jumpToSlideIndex( getPreviousSlideIndex() );
}

void AnimationSlideController::displayCurrentSlide( const Reference< XSlideShow >& xShow,
                                                    const Reference< XDrawPagesSupplier>& xDrawPages,
                                                    const bool bSkipAllMainSequenceEffects )
{
    const sal_Int32 nCurrentSlideNumber = getCurrentSlideNumber();

    if( !(xShow.is() && (nCurrentSlideNumber != -1 )) )
        return;

    Reference< XDrawPage > xSlide;
    Reference< XAnimationNode > xAnimNode;
    ::std::vector<PropertyValue> aProperties;

    const sal_Int32 nNextSlideNumber = getNextSlideNumber();
    if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode )  )
    {
        Sequence< Any > aValue{ Any(xSlide), Any(xAnimNode) };
        aProperties.emplace_back( "Prefetch" ,
                -1,
                Any(aValue),
                PropertyState_DIRECT_VALUE);
    }
    if (bSkipAllMainSequenceEffects)
    {
        // Add one property that prevents the slide transition from being
        // shown (to speed up the transition to the previous slide) and
        // one to show all main sequence effects so that the user can
        // continue to undo effects.
        aProperties.emplace_back( "SkipAllMainSequenceEffects",
                -1,
                Any(true),
                PropertyState_DIRECT_VALUE);
        aProperties.emplace_back("SkipSlideTransition",
                -1,
                Any(true),
                PropertyState_DIRECT_VALUE);
    }

    if( getSlideAPI( nCurrentSlideNumber, xSlide, xAnimNode ) )
        xShow->displaySlide( xSlide, xDrawPages, xAnimNode, comphelper::containerToSequence(aProperties) );
}

constexpr OUString gsOnClick( u"OnClick"_ustr );
constexpr OUString gsBookmark( u"Bookmark"_ustr );
constexpr OUString gsVerb( u"Verb"_ustr );

SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow )
: mxShow()
, mxView()
, mxModel(pDoc->getUnoModel())
, maUpdateTimer("SlideShowImpl maUpdateTimer")
, maInputFreezeTimer("SlideShowImpl maInputFreezeTimer")
, maDeactivateTimer("SlideShowImpl maDeactivateTimer")
, mpView(pView)
, mpViewShell(pViewSh)
, mpDocSh(pDoc->GetDocSh())
, mpDoc(pDoc)
, mpParentWindow(pParentWindow)
, mpShowWindow(nullptr)
, mpSlideController()
, mnRestoreSlide(0)
, maPopupMousePos()
, maPresSize( -1, -1 )
, meAnimationMode(ANIMATIONMODE_SHOW)
, maCharBuffer()
, mpOldActiveWindow(nullptr)
, maStarBASICGlobalErrorHdl()
, mnChildMask( 0 )
, mbDisposed(false)
, mbAutoSaveWasOn(false)
, mbRehearseTimings(false)
, mbIsPaused(false)
, mbWasPaused(false)
, mbInputFreeze(false)
, mbActive(false)
, maPresSettings( pDoc->getPresentationSettings() )
, mnUserPaintColor( 0x80ff0000L )
, mbUsePen(false)
, mdUserPaintStrokeWidth ( 150.0 )
, maShapeEventMap()
, mxPreviewDrawPage()
, mxPreviewAnimationNode()
, mxPlayer()
, mpPaneHider()
, mnEndShowEvent(nullptr)
, mnContextMenuEvent(nullptr)
, mnEventObjectChange(nullptr)
, mnEventObjectInserted(nullptr)
, mnEventObjectRemoved(nullptr)
, mnEventPageOrderChange(nullptr)
, mxPresentation( xPresentation )
, mxListenerProxy()
, mxShow2()
, mxView2()
, meAnimationMode2()
, mbInterActiveSetup(false)
, maPresSettings2()
, mxPreviewDrawPage2()
, mxPreviewAnimationNode2()
, mnSlideIndex(0)
{
    if( mpViewShell )
        mpOldActiveWindow = mpViewShell->GetActiveWindow();

    maUpdateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, updateHdl));
    // Priority must not be too high or we'll starve input handling etc.
    maUpdateTimer.SetPriority(TaskPriority::REPAINT);

    maDeactivateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, deactivateHdl));
    maDeactivateTimer.SetTimeout( 20 );

    maInputFreezeTimer.SetInvokeHandler( LINK( this, SlideshowImpl, ReadyForNextInputHdl ) );
    maInputFreezeTimer.SetTimeout( 20 );

        // no autosave during show
    if (officecfg::Office::Recovery::AutoSave::Enabled::get())
        mbAutoSaveWasOn = true;

    Application::AddEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) );

    mbUsePen = maPresSettings.mbMouseAsPen;

    SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress);
    if( pOptions )
    {
        mnUserPaintColor = pOptions->GetPresentationPenColor();
        mdUserPaintStrokeWidth = pOptions->GetPresentationPenWidth();
    }

    // to be able to react on various changes in the DrawModel, this class
    // is now derived from SfxListener and registers itself at the DrawModel
    if (nullptr != mpDoc)
        StartListening(*mpDoc);
}

SlideshowImpl::~SlideshowImpl()
{
    // stop listening to DrawModel (see above)
    if (nullptr != mpDoc)
        EndListening(*mpDoc);

    SdModule* pModule = SdModule::get();
    //rhbz#806663 SlideshowImpl can outlive SdModule
    SdOptions* pOptions = pModule ?
        pModule->GetSdOptions(DocumentType::Impress) : nullptr;
    if( pOptions )
    {
        pOptions->SetPresentationPenColor(mnUserPaintColor);
        pOptions->SetPresentationPenWidth(mdUserPaintStrokeWidth);
    }

    Application::RemoveEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) );

    maDeactivateTimer.Stop();

    if( !mbDisposed )
    {
        OSL_FAIL("SlideshowImpl::~SlideshowImpl(), component was not disposed!");
        std::unique_lock g(m_aMutex);
        disposing(g);
    }
}

void SlideshowImpl::disposing(std::unique_lock<std::mutex>&)
{
#ifdef ENABLE_SDREMOTE
    RemoteServer::presentationStopped();
#endif
    // IASS: This is the central methodology to 'steer' the
    // PresenterConsole - in this case, to shut it down
    if( mxShow.is() && mpDoc )
        NotifyDocumentEvent(
            *mpDoc,
            u"OnEndPresentation"_ustr );

    if( mbAutoSaveWasOn )
        setAutoSaveState( true );

    if( mnEndShowEvent )
        Application::RemoveUserEvent( mnEndShowEvent );
    if( mnContextMenuEvent )
        Application::RemoveUserEvent( mnContextMenuEvent );
    if( mnEventObjectChange )
        Application::RemoveUserEvent( mnEventObjectChange );
    if( mnEventObjectInserted )
        Application::RemoveUserEvent( mnEventObjectInserted );
    if( mnEventObjectRemoved )
        Application::RemoveUserEvent( mnEventObjectRemoved );
    if( mnEventPageOrderChange )
        Application::RemoveUserEvent( mnEventPageOrderChange );

    maInputFreezeTimer.Stop();

    SolarMutexGuard aSolarGuard;

    if( !mxShow.is() )
        return;

    if( mxPresentation.is() )
        mxPresentation->end();

    maUpdateTimer.Stop();

    removeShapeEvents();

    if( mxListenerProxy.is() )
        mxListenerProxy->removeAsSlideShowListener();

    try
    {
        if( mxView.is() )
            mxShow->removeView( mxView );

        Reference< XComponent > xComponent( mxShow, UNO_QUERY );
        if( xComponent.is() )
            xComponent->dispose();

        if( mxView.is() )
            mxView->dispose();
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::stop()" );
    }

    mxShow.clear();
    mxView.clear();
    mxListenerProxy.clear();
    mpSlideController.reset();

    // take DrawView from presentation window, but give the old window back
    if( mpShowWindow && mpView )
        mpView->DeleteDeviceFromPaintView( *mpShowWindow->GetOutDev() );

    if( mpView )
        mpView->SetAnimationPause( false );

    if( mpViewShell )
    {
        mpViewShell->SetActiveWindow(mpOldActiveWindow);
        if (mpShowWindow)
            mpShowWindow->SetViewShell( nullptr );
    }

    if( mpView )
        mpView->InvalidateAllWin();

    if( maPresSettings.mbFullScreen )
    {
#if HAVE_FEATURE_SCRIPTING
        // restore StarBASICErrorHdl
        StarBASIC::SetGlobalErrorHdl(maStarBASICGlobalErrorHdl);
        maStarBASICGlobalErrorHdl = Link<StarBASIC*,bool>();
#endif
    }
    else
    {
        if( mpShowWindow )
            mpShowWindow->Hide();
    }

    if( meAnimationMode == ANIMATIONMODE_SHOW )
    {
        mpDocSh->SetSlotFilter();
        mpDocSh->ApplySlotFilter();

        Help::EnableContextHelp();
        Help::EnableExtHelp();

        showChildWindows();
        mnChildMask = 0;
    }

    // show current window again
    if( mpViewShell && dynamic_cast< PresentationViewShell *>( mpViewShell ) ==  nullptr)
    {
        if( meAnimationMode == ANIMATIONMODE_SHOW )
        {
            mpViewShell->GetViewShellBase().ShowUIControls (true);
            mpPaneHider.reset();
        }
        else if( meAnimationMode == ANIMATIONMODE_PREVIEW )
        {
            mpViewShell->ShowUIControls(true);
        }
    }

    if( mpShowWindow )
        mpShowWindow->Hide();
    mpShowWindow.disposeAndClear();

    if ( mpViewShell )
    {
        if( meAnimationMode == ANIMATIONMODE_SHOW )
        {
            ::sd::Window* pActWin = mpViewShell->GetActiveWindow();

            if (pActWin)
            {
                Size aVisSizePixel = pActWin->GetOutputSizePixel();
                ::tools::Rectangle aVisAreaWin = pActWin->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) );
                mpViewShell->VisAreaChanged(aVisAreaWin);
                if (mpView)
                    mpView->VisAreaChanged(pActWin->GetOutDev());
                pActWin->GrabFocus();
            }
        }

        // restart the custom show dialog if he started us
        if( mpViewShell->IsStartShowWithDialog() && getDispatcher() )
        {
            mpViewShell->SetStartShowWithDialog( false );
            getDispatcher()->Execute( SID_CUSTOMSHOW_DLG, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
        }

        mpViewShell->GetViewShellBase().UpdateBorder(true);
    }

    if( mpShowWindow )
    {
        mpShowWindow.disposeAndClear();
    }

    setActiveXToolbarsVisible( true );

    mbDisposed = true;
}

bool SlideshowImpl::isInteractiveSetup() const
{
    return mbInterActiveSetup;
}

void SlideshowImpl::startInteractivePreview( const Reference< XDrawPage >& xDrawPageconst Reference< XAnimationNode >& xAnimationNode )
{
    // set flag that we are in IASS mode
    mbInterActiveSetup = true;

    // save stuff that will be replaced temporarily
    mxShow2 = mxShow;
    mxView2 = mxView;
    mxPreviewDrawPage2 = mxPreviewDrawPage;
    mxPreviewAnimationNode2 = mxPreviewAnimationNode;
    meAnimationMode2 = meAnimationMode;
    maPresSettings2 = maPresSettings;

    // remember slide shown before preview
    mnSlideIndex = getCurrentSlideIndex();

    // set DrawPage/AnimationNode
    mxPreviewDrawPage = xDrawPage;
    mxPreviewAnimationNode = xAnimationNode;
    meAnimationMode = ANIMATIONMODE_PREVIEW;

    // set PresSettings for preview
    maPresSettings.mbAll = false;
    maPresSettings.mbEndless = false;
    maPresSettings.mbCustomShow = false;
    maPresSettings.mbManual = false;
    maPresSettings.mbMouseVisible = false;
    maPresSettings.mbMouseAsPen = false;
    maPresSettings.mbLockedPages = false;
    maPresSettings.mbAlwaysOnTop = false;
    maPresSettings.mbFullScreen = false;
    maPresSettings.mbAnimationAllowed = true;
    maPresSettings.mnPauseTimeout = 0;
    maPresSettings.mbShowPauseLogo = false;

    // create a new temporary AnimationSlideController
    mpSlideController->pushForPreview();
    // Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW );
    // Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
    // mpSlideController = std::make_shared<AnimationSlideController>( xSlides, AnimationSlideController::PREVIEW );
    sal_Int32 nSlideNumber = 0;
    Reference< XPropertySet > xSet( xDrawPage, UNO_QUERY_THROW );
    xSet->getPropertyValue( u"Number"_ustr ) >>= nSlideNumber;
    mpSlideController->insertSlideNumber( nSlideNumber-1 );
    mpSlideController->setPreviewNode( xAnimationNode );

    // prepare properties
    sal_Int32 nPropertyCount = 1;
    if( xAnimationNode.is() )
        nPropertyCount++;
    Sequence< beans::PropertyValue > aProperties(nPropertyCount);
    auto pProperties = aProperties.getArray();
    pProperties[0].Name = "AutomaticAdvancement";
    pProperties[0].Value <<= 1.0; // one second timeout

    if( xAnimationNode.is() )
    {
        pProperties[1].Name = "NoSlideTransitions";
        pProperties[1].Value <<= true;
    }

    // start preview
    startShowImpl( aProperties );
}

void SlideshowImpl::endInteractivePreview()
{
    if (!mbInterActiveSetup)
        // not in use, nothing to do
        return;

    // cleanup Show/View
    try
    {
        if( mxView.is() )
            mxShow->removeView( mxView );

        Reference< XComponent > xComponent( mxShow, UNO_QUERY );
        if( xComponent.is() )
            xComponent->dispose();

        if( mxView.is() )
            mxView->dispose();
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::stop()" );
    }
    mxShow.clear();
    mxView.clear();
    mxView = mxView2;
    mxShow = mxShow2;

    // restore SlideController
    mpSlideController->popFromPreview();

    // restore other settings and cleanup temporary incarnations
    maPresSettings = maPresSettings2;
    meAnimationMode = meAnimationMode2;
    mxPreviewAnimationNode = mxPreviewAnimationNode2;
    mxPreviewAnimationNode2.clear();
    mxPreviewDrawPage = mxPreviewDrawPage2;
    mxPreviewDrawPage2.clear();

    // go back to slide shown before preview
    gotoSlideIndex(mnSlideIndex);

    // reset IASS mode flag
    mbInterActiveSetup = false;
}

bool SlideshowImpl::startPreview(
        const Reference< XDrawPage >& xDrawPage,
        const Reference< XAnimationNode >& xAnimationNode,
        vcl::Window * pParent )
{
    bool bRet = false;

    try
    {
        const Reference<lang::XServiceInfo> xServiceInfo( xDrawPage, UNO_QUERY );
        if (xServiceInfo.is()) {
            const Sequence<OUString> supportedServices(
                xServiceInfo->getSupportedServiceNames() );
            if (comphelper::findValue(supportedServices, "com.sun.star.drawing.MasterPage") != -1) {
                OSL_FAIL("sd::SlideshowImpl::startPreview() "
                          "not allowed on master page!");
                return false;
            }
        }

        mxPreviewDrawPage = xDrawPage;
        mxPreviewAnimationNode = xAnimationNode;
        meAnimationMode = ANIMATIONMODE_PREVIEW;

        maPresSettings.mbAll = false;
        maPresSettings.mbEndless = false;
        maPresSettings.mbCustomShow = false;
        maPresSettings.mbManual = false;
        maPresSettings.mbMouseVisible = false;
        maPresSettings.mbMouseAsPen = false;
        maPresSettings.mbLockedPages = false;
        maPresSettings.mbAlwaysOnTop = false;
        maPresSettings.mbFullScreen = false;
        maPresSettings.mbAnimationAllowed = true;
        maPresSettings.mnPauseTimeout = 0;
        maPresSettings.mbShowPauseLogo = false;

        rtl::Reference< SdXImpressDocument > xDrawPages( mpDoc->getUnoModel() );
        Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
        mpSlideController = std::make_shared<AnimationSlideController>( xSlides, AnimationSlideController::PREVIEW );

        sal_Int32 nSlideNumber = 0;
        Reference< XPropertySet > xSet( mxPreviewDrawPage, UNO_QUERY_THROW );
        xSet->getPropertyValue( u"Number"_ustr ) >>= nSlideNumber;
        mpSlideController->insertSlideNumber( nSlideNumber-1 );
        mpSlideController->setPreviewNode( xAnimationNode );

        mpShowWindow = VclPtr<ShowWindow>::Create( this, ((pParent == nullptr) && mpViewShell) ?  mpParentWindow.get() : pParent );
        if( mpViewShell )
        {
            mpViewShell->SetActiveWindow( mpShowWindow );
            mpShowWindow->SetViewShell (mpViewShell);
            mpViewShell->ShowUIControls (false);
        }

        if( mpView )
        {
            mpView->AddDeviceToPaintView( *mpShowWindow->GetOutDev(), nullptr );
            mpView->SetAnimationPause( true );
        }

        // call resize handler
        if( pParent )
        {
            maPresSize = pParent->GetSizePixel();
        }
        else if( mpViewShell )
        {
            ::tools::Rectangle aContentRect (mpViewShell->GetViewShellBase().getClientRectangle());
            if (AllSettings::GetLayoutRTL())
            {
                aContentRect.SetLeft( aContentRect.Right() );
                aContentRect.AdjustRight(aContentRect.Right() );
            }
            maPresSize = aContentRect.GetSize();
            mpShowWindow->SetPosPixel( aContentRect.TopLeft() );
        }
        else
        {
            OSL_FAIL("sd::SlideshowImpl::startPreview(), I need either a parent window or a viewshell!");
        }
        resize( maPresSize );

        sal_Int32 nPropertyCount = 1;
        if( mxPreviewAnimationNode.is() )
            nPropertyCount++;

        Sequence< beans::PropertyValue > aProperties(nPropertyCount);
        auto pProperties = aProperties.getArray();
        pProperties[0].Name = "AutomaticAdvancement";
        pProperties[0].Value <<= 1.0; // one second timeout

        if( mxPreviewAnimationNode.is() )
        {
            pProperties[1].Name = "NoSlideTransitions";
            pProperties[1].Value <<= true;
        }

        bRet = startShowImpl( aProperties );

        if( mpShowWindow != nullptr && meAnimationMode == ANIMATIONMODE_PREVIEW )
            mpShowWindow->SetPreviewMode();

    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::startPreview()" );
        bRet = false;
    }

    return bRet;
}

bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings )
{
    const rtl::Reference<SlideshowImpl> xKeepAlive(this);

    DBG_ASSERT( !mxShow.is(), "sd::SlideshowImpl::startShow(), called twice!" );
    if( mxShow.is() )
        return true;
    DBG_ASSERT( mpParentWindow!=nullptr, "sd::SlideshowImpl::startShow() called without parent window" );
    if (mpParentWindow == nullptr)
        return false;

    // Autoplay (pps/ppsx)
    if (mpViewShell->GetDoc()->GetStartWithPresentation())
    {
        mpViewShell->GetDoc()->SetExitAfterPresenting(true);
    }

    bool bRet = false;

    try
    {
        if( pPresSettings )
        {
            maPresSettings = *pPresSettings;
            mbRehearseTimings = pPresSettings->mbRehearseTimings;
        }

        OUString  aPresSlide( maPresSettings.maPresPage );
        SdPage* pStartPage = mpViewShell->GetActualPage();
        bool    bStartWithActualSlide =  pStartPage;

        // times should be measured?
        if( mbRehearseTimings )
        {
            maPresSettings.mbEndless = false;
            maPresSettings.mbManual = true;
            maPresSettings.mbMouseVisible = true;
            maPresSettings.mbMouseAsPen = false;
            maPresSettings.mnPauseTimeout = 0;
            maPresSettings.mbShowPauseLogo = false;
        }

        if( pStartPage )
        {
            if( pStartPage->GetPageKind() == PageKind::Notes )
            {
                // we are in notes page mode, so get
                // the corresponding draw page
                const sal_uInt16 nNotePgNum = pStartPage->GetPageNum();
                assert(nNotePgNum >= 2);
                const sal_uInt16 nPgNum = ( nNotePgNum - 2 ) >> 1;
                pStartPage = mpDoc->GetSdPage( nPgNum, PageKind::Standard );
            }
        }

        if( bStartWithActualSlide )
        {
            if ( aPresSlide.isEmpty())
            {
                // no preset slide yet, so pick current on one
                aPresSlide = pStartPage->GetName();
                // if the starting slide is hidden, we can't set slide controller to ALL mode
                maPresSettings.mbAll = !pStartPage->IsExcluded();
            }

            if( meAnimationMode != ANIMATIONMODE_SHOW )
            {
                if( pStartPage->GetPageKind() == PageKind::Standard )
                {
                    maPresSettings.mbAll = false;
                }
            }
        }

        // build page list
        createSlideList( maPresSettings.mbAll, aPresSlide );

        // remember Slide number from where the show was started
        if( pStartPage )
            mnRestoreSlide = ( pStartPage->GetPageNum() - 1 ) / 2;

        if( mpSlideController->hasSlides() )
        {
            // hide child windows
            hideChildWindows();

            mpShowWindow = VclPtr<ShowWindow>::Create( this, mpParentWindow );
            mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible );
            mpViewShell->SetActiveWindow( mpShowWindow );
            mpShowWindow->SetViewShell (mpViewShell);
            mpViewShell->GetViewShellBase().ShowUIControls (false);
            // Hide the side panes for in-place presentations.
            if ( ! maPresSettings.mbFullScreen)
                mpPaneHider.reset(new PaneHider(*mpViewShell,this));

            // these Slots are forbidden in other views for this document
            if( mpDocSh && pPresSettings && !pPresSettings->mbInteractive) // IASS
            {
                mpDocSh->SetSlotFilter( true, pAllowed );
                mpDocSh->ApplySlotFilter();
            }

            Help::DisableContextHelp();
            Help::DisableExtHelp();

            if( maPresSettings.mbFullScreen )
            {
#if HAVE_FEATURE_SCRIPTING
                // disable basic ide error handling
                maStarBASICGlobalErrorHdl = StarBASIC::GetGlobalErrorHdl();
                StarBASIC::SetGlobalErrorHdl( Link<StarBASIC*,bool>() );
#endif
            }

            // call resize handler
            maPresSize = mpParentWindow->GetSizePixel();
            if (!maPresSettings.mbFullScreen)
            {
                const ::tools::Rectangle& aClientRect = mpViewShell->GetViewShellBase().getClientRectangle();
                maPresSize = aClientRect.GetSize();
                mpShowWindow->SetPosPixel( aClientRect.TopLeft() );
                resize( maPresSize );
            }

            // #i41824#
            // Note: In FullScreen Mode the OS (window manager) sends a resize to
            // the WorkWindow once it actually resized it to full size.  The
            // WorkWindow propagates the resize to the DrawViewShell which calls
            // resize() at the SlideShow (this).  Calling resize here results in a
            // temporary display of a black window in the window's default size

            if( mpView )
            {
                mpView->AddDeviceToPaintView( *mpShowWindow->GetOutDev(), nullptr );
                mpView->SetAnimationPause( true );
            }

            SfxBindings* pBindings = getBindings();
            if( pBindings )
            {
                pBindings->Invalidate( SID_PRESENTATION );
                pBindings->Invalidate( SID_REHEARSE_TIMINGS );
            }

            // Defer the sd::ShowWindow's GrabFocus to SlideShow::activate. so that the accessible event can be fired correctly.
            //mpShowWindow->GrabFocus();

            std::vector<beans::PropertyValue> aProperties;
            aProperties.reserve( 4 );

            aProperties.emplace_back( "AdvanceOnClick" ,
                    -1, Any( !maPresSettings.mbLockedPages ),
                    beans::PropertyState_DIRECT_VALUE );

            aProperties.emplace_back( "ImageAnimationsAllowed" ,
                    -1, Any( maPresSettings.mbAnimationAllowed ),
                    beans::PropertyState_DIRECT_VALUE );

            const bool bZOrderEnabled(
                SdModule::get()->GetSdOptions( mpDoc->GetDocumentType() )->IsSlideshowRespectZOrder() );
            aProperties.emplace_back( "DisableAnimationZOrder" ,
                    -1, Any( !bZOrderEnabled ),
                    beans::PropertyState_DIRECT_VALUE );

            aProperties.emplace_back( "ForceManualAdvance" ,
                    -1, Any( maPresSettings.mbManual ),
                    beans::PropertyState_DIRECT_VALUE );

            if( mbUsePen )
            {
                aProperties.emplace_back( "UserPaintColor" ,
                        // User paint color is black by default.
                        -1, Any( mnUserPaintColor ),
                        beans::PropertyState_DIRECT_VALUE );

                aProperties.emplace_back( "UserPaintStrokeWidth" ,
                        // User paint color is black by default.
                        -1, Any( mdUserPaintStrokeWidth ),
                        beans::PropertyState_DIRECT_VALUE );
            }

            if (mbRehearseTimings) {
                aProperties.emplace_back( "RehearseTimings" ,
                        -1, Any(true), beans::PropertyState_DIRECT_VALUE );
            }

            bRet = startShowImpl( Sequence<beans::PropertyValue>(
                                      aProperties.data(), aProperties.size() ) );

        }

        setActiveXToolbarsVisible( false );
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::startShow()" );
        bRet = false;
    }

    return bRet;
}

bool SlideshowImpl::startShowImpl( const Sequence< beans::PropertyValue >& aProperties )
{
    try
    {
        mxShow.set( createSlideShow(), UNO_SET_THROW );

        mxView = new SlideShowView(
                                             *mpShowWindow,
                                             mpDoc,
                                             meAnimationMode,
                                             this,
                                             maPresSettings.mbFullScreen);

        // try add wait symbol to properties:
        const Reference<rendering::XSpriteCanvas> xSpriteCanvas(
            mxView->getCanvas() );
        if (xSpriteCanvas.is())
        {
            BitmapEx waitSymbolBitmap(BMP_WAIT_ICON);
            const Reference<rendering::XBitmap> xBitmap(
                vcl::unotools::xBitmapFromBitmapEx( waitSymbolBitmap ) );
            if (xBitmap.is())
            {
                mxShow->setProperty(
                    beans::PropertyValue( u"WaitSymbolBitmap"_ustr ,
                        -1,
                        Any( xBitmap ),
                        beans::PropertyState_DIRECT_VALUE ) );
            }

            BitmapEx pointerSymbolBitmap(BMP_POINTER_ICON);
            const Reference<rendering::XBitmap> xPointerBitmap(
                vcl::unotools::xBitmapFromBitmapEx( pointerSymbolBitmap ) );
            if (xPointerBitmap.is())
            {
                mxShow->setProperty(
                    beans::PropertyValue( u"PointerSymbolBitmap"_ustr ,
                        -1,
                        Any( xPointerBitmap ),
                        beans::PropertyState_DIRECT_VALUE ) );
            }
            if (officecfg::Office::Impress::Misc::Start::ShowNavigationPanel::get())
            {
                NavbarButtonSize btnScale = static_cast<NavbarButtonSize>(officecfg::Office::Impress::Layout::Display::NavigationBtnScale::get());
                OUString prevSlidePath = u""_ustr;
                OUString nextSlidePath = u""_ustr;
                OUString menuPath = u""_ustr;
                switch (btnScale)
                {
                    case NavbarButtonSize::Large:
                    {
                        prevSlidePath = BMP_PREV_SLIDE_LARGE;
                        nextSlidePath = BMP_NEXT_SLIDE_LARGE;
                        menuPath = BMP_MENU_SLIDE_LARGE;
                        break;
                    }
                    case NavbarButtonSize::XLarge:
                    {
                        prevSlidePath = BMP_PREV_SLIDE_EXTRALARGE;
                        nextSlidePath = BMP_NEXT_SLIDE_EXTRALARGE;
                        menuPath = BMP_MENU_SLIDE_EXTRALARGE;
                        break;
                    }
                    case NavbarButtonSize::Auto:
                    case NavbarButtonSize::Small:
                    default:
                    {
                        prevSlidePath = BMP_PREV_SLIDE_SMALL;
                        nextSlidePath = BMP_NEXT_SLIDE_SMALL;
                        menuPath = BMP_MENU_SLIDE_SMALL;
                        break;
                    }
                }
                BitmapEx prevSlideBm(prevSlidePath);
                const Reference<rendering::XBitmap> xPrevSBitmap(
                    vcl::unotools::xBitmapFromBitmapEx(prevSlideBm));
                if (xPrevSBitmap.is())
                {
                    mxShow->setProperty(beans::PropertyValue(u"NavigationSlidePrev"_ustr, -1,
                                                             Any(xPrevSBitmap),
                                                             beans::PropertyState_DIRECT_VALUE));
                }
                BitmapEx menuSlideBm(menuPath);
                const Reference<rendering::XBitmap> xMenuSBitmap(
                    vcl::unotools::xBitmapFromBitmapEx(menuSlideBm));
                if (xMenuSBitmap.is())
                {
                    mxShow->setProperty(beans::PropertyValue(u"NavigationSlideMenu"_ustr, -1,
                                                             Any(xMenuSBitmap),
                                                             beans::PropertyState_DIRECT_VALUE));
                }
                BitmapEx nextSlideBm(nextSlidePath);
                const Reference<rendering::XBitmap> xNextSBitmap(
                    vcl::unotools::xBitmapFromBitmapEx(nextSlideBm));
                if (xNextSBitmap.is())
                {
                    mxShow->setProperty(beans::PropertyValue(u"NavigationSlideNext"_ustr, -1,
                                                             Any(xNextSBitmap),
                                                             beans::PropertyState_DIRECT_VALUE));
                }
            }
        }

        forconst auto& rProp : aProperties )
            mxShow->setProperty( rProp );

        mxShow->addView( mxView );

        mxListenerProxy.set( new SlideShowListenerProxy( this, mxShow ) );
        mxListenerProxy->addAsSlideShowListener();

        // IASS: Do only startup the PresenterConsole if this is not
        // the SlideShow Preview mode (else would be double)
        if (!mbInterActiveSetup)
        {
            // IASS: This is the central methodology to 'steer' the
            // PresenterConsole - in this case, to start it up and make
            // it visible (if activated)
            NotifyDocumentEvent(
                *mpDoc,
                u"OnStartPresentation"_ustr);
        }

        displaySlideIndex( mpSlideController->getStartSlideIndex() );

        return true;
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::startShowImpl()" );
        return false;
    }
}

/** called only by the slideshow view when the first paint event occurs.
    This actually starts the slideshow. */

void SlideshowImpl::onFirstPaint()
{
    if( mpShowWindow )
    {
        /*
        mpShowWindow->SetBackground( Wallpaper( COL_BLACK ) );
        mpShowWindow->Erase();
        mpShowWindow->SetBackground();
        */

    }

    SolarMutexGuard aSolarGuard;
    maUpdateTimer.SetTimeout( sal_uLong(100) );
    maUpdateTimer.Start();
}

void SlideshowImpl::paint()
{
    if( mxView.is() ) try
    {
        awt::PaintEvent aEvt;
        // aEvt.UpdateRect = TODO
        mxView->paint( aEvt );
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::paint()" );
    }
}

void SAL_CALL SlideshowImpl::addSlideShowListener( const Reference< XSlideShowListener >&&nbsp;xListener )
{
    if( mxListenerProxy.is() )
        mxListenerProxy->addSlideShowListener( xListener );
}

void SAL_CALL SlideshowImpl::removeSlideShowListener( const Reference< XSlideShowListener >& xListener )
{
    if( mxListenerProxy.is() )
        mxListenerProxy->removeSlideShowListener( xListener );
}

void SlideshowImpl::slideEnded(const bool bReverse)
{
    if (bReverse)
        gotoPreviousSlide(true);
    else
        gotoNextSlide();
}

bool SlideshowImpl::swipe(const CommandGestureSwipeData &rSwipeData)
{
    if (mbUsePen || mnContextMenuEvent)
        return false;
    double nVelocityX = rSwipeData.getVelocityX();
    // tdf#108475 make it swipe only if some reasonable movement was involved
    if (fabs(nVelocityX) < 50)
        return false;
    if (nVelocityX > 0)
    {
        gotoPreviousSlide();
    }
    else
    {
        gotoNextEffect();
    }
    //a swipe is followed by a mouse up, tell the view to ignore that mouse up as we've reacted
    //to the swipe instead
    mxView->ignoreNextMouseReleased();
    return true;
}

bool SlideshowImpl::longpress(const CommandGestureLongPressData &rLongPressData)
{
    if (mnContextMenuEvent)
        return false;

    maPopupMousePos = Point(rLongPressData.getX(), rLongPressData.getY());
    mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) );

    return true;
}

void SlideshowImpl::removeShapeEvents()
{
    if( !(mxShow.is() && mxListenerProxy.is()) )
        return;

    try
    {
        forconst auto& rEntry : maShapeEventMap )
        {
            mxListenerProxy->removeShapeEventListener( rEntry.first );
            mxShow->setShapeCursor( rEntry.first, awt::SystemPointer::ARROW );
        }

        maShapeEventMap.clear();
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::removeShapeEvents()" );
    }
}

void SlideshowImpl::registerShapeEvents(sal_Int32 nSlideNumber)
{
    if( nSlideNumber < 0 )
        return;

    try
    {
        Reference< XIndexAccess > xPages( mxModel->getDrawPages(), UNO_QUERY_THROW );

        Reference< XShapes > xDrawPage;
        xPages->getByIndex(nSlideNumber) >>= xDrawPage;

        if( xDrawPage.is() )
        {
            Reference< XMasterPageTarget > xMasterPageTarget( xDrawPage, UNO_QUERY );
            if( xMasterPageTarget.is() )
            {
                Reference< XShapes > xMasterPage = xMasterPageTarget->getMasterPage();
                if( xMasterPage.is() )
                    registerShapeEvents( xMasterPage );
            }
            registerShapeEvents( xDrawPage );
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::registerShapeEvents()" );
    }
}

void SlideshowImpl::registerShapeEvents( Reference< XShapes > const & xShapes )
{
    try
    {
        const sal_Int32 nShapeCount = xShapes->getCount();
        sal_Int32 nShape;
        for( nShape = 0; nShape < nShapeCount; nShape++ )
        {
            Reference< XShape > xShape;
            xShapes->getByIndex( nShape ) >>= xShape;

            if( xShape.is() && xShape->getShapeType() == "com.sun.star.drawing.GroupShape" )
            {
                Reference< XShapes > xSubShapes( xShape, UNO_QUERY );
                if( xSubShapes.is() )
                    registerShapeEvents( xSubShapes );
            }

            Reference< XPropertySet > xSet( xShape, UNO_QUERY );
            if( !xSet.is() )
                continue;

            Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() );
            if( !xSetInfo.is() || !xSetInfo->hasPropertyByName( gsOnClick ) )
                continue;

            WrappedShapeEventImplPtr pEvent = std::make_shared<WrappedShapeEventImpl>();
            xSet->getPropertyValue( gsOnClick ) >>= pEvent->meClickAction;

            switch( pEvent->meClickAction )
            {
            case ClickAction_PREVPAGE:
            case ClickAction_NEXTPAGE:
            case ClickAction_FIRSTPAGE:
            case ClickAction_LASTPAGE:
            case ClickAction_STOPPRESENTATION:
                break;
            case ClickAction_BOOKMARK:
                if( xSetInfo->hasPropertyByName( gsBookmark ) )
                    xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark;
                if( getSlideNumberForBookmark( pEvent->maStrBookmark ) == -1 )
                    continue;
                break;
            case ClickAction_DOCUMENT:
            case ClickAction_SOUND:
            case ClickAction_PROGRAM:
            case ClickAction_MACRO:
                if( xSetInfo->hasPropertyByName( gsBookmark ) )
                    xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark;
                break;
            case ClickAction_VERB:
                if( xSetInfo->hasPropertyByName( gsVerb ) )
                    xSet->getPropertyValue( gsVerb ) >>= pEvent->mnVerb;
                break;
            default:
                continue// skip all others
            }

            maShapeEventMap[ xShape ] = std::move(pEvent);

            if( mxListenerProxy.is() )
                mxListenerProxy->addShapeEventListener( xShape );
            mxShow->setShapeCursor( xShape, awt::SystemPointer::REFHAND );
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::registerShapeEvents()" );
    }
}

void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects)
{
    stopSound();
    removeShapeEvents();

    if( mpSlideController && mxShow.is() )
    {
        rtl::Reference< SdXImpressDocument > xDrawPages( mpDoc->getUnoModel() );
        mpSlideController->displayCurrentSlide( mxShow, xDrawPages, bSkipAllMainSequenceEffects );
        registerShapeEvents(mpSlideController->getCurrentSlideNumber());
        update();

    }
    // send out page change event and notify to update all acc info for current page
    if (mpViewShell)
    {
        sal_Int32 currentPageIndex = getCurrentSlideIndex();
        mpViewShell->fireSwitchCurrentPage(currentPageIndex);
        mpViewShell->NotifyAccUpdate();
    }
}

void SlideshowImpl::endPresentation()
{
    if( maPresSettings.mbMouseAsPen)
    {
        rtl::Reference< SdXImpressDocument > xDocFactory(mpDoc->getUnoModel() );
        if( xDocFactory.is() )
            mxShow->registerUserPaintPolygons(xDocFactory);
    }

    if( !mnEndShowEvent )
        mnEndShowEvent = Application::PostUserEvent( LINK(this, SlideshowImpl, endPresentationHdl) );
}

IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void)
{
    mnEndShowEvent = nullptr;

    stopSound();

    if( mxPresentation.is() )
        mxPresentation->end();
}

void SAL_CALL SlideshowImpl::pause()
{
    SolarMutexGuard aSolarGuard;

    if( mbIsPaused )
        return;

    try
    {
        mbIsPaused = true;
        if( mxShow.is() )
        {
            mxShow->pause(true);

            if( mxListenerProxy.is() )
                mxListenerProxy->paused();
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::pause()" );
    }
}

void SAL_CALL SlideshowImpl::resume()
{
    SolarMutexGuard aSolarGuard;

    if( mbIsPaused ) try
    {
        if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK || mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END )
        {
            mpShowWindow->RestartShow();
        }
        else
        {
            mbIsPaused = false;
            if( mxShow.is() )
            {
                mxShow->pause(false);
                update();

                if( mxListenerProxy.is() )
                    mxListenerProxy->resumed();
            }
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::resume()" );
    }
#ifdef ENABLE_SDREMOTE
    RemoteServer::presentationStarted( this );
#endif
}

sal_Bool SAL_CALL SlideshowImpl::isPaused()
{
    SolarMutexGuard aSolarGuard;
    return mbIsPaused;
}

void SAL_CALL SlideshowImpl::blankScreen( sal_Int32 nColor )
{
    SolarMutexGuard aSolarGuard;

    if( mpShowWindow && mpSlideController )
    {
        if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), Color(ColorTransparency, nColor) ) )
        {
            pause();
        }
    }
}

// XShapeEventListener

void SlideshowImpl::click( const Reference< XShape >& xShape )
{
    SolarMutexGuard aSolarGuard;

    WrappedShapeEventImplPtr pEvent = maShapeEventMap[xShape];
    if( !pEvent )
        return;

    switch( pEvent->meClickAction )
    {
    case ClickAction_PREVPAGE:          gotoPreviousSlide();        break;
    case ClickAction_NEXTPAGE:          gotoNextSlide();            break;
    case ClickAction_FIRSTPAGE:         gotoFirstSlide();           break;
    case ClickAction_LASTPAGE:          gotoLastSlide();            break;
    case ClickAction_STOPPRESENTATION:  endPresentation();          break;
    case ClickAction_BOOKMARK:
    {
        gotoBookmark( pEvent->maStrBookmark );
    }
    break;
    case ClickAction_SOUND:
    {
#if HAVE_FEATURE_AVMEDIA
        try
        {
            mxPlayer.set(avmedia::MediaWindow::createPlayer(pEvent->maStrBookmark, u""_ustr/*TODO?*/), uno::UNO_SET_THROW );
            mxPlayer->start();
        }
        catch( uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::click()" );
        }
#endif
    }
    break;

    case ClickAction_DOCUMENT:
    {
        OUString aBookmark( pEvent->maStrBookmark );

        sal_Int32 nPos = aBookmark.indexOf( '#' );
        if( nPos >= 0 )
        {
            OUString aURL( aBookmark.copy( 0, nPos+1 ) );
            OUString aName( aBookmark.copy( nPos+1 ) );
            aURL += getUiNameFromPageApiNameImpl( aName );
            aBookmark = aURL;
        }

        mpDocSh->OpenBookmark( aBookmark );
    }
    break;

    case ClickAction_PROGRAM:
    {
        INetURLObject aURL(
            ::URIHelper::SmartRel2Abs(
                INetURLObject(mpDocSh->GetMedium()->GetBaseURL()),
                pEvent->maStrBookmark, ::URIHelper::GetMaybeFileHdl(), true,
                false, INetURLObject::EncodeMechanism::WasEncoded,
                INetURLObject::DecodeMechanism::Unambiguous ) );

        if( INetProtocol::File == aURL.GetProtocol() )
        {
            SfxStringItem aUrl( SID_FILE_NAME, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
            SfxBoolItem aBrowsing( SID_BROWSE, true );

            if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
            {
                SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pViewFrm->GetFrame().GetFrameInterface());
                pViewFrm->GetDispatcher()->ExecuteList( SID_OPENDOC,
                    SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
                    { &aUrl, &aBrowsing }, { &aDocFrame } );
            }
        }
    }
    break;

#if HAVE_FEATURE_SCRIPTING
    case presentation::ClickAction_MACRO:
    {
        const OUString aMacro( pEvent->maStrBookmark );

        if ( SfxApplication::IsXScriptURL( aMacro ) )
        {
            Any aRet;
            Sequence< sal_Int16 > aOutArgsIndex;
            Sequence< Any > aOutArgs;
            Sequence< Any >* pInArgs = new Sequence< Any >(0);
            mpDocSh->CallXScript( aMacro, *pInArgs, aRet, aOutArgsIndex, aOutArgs);
        }
        else
        {
            // aMacro has the following syntax:
            // "Macroname.Modulname.Libname.Documentname" or
            // "Macroname.Modulname.Libname.Applicationname"
            sal_Int32 nIdx{ 0 };
            const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx);
            const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx);

            // todo: is the limitation still given that only
            // Modulname+Macroname can be used here?
            OUString aExecMacro = OUString::Concat(aModulName) + "." + aMacroName;
            mpDocSh->GetBasic()->Call(aExecMacro);
        }
    }
    break;
#endif

    case ClickAction_VERB:
    {
        // todo, better do it async?
        SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
        SdrOle2Obj* pOleObject = dynamic_cast< SdrOle2Obj* >(pObj);
        if (pOleObject && mpViewShell )
            mpViewShell->ActivateObject(pOleObject, pEvent->mnVerb);
    }
    break;
    default:
        break;
    }
}

sal_Int32 SlideshowImpl::getSlideNumberForBookmark( const OUString& rStrBookmark )
{
    bool bIsMasterPage;
    OUString aBookmark = getUiNameFromPageApiNameImpl( rStrBookmark );
    sal_uInt16 nPgNum = mpDoc->GetPageByName( aBookmark, bIsMasterPage );

    if( nPgNum == SDRPAGE_NOTFOUND )
    {
        // Is the bookmark an object?
        SdrObject* pObj = mpDoc->GetObj( aBookmark );

        if( pObj )
        {
            nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum();
            bIsMasterPage = pObj->getSdrPageFromSdrObject()->IsMasterPage();
        }
    }

    if( (nPgNum == SDRPAGE_NOTFOUND) || bIsMasterPage || static_cast<SdPage*>(mpDoc->GetPage(nPgNum))->GetPageKind() != PageKind::Standard )
        return -1;

    return ( nPgNum - 1) >> 1;
}

void SlideshowImpl::contextMenuShow(const css::awt::Point& point)
{
    maPopupMousePos = { point.X, point.Y };
    mnContextMenuEvent = Application::PostUserEvent(LINK(this, SlideshowImpl, ContextMenuHdl));
}

void SlideshowImpl::hyperLinkClicked( OUString const& aHyperLink )
{
    OUString aBookmark( aHyperLink );

    sal_Int32 nPos = aBookmark.indexOf( '#' );
    if( nPos >= 0 )
    {
        OUString aURL( aBookmark.copy( 0, nPos+1 ) );
        OUString aName( aBookmark.copy( nPos+1 ) );
        aURL += getUiNameFromPageApiNameImpl( aName );
        aBookmark = aURL;
    }

    mpDocSh->OpenBookmark( aBookmark );
}

void SlideshowImpl::displaySlideNumber( sal_Int32 nSlideNumber )
{
    if( mpSlideController )
    {
        if( mpSlideController->jumpToSlideNumber( nSlideNumber ) )
        {
            displayCurrentSlide();
        }
    }
}

/** nSlideIndex == -1 displays current slide again */
void SlideshowImpl::displaySlideIndex( sal_Int32 nSlideIndex )
{
    if( mpSlideController )
    {
        if( (nSlideIndex == -1) || mpSlideController->jumpToSlideIndex( nSlideIndex ) )
        {
            displayCurrentSlide();
        }
    }
}

void SlideshowImpl::jumpToBookmark( const OUString& sBookmark )
{
    sal_Int32 nSlideNumber = getSlideNumberForBookmark( sBookmark );
    if( nSlideNumber != -1 )
        displaySlideNumber( nSlideNumber );
}

sal_Int32 SlideshowImpl::getCurrentSlideNumber() const
{
    return mpSlideController ? mpSlideController->getCurrentSlideNumber() : -1;
}

sal_Bool SAL_CALL SlideshowImpl::isEndless()
{
    SolarMutexGuard aSolarGuard;
    return maPresSettings.mbEndless;
}

void SlideshowImpl::update()
{
    startUpdateTimer();
}

void SlideshowImpl::startUpdateTimer()
{
    SolarMutexGuard aSolarGuard;
    maUpdateTimer.SetTimeout( 0 );
    maUpdateTimer.Start();
}

/** this timer is called 20ms after a new slide was displayed.
    This is used to unfreeze user input that was disabled after
    slide change to skip input that was buffered during slide
    transition preparation */

IMPL_LINK_NOARG(SlideshowImpl, ReadyForNextInputHdl, Timer *, void)
{
    mbInputFreeze = false;
}

/** if I catch someone someday who calls this method by hand
    and not by using the timer, I will personally punish this
    person seriously, even if this person is me.
*/

IMPL_LINK_NOARG(SlideshowImpl, updateHdl, Timer *, void)
{
    updateSlideShow();
}

void SlideshowImpl::updateSlideShow()
{
    // prevent me from deletion when recursing (App::EnableYieldMode does)
    const rtl::Reference<SlideshowImpl> xKeepAlive(this);

    Reference< XSlideShow > xShow( mxShow );
    if ( ! xShow.is())
        return;

    try
    {
        double fUpdate = 0.0;
        if( !xShow->update(fUpdate) )
            fUpdate = -1.0;

        if (mxShow.is() && (fUpdate >= 0.0))
        {
            if (::basegfx::fTools::equalZero(fUpdate))
            {
                // Make sure idle tasks don't starve when we don't have to wait.
                // Don't process any events generated after invoking the function.
                Application::Reschedule(/*bHandleAllCurrentEvents=*/true);
            }
            else
            {
                // Avoid busy loop when the previous call to update()
                // returns a small positive number but not 0 (which is
                // handled above).  Also, make sure that calls to update()
                // have a minimum frequency.
                // => Allow up to 60 frames per second.  Call at least once
                // every 4 seconds.
                const static sal_Int32 nMaximumFrameCount (60);
                const static double nMinimumTimeout (1.0 / nMaximumFrameCount);
                const static double nMaximumTimeout (4.0);
                fUpdate = std::clamp(fUpdate, nMinimumTimeout, nMaximumTimeout);

                // Make sure that the maximum frame count has not been set
                // too high (only then conversion to milliseconds and long
                // integer may lead to zero value.)
                OSL_ASSERT(static_cast<sal_uLong>(fUpdate * 1000.0) > 0);
            }

            // Use our high resolution timers for the asynchronous callback.
            maUpdateTimer.SetTimeout(static_cast<sal_uLong>(fUpdate * 1000.0));
            maUpdateTimer.Start();
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::updateSlideShow()" );
    }
}

bool SlideshowImpl::keyInput(const KeyEvent& rKEvt)
{
    if( !mxShow.is() || mbInputFreeze )
        return false;

    bool bRet = true;

    try
    {
        const int nKeyCode = rKEvt.GetKeyCode().GetCode();
        switch( nKeyCode )
        {
            case awt::Key::CONTEXTMENU:
                if( !mnContextMenuEvent )
                {
                    if( mpShowWindow )
                        maPopupMousePos = mpShowWindow->GetPointerState().maPos;
                    mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) );
                }
                break;

            // cancel show
            case KEY_ESCAPE:
            case KEY_SUBTRACT:
                // in case the user cancels the presentation, switch to current slide
                // in edit mode
                if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) )
                {
                    if( mpSlideController->getCurrentSlideNumber() != -1 )
                        mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
                }
                endPresentation();
                break;

            // advance show
            case KEY_PAGEDOWN:
                if(rKEvt.GetKeyCode().IsMod2())
                {
                    gotoNextSlide();
                    break;
                }
                [[fallthrough]];
            case KEY_SPACE:
            case KEY_RIGHT:
            case KEY_DOWN:
            case KEY_XF86FORWARD:
                gotoNextEffect();
                break;

            case KEY_RETURN:
            {
                if( !maCharBuffer.isEmpty() )
                {
                    if( mpSlideController )
                    {
                        if( mpSlideController->jumpToSlideNumber( maCharBuffer.toInt32() - 1 ) )
                            displayCurrentSlide();
                    }
                    maCharBuffer.clear();
                }
                else
                {
                    gotoNextEffect();
                }
            }
            break;

            // numeric: add to buffer
            case KEY_0:
            case KEY_1:
            case KEY_2:
            case KEY_3:
            case KEY_4:
            case KEY_5:
            case KEY_6:
            case KEY_7:
            case KEY_8:
            case KEY_9:
                maCharBuffer += OUStringChar( rKEvt.GetCharCode() );
                break;

            case KEY_PAGEUP:
                if(rKEvt.GetKeyCode().IsMod2())
                {
                    gotoPreviousSlide();
                    break;
                }
                [[fallthrough]];
            case KEY_LEFT:
            case KEY_UP:
            case KEY_BACKSPACE:
            case KEY_XF86BACK:
                gotoPreviousEffect();
                break;

            case KEY_P:
                setUsePen( !mbUsePen );
                break;

            // tdf#149351 Ctrl+A disables pointer as pen mode
            case KEY_A:
                if(rKEvt.GetKeyCode().IsMod1())
                {
                    setUsePen( false );
                    break;
                }
            break;

            case KEY_E:
                setEraseAllInk( true );
                updateSlideShow();
                break;

            case KEY_HOME:
                gotoFirstSlide();
                break;

            case KEY_END:
                gotoLastSlide();
                break;

            case KEY_B:
            case KEY_W:
            case KEY_POINT:
            case KEY_COMMA:
            {
                blankScreen( ((nKeyCode == KEY_W ) || (nKeyCode == KEY_COMMA)) ? 0x00ffffff : 0x00000000 );
            }
            break;

            default:
                bRet = false;
            break;
        }
    }
    catch( Exception& )
    {
        bRet = false;
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::keyInput()" );
    }

    return bRet;
}

IMPL_LINK( SlideshowImpl, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void )
{
    if( !mxShow.is() || mbInputFreeze )
        return;

    if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData()) )
        return;

    const CommandEvent& rEvent = *static_cast<const CommandEvent*>(static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData());

    if( rEvent.GetCommand() != CommandEventId::Media )
        return;

    CommandMediaData* pMediaData = rEvent.GetMediaData();
    pMediaData->SetPassThroughToOS(false);
    switch (pMediaData->GetMediaId())
    {
#if defined( MACOSX )
    case MediaCommand::Menu:
        if( !mnContextMenuEvent )
        {
            if( mpShowWindow )
                maPopupMousePos = mpShowWindow->GetPointerState().maPos;
            mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) );
        }
        break;
    case MediaCommand::VolumeDown:
        gotoPreviousSlide();
        break;
    case MediaCommand::VolumeUp:
        gotoNextEffect();
        break;
#endif
    case MediaCommand::NextTrack:
        gotoNextEffect();
        break;
    case MediaCommand::Pause:
        if( !mbIsPaused )
            blankScreen(0);
        break;
    case MediaCommand::Play:
        if( mbIsPaused )
            resume();
        break;

    case MediaCommand::PlayPause:
        if( mbIsPaused )
            resume();
        else
            blankScreen(0);
        break;
    case MediaCommand::PreviousTrack:
        gotoPreviousSlide();
        break;
    case MediaCommand::NextTrackHold:
        gotoLastSlide();
        break;

    case MediaCommand::Rewind:
        gotoFirstSlide();
        break;
    case MediaCommand::Stop:
        // in case the user cancels the presentation, switch to current slide
        // in edit mode
        if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) )
        {
            if( mpSlideController->getCurrentSlideNumber() != -1 )
                mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
        }
        endPresentation();
        break;
    default:
        pMediaData->SetPassThroughToOS(true);
        break;
    }
}

void SlideshowImpl::mouseButtonUp(const MouseEvent& rMEvt)
{
    if( rMEvt.IsRight() && !mnContextMenuEvent )
    {
        maPopupMousePos = rMEvt.GetPosPixel();
        mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) );
    }
}

IMPL_LINK_NOARG(SlideshowImpl, ContextMenuHdl, void*, void)
{
    mnContextMenuEvent = nullptr;

    if (mpSlideController == nullptr)
        return;

    mbWasPaused = mbIsPaused;
    if( !mbWasPaused )
        pause();

    std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, u"modules/simpress/ui/slidecontextmenu.ui"_ustr));
    std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu(u"menu"_ustr));
    OUString sNextImage(BMP_MENU_NEXT), sPrevImage(BMP_MENU_PREV);
    xMenu->insert(0, u"next"_ustr, SdResId(RID_SVXSTR_MENU_NEXT), &sNextImage, nullptr, nullptr, TRISTATE_INDET);
    xMenu->insert(1, u"prev"_ustr, SdResId(RID_SVXSTR_MENU_PREV), &sPrevImage, nullptr, nullptr, TRISTATE_INDET);

    // Adding button to display if in Pen  mode
    xMenu->set_active(u"pen"_ustr, mbUsePen);

    const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
    xMenu->set_visible(u"next"_ustr, mpSlideController->getNextSlideIndex() != -1);
    xMenu->set_visible(u"prev"_ustr, (mpSlideController->getPreviousSlideIndex() != -1 ) || (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK));
    xMenu->set_visible(u"edit"_ustr, mpViewShell->GetDoc()->GetStartWithPresentation() != 0);

    std::unique_ptr<weld::Menu> xPageMenu(xBuilder->weld_menu(u"gotomenu"_ustr));
    OUString sFirstImage(BMP_MENU_FIRST), sLastImage(BMP_MENU_LAST);
    xPageMenu->insert(0, u"first"_ustr, SdResId(RID_SVXSTR_MENU_FIRST), &sFirstImage, nullptr, nullptr, TRISTATE_INDET);
    xPageMenu->insert(1, u"last"_ustr, SdResId(RID_SVXSTR_MENU_LAST), &sLastImage, nullptr, nullptr, TRISTATE_INDET);

    // populate slide goto list
    const sal_Int32 nPageNumberCount = mpSlideController->getSlideNumberCount();
    if( nPageNumberCount <= 1 )
    {
        xMenu->set_visible(u"goto"_ustr, false);
    }
    else
    {
        sal_Int32 nCurrentSlideNumber = mpSlideController->getCurrentSlideNumber();
        if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
            nCurrentSlideNumber = -1;

        xPageMenu->set_visible(u"first"_ustr, mpSlideController->getSlideNumber(0) != nCurrentSlideNumber);
        xPageMenu->set_visible(u"last"_ustr, mpSlideController->getSlideNumber(mpSlideController->getSlideIndexCount() - 1) != nCurrentSlideNumber);

        sal_Int32 nPageNumber;

        for( nPageNumber = 0; nPageNumber < nPageNumberCount; nPageNumber++ )
        {
            if( mpSlideController->isVisibleSlideNumber( nPageNumber ) )
            {
                SdPage* pPage = mpDoc->GetSdPage(static_cast<sal_uInt16>(nPageNumber), PageKind::Standard);
                if (pPage)
                {
                    OUString sId(OUString::number(CM_SLIDES + nPageNumber));
                    xPageMenu->append_check(sId, pPage->GetName());
                    if (nPageNumber == nCurrentSlideNumber)
                        xPageMenu->set_active(sId, true);
                }
            }
        }
    }

    std::unique_ptr<weld::Menu> xBlankMenu(xBuilder->weld_menu(u"screenmenu"_ustr));

    if (mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK)
    {
        xBlankMenu->set_active((mpShowWindow->GetBlankColor() == COL_WHITE) ? "white" : "black"true);
    }

    std::unique_ptr<weld::Menu> xWidthMenu(xBuilder->weld_menu(u"widthmenu"_ustr));

    // populate color width list
    sal_Int32 nIterator;
    double nWidth;

    nWidth = 4.0;
    for( nIterator = 1; nIterator < 6; nIterator++)
    {
        switch(nIterator)
        {
            case 1:
                nWidth = 4.0;
                break;
            case 2:
                nWidth = 100.0;
                break;
            case 3:
                nWidth = 150.0;
                break;
            case 4:
                nWidth = 200.0;
                break;
            case 5:
                nWidth = 400.0;
                break;
            default:
                break;
        }

        if (nWidth == mdUserPaintStrokeWidth)
            xWidthMenu->set_active(OUString::number(nWidth), true);
    }

    ::tools::Rectangle aRect(maPopupMousePos, Size(1,1));
    weld::Window* pParent = weld::GetPopupParent(*mpShowWindow, aRect);
    ContextMenuSelectHdl(xMenu->popup_at_rect(pParent, aRect));

    if( mxView.is() )
        mxView->ignoreNextMouseReleased();

    if( !mbWasPaused )
        resume();
}

void SlideshowImpl::ContextMenuSelectHdl(std::u16string_view rMenuId)
{
    if (rMenuId == u"prev")
    {
        gotoPreviousSlide();
        mbWasPaused = false;
    }
    else if(rMenuId == u"next")
    {
        gotoNextSlide();
        mbWasPaused = false;
    }
    else if (rMenuId == u"first")
    {
        gotoFirstSlide();
        mbWasPaused = false;
    }
    else if (rMenuId == u"last")
    {
        gotoLastSlide();
        mbWasPaused = false;
    }
    else if (rMenuId == u"black" || rMenuId == u"white")
    {
        const Color aBlankColor(rMenuId == u"white" ? COL_WHITE : COL_BLACK);
        if( mbWasPaused )
        {
            if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK )
            {
                if( mpShowWindow->GetBlankColor() == aBlankColor )
                {
                    mbWasPaused = false;
                    mpShowWindow->RestartShow();
                    return;
                }
            }
            mpShowWindow->RestartShow();
        }
        if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), aBlankColor ) )
        {
            pause();
            mbWasPaused = true;
        }
    }
    else if (rMenuId == u"color")
    {
        //Open a color picker based on SvColorDialog
        ::Color aColor( ColorTransparency, mnUserPaintColor );
        SvColorDialog aColorDlg;
        aColorDlg.SetColor( aColor );

        if (aColorDlg.Execute(mpShowWindow->GetFrameWeld()))
        {
            aColor = aColorDlg.GetColor();
            setPenColor(sal_Int32(aColor));
        }
        mbWasPaused = false;
    }
    else if (rMenuId == u"4")
    {
        setPenWidth(4.0);
        mbWasPaused = false;
    }
    else if (rMenuId == u"100")
    {
        setPenWidth(100.0);
        mbWasPaused = false;
    }
    else if (rMenuId == u"150")
    {
        setPenWidth(150.0);
        mbWasPaused = false;
    }
    else if (rMenuId == u"200")
    {
        setPenWidth(200.0);
        mbWasPaused = false;
    }
    else if (rMenuId == u"400")
    {
        setPenWidth(400.0);
        mbWasPaused = false;
    }
    else if (rMenuId == u"erase")
    {
        setEraseAllInk(true);
        mbWasPaused = false;
    }
    else if (rMenuId == u"pen")
    {
        setUsePen(!mbUsePen);
        mbWasPaused = false;
    }
    else if (rMenuId == u"edit")
    {
        // When in autoplay mode (pps/ppsx), offer editing of the presentation
        // Turn autostart off, else Impress will close when exiting the Presentation
        mpViewShell->GetDoc()->SetExitAfterPresenting(false);
        if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) )
        {
            if( mpSlideController->getCurrentSlideNumber() != -1 )
            {
                mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
            }
        }
        endPresentation();
    }
    else if (rMenuId == u"end")
    {
        // in case the user cancels the presentation, switch to current slide
        // in edit mode
        if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) )
        {
            if( mpSlideController->getCurrentSlideNumber() != -1 )
            {
                mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
            }
        }
        endPresentation();
    }
    else if (!rMenuId.empty())
    {
        sal_Int32 nPageNumber = o3tl::toInt32(rMenuId) - CM_SLIDES;
        const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
        if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
        {
            mpShowWindow->RestartShow( nPageNumber );
        }
        else if( nPageNumber != mpSlideController->getCurrentSlideNumber() )
        {
            displaySlideNumber( nPageNumber );
        }
        mbWasPaused = false;
    }
}

Reference< XSlideShow > SlideshowImpl::createSlideShow()
{
    Reference< XSlideShow > xShow;

    try
    {
        const Reference< uno::XComponentContext >& xContext =
            ::comphelper::getProcessComponentContext();

        xShow.set( presentation::SlideShow::create(xContext), UNO_SET_THROW );
    }
    catch( uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::createSlideShow()" );
    }

    return xShow;
}

void SlideshowImpl::createSlideList( bool bAll, std::u16string_view rPresSlide )
{
    const sal_uInt16 nSlideCount = mpDoc->GetSdPageCount( PageKind::Standard );

    if( !nSlideCount )
        return;

    SdCustomShow*   pCustomShow;

    if( mpDoc->GetCustomShowList() && maPresSettings.mbCustomShow )
        pCustomShow = mpDoc->GetCustomShowList()->GetCurObject();
    else
        pCustomShow = nullptr;

    // create animation slide controller
    AnimationSlideController::Mode eMode =
        ( pCustomShow && !pCustomShow->PagesVector().empty() ) ? AnimationSlideController::CUSTOM :
            (bAll ? AnimationSlideController::ALL : AnimationSlideController::FROM);

    rtl::Reference< SdXImpressDocument > xDrawPages( mpDoc->getUnoModel() );
    Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
    mpSlideController = std::make_shared<AnimationSlideController>( xSlides, eMode );

    if( eMode != AnimationSlideController::CUSTOM )
    {
        sal_Int32 nFirstVisibleSlide = 0;

        // normal presentation
        if( !rPresSlide.empty() )
        {
            sal_Int32 nSlide;
            bool bTakeNextAvailable = false;

            for( nSlide = 0, nFirstVisibleSlide = -1;
                ( nSlide < nSlideCount ) && ( -1 == nFirstVisibleSlide ); nSlide++ )
            {
                SdPage* pTestSlide = mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard );

                if( pTestSlide->GetName() == rPresSlide )
                {
                    if( pTestSlide->IsExcluded() )
                        bTakeNextAvailable = true;
                    else
                        nFirstVisibleSlide = nSlide;
                }
                else if( bTakeNextAvailable && !pTestSlide->IsExcluded() )
                    nFirstVisibleSlide = nSlide;
            }

            if( -1 == nFirstVisibleSlide )
                nFirstVisibleSlide = 0;
        }

        for( sal_Int32 i = 0; i < nSlideCount; i++ )
        {
            bool bVisible = ! mpDoc->GetSdPage( static_cast<sal_uInt16>(i), PageKind::Standard )->IsExcluded();
            if( bVisible || (eMode == AnimationSlideController::ALL) )
                mpSlideController->insertSlideNumber( i, bVisible );
        }

        mpSlideController->setStartSlideNumber( nFirstVisibleSlide );
    }
    else
    {
        if( meAnimationMode != ANIMATIONMODE_SHOW && !rPresSlide.empty() )
        {
            sal_Int32 nSlide;
            for( nSlide = 0; nSlide < nSlideCount; nSlide++ )
                if( rPresSlide == mpDoc->GetSdPage( static_cast<sal_uInt16>(nSlide), PageKind::Standard )->GetName() )
                    break;

            if( nSlide < nSlideCount )
                mpSlideController->insertSlideNumber( static_cast<sal_uInt16>(nSlide) );
        }

        forconst auto& rpPage : pCustomShow->PagesVector() )
        {
            const sal_uInt16 nSdSlide = ( rpPage->GetPageNum() - 1 ) / 2;

            if( ! mpDoc->GetSdPage( nSdSlide, PageKind::Standard )->IsExcluded())
                mpSlideController->insertSlideNumber( nSdSlide );
        }
    }
}

typedef sal_uInt16 (*FncGetChildWindowId)();

const FncGetChildWindowId aShowChildren[] =
{
    &AnimationChildWindow::GetChildWindowId,
    &Svx3DChildWindow::GetChildWindowId,
    &SvxFontWorkChildWindow::GetChildWindowId,
    &SvxColorChildWindow::GetChildWindowId,
    &SvxSearchDialogWrapper::GetChildWindowId,
    &SvxBmpMaskChildWindow::GetChildWindowId,
    &SvxIMapDlgChildWindow::GetChildWindowId,
    &SvxHlinkDlgWrapper::GetChildWindowId,
    &SfxInfoBarContainerChild::GetChildWindowId
};

void SlideshowImpl::hideChildWindows()
{
    mnChildMask = 0;

    if( ANIMATIONMODE_SHOW != meAnimationMode )
        return;

    SfxViewFrame* pViewFrame = getViewFrame();

    if( !pViewFrame )
        return;

    for( sal_uLong i = 0; i < SAL_N_ELEMENTS( aShowChildren ); i++ )
    {
        const sal_uInt16 nId = ( *aShowChildren[ i ] )();

        if( pViewFrame->GetChildWindow( nId ) )
        {
            pViewFrame->SetChildWindow( nId, false );
            mnChildMask |= ::tools::ULong(1) << i;
        }
    }
}

void SlideshowImpl::showChildWindows()
{
    if( ANIMATIONMODE_SHOW == meAnimationMode )
    {
        SfxViewFrame* pViewFrame = getViewFrame();
        if( pViewFrame )
        {
            for( sal_uLong i = 0; i < SAL_N_ELEMENTS(aShowChildren); i++ )
            {
                if( mnChildMask & ( ::tools::ULong(1) << i ) )
                    pViewFrame->SetChildWindow( ( *aShowChildren[ i ] )(), true );
            }
        }
    }
}

SfxViewFrame* SlideshowImpl::getViewFrame() const
{
    return mpViewShell ? mpViewShell->GetViewFrame() : nullptr;
}

SfxDispatcher* SlideshowImpl::getDispatcher() const
{
    return (mpViewShell && mpViewShell->GetViewFrame()) ? mpViewShell->GetViewFrame()->GetDispatcher() : nullptr;
}

SfxBindings* SlideshowImpl::getBindings() const
{
    return (mpViewShell && mpViewShell->GetViewFrame()) ? &mpViewShell->GetViewFrame()->GetBindings() : nullptr;
}

void SlideshowImpl::resize( const Size& rSize )
{
    maPresSize = rSize;

    if(mpShowWindow)
    {
        mpShowWindow->SetSizePixel( maPresSize );
        mpShowWindow->Show();
    }

    if( mxView.is() ) try
    {
        awt::WindowEvent aEvt;
        mxView->windowResized(aEvt);
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::resize()" );
    }
}

void SlideshowImpl::setActiveXToolbarsVisible( bool bVisible )
{
    // in case of ActiveX control the toolbars should not be visible if slide show runs in window mode
    // actually it runs always in window mode in case of ActiveX control
    if ( !(!maPresSettings.mbFullScreen && mpDocSh && mpDocSh->GetMedium()) )
        return;

    const SfxBoolItem* pItem = mpDocSh->GetMedium()->GetItemSet().GetItem(SID_VIEWONLY, false);
    if ( !(pItem && pItem->GetValue()) )
        return;

    // this is a plugin/activex mode, no toolbars should be visible during slide show
    // after the end of slide show they should be visible again
    SfxViewFrame* pViewFrame = getViewFrame();
    if( !pViewFrame )
        return;

    try
    {
        Reference< frame::XLayoutManager > xLayoutManager;
        Reference< beans::XPropertySet > xFrameProps( pViewFrame->GetFrame().GetFrameInterface(), UNO_QUERY_THROW );
        if ( ( xFrameProps->getPropertyValue( u"LayoutManager"_ustr )
                    >>= xLayoutManager )
          && xLayoutManager.is() )
        {
            xLayoutManager->setVisible( bVisible );
        }
    }
    catch( uno::Exception& )
    {}
}

void SAL_CALL SlideshowImpl::activate()
{
    SolarMutexGuard aSolarGuard;

    maDeactivateTimer.Stop();

    if( mbActive || !mxShow.is() )
        return;

    mbActive = true;

    if( ANIMATIONMODE_SHOW == meAnimationMode )
    {
        if( mbAutoSaveWasOn )
            setAutoSaveState( false );

        if( mpShowWindow )
        {
            SfxViewFrame* pViewFrame = getViewFrame();
            SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : nullptr;

            hideChildWindows();

            if( pDispatcher )
            {
                // filter all forbidden slots
                pDispatcher->SetSlotFilter( SfxSlotFilterState::ENABLED, pAllowed );
            }

            if( getBindings() )
                getBindings()->InvalidateAll(true);

            mpShowWindow->GrabFocus();
        }
    }

    resume();
}

void SAL_CALL SlideshowImpl::deactivate()
{
    SolarMutexGuard aSolarGuard;

    if( mbActive && mxShow.is() )
    {
        maDeactivateTimer.Start();
    }
}

IMPL_LINK_NOARG(SlideshowImpl, deactivateHdl, Timer *, void)
{
    if( !(mbActive && mxShow.is()) )
        return;

    mbActive = false;

    pause();

    if( ANIMATIONMODE_SHOW == meAnimationMode )
    {
        if( mbAutoSaveWasOn )
            setAutoSaveState( true );

        if( mpShowWindow )
        {
            showChildWindows();
        }
    }
}

sal_Bool SAL_CALL SlideshowImpl::isActive()
{
    SolarMutexGuard aSolarGuard;
    return mbActive;
}

void SlideshowImpl::setAutoSaveState( bool bOn)
{
    try
    {
        const uno::Reference<uno::XComponentContext>& xContext( ::comphelper::getProcessComponentContext() );

        uno::Reference< util::XURLTransformer > xParser(util::URLTransformer::create(xContext));
        util::URL aURL;
        aURL.Complete = "vnd.sun.star.autorecovery:/setAutoSaveState";
        xParser->parseStrict(aURL);

        Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue(u"AutoSaveState"_ustr, bOn) };

        uno::Reference< frame::XDispatch > xAutoSave = frame::theAutoRecovery::get(xContext);
        xAutoSave->dispatch(aURL, aArgs);
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::setAutoSaveState()");
    }
}

Reference< XDrawPage > SAL_CALL SlideshowImpl::getCurrentSlide()
{
    SolarMutexGuard aSolarGuard;

    Reference< XDrawPage > xSlide;
    if( mxShow.is() && mpSlideController )
    {
        sal_Int32 nSlide = getCurrentSlideNumber();
        if( (nSlide >= 0) && (nSlide < mpSlideController->getSlideNumberCount() ) )
            xSlide = mpSlideController->getSlideByNumber( nSlide );
    }

    return xSlide;
}

sal_Int32 SAL_CALL SlideshowImpl::getNextSlideIndex()
{
    SolarMutexGuard aSolarGuard;

    if( mxShow.is() )
    {
        return mpSlideController->getNextSlideIndex();
    }
    else
    {
        return -1;
    }
}

sal_Int32 SAL_CALL SlideshowImpl::getCurrentSlideIndex()
{
    return mpSlideController ? mpSlideController->getCurrentSlideIndex() : -1;
}

// css::presentation::XSlideShowController:

::sal_Int32 SAL_CALL SlideshowImpl::getSlideCount()
{
    return mpSlideController ? mpSlideController->getSlideIndexCount() : 0;
}

Reference< XDrawPage > SAL_CALL SlideshowImpl::getSlideByIndex(::sal_Int32 Index)
{
    if ((mpSlideController == nullptr) || (Index < 0)
        || (Index >= mpSlideController->getSlideIndexCount()))
        throw IndexOutOfBoundsException();

    return mpSlideController->getSlideByNumber( mpSlideController->getSlideNumber( Index ) );
}

sal_Bool SAL_CALL SlideshowImpl::getAlwaysOnTop()
{
    SolarMutexGuard aSolarGuard;
    return maPresSettings.mbAlwaysOnTop;
}

void SAL_CALL SlideshowImpl::setAlwaysOnTop( sal_Bool bAlways )
{
    SolarMutexGuard aSolarGuard;
    if( maPresSettings.mbAlwaysOnTop != bool(bAlways) )
    {
        maPresSettings.mbAlwaysOnTop = bAlways;
        // todo, can this be changed while running?
    }
}

sal_Bool SAL_CALL SlideshowImpl::isFullScreen()
{
    SolarMutexGuard aSolarGuard;
    return maPresSettings.mbFullScreen;
}

sal_Bool SAL_CALL SlideshowImpl::getMouseVisible()
{
    SolarMutexGuard aSolarGuard;
    return maPresSettings.mbMouseVisible;
}

void SAL_CALL SlideshowImpl::setMouseVisible( sal_Bool bVisible )
{
    SolarMutexGuard aSolarGuard;
    if( maPresSettings.mbMouseVisible != bool(bVisible) )
    {
        maPresSettings.mbMouseVisible = bVisible;
        if( mpShowWindow )
            mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible );
    }
}

sal_Bool SAL_CALL SlideshowImpl::getUsePen()
{
    SolarMutexGuard aSolarGuard;
    return mbUsePen;
}

void SAL_CALL SlideshowImpl::setUsePen( sal_Bool bMouseAsPen )
{
    SolarMutexGuard aSolarGuard;
    mbUsePen = bMouseAsPen;
    if( !mxShow.is() )
        return;

    try
    {
        // For Pencolor;
        beans::PropertyValue aPenProp;
        aPenProp.Name = "UserPaintColor";
        if( mbUsePen )
            aPenProp.Value <<= mnUserPaintColor;
        mxShow->setProperty( aPenProp );

        //for StrokeWidth :
        if( mbUsePen )
        {
            beans::PropertyValue aPenPropWidth;
            aPenPropWidth.Name = "UserPaintStrokeWidth";
            aPenPropWidth.Value <<= mdUserPaintStrokeWidth;
            mxShow->setProperty( aPenPropWidth );

            // for Pen Mode
            beans::PropertyValue aPenPropSwitchPenMode;
            aPenPropSwitchPenMode.Name = "SwitchPenMode";
            aPenPropSwitchPenMode.Value <<= true;
            mxShow->setProperty( aPenPropSwitchPenMode );
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::setUsePen()" );
    }
}

double SAL_CALL SlideshowImpl::getPenWidth()
{
    SolarMutexGuard aSolarGuard;
    return mdUserPaintStrokeWidth;
}

void SAL_CALL SlideshowImpl::setPenWidth( double dStrokeWidth )
{
    SolarMutexGuard aSolarGuard;
    mdUserPaintStrokeWidth = dStrokeWidth;
    setUsePen( true ); // enable pen mode, update color and width
}

sal_Int32 SAL_CALL SlideshowImpl::getPenColor()
{
    SolarMutexGuard aSolarGuard;
    return mnUserPaintColor;
}

void SAL_CALL SlideshowImpl::setPenColor( sal_Int32 nColor )
{
    SolarMutexGuard aSolarGuard;
    mnUserPaintColor = nColor;
    setUsePen( true ); // enable pen mode, update color
}

void SAL_CALL SlideshowImpl::setEraseAllInk(sal_Bool bEraseAllInk)
{
    if( !bEraseAllInk )
        return;

    SolarMutexGuard aSolarGuard;
    if( !mxShow.is() )
        return;

    try
    {
        beans::PropertyValue aPenPropEraseAllInk;
        aPenPropEraseAllInk.Name = "EraseAllInk";
        aPenPropEraseAllInk.Value <<= bEraseAllInk;
        mxShow->setProperty( aPenPropEraseAllInk );
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd.slideshow""sd::SlideshowImpl::setEraseAllInk()" );
    }
}

// XSlideShowController Methods
sal_Bool SAL_CALL SlideshowImpl::isRunning(  )
{
    SolarMutexGuard aSolarGuard;
    return mxShow.is();
}

void SAL_CALL SlideshowImpl::gotoNextEffect(  )
{
    SolarMutexGuard aSolarGuard;

    if( !(mxShow.is() && mpSlideController && mpShowWindow) )
        return;

    if( mbIsPaused && mpShowWindow->GetShowWindowMode() != SHOWWINDOWMODE_END )
        resume();

    const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
    if( eMode == SHOWWINDOWMODE_END )
    {
        endPresentation();
    }
    else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
    {
        mpShowWindow->RestartShow();
    }
    else
    {
        mxShow->nextEffect();
        update();
    }
}

void SAL_CALL SlideshowImpl::gotoPreviousEffect(  )
{
    SolarMutexGuard aSolarGuard;

    if( !(mxShow.is() && mpSlideController && mpShowWindow) )
        return;

    const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
    if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) || mbIsPaused )
    {
        resume();
    }
    else
    {
        mxShow->previousEffect();
        update();
    }
}

void SAL_CALL SlideshowImpl::gotoFirstSlide(  )
{
    SolarMutexGuard aSolarGuard;

    if( !(mpShowWindow && mpSlideController) )
        return;

    if( mbIsPaused )
        resume();

    if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END )
    {
        if( mpSlideController->getSlideIndexCount() )
            mpShowWindow->RestartShow( 0);
    }
    else
    {
        displaySlideIndex( 0 );
    }
}

void SAL_CALL SlideshowImpl::gotoNextSlide(  )
{
    SolarMutexGuard aSolarGuard;

    if( mbIsPaused )
        resume();

    const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
    if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
    {
        mpShowWindow->RestartShow();
    }
    else
    {
        // if this is a show, ignore user inputs and
        // start 20ms timer to reenable inputs to filter
        // buffered inputs during slide transition
        if( meAnimationMode == ANIMATIONMODE_SHOW )
        {
            mbInputFreeze = true;
            maInputFreezeTimer.Start();
        }

        if( mpSlideController )
        {
            if( mpSlideController->nextSlide() )
            {
                displayCurrentSlide();
            }
            else
            {
                stopSound();

                if( meAnimationMode == ANIMATIONMODE_PREVIEW )
                {
                    endPresentation();
                }
                else if( maPresSettings.mbEndless )
                {
                    if( maPresSettings.mnPauseTimeout )
                    {
                        if( mpShowWindow )
                        {
                            if ( maPresSettings.mbShowPauseLogo )
                            {
                                Graphic aGraphic(SfxApplication::GetApplicationLogo(360));
                                mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout, &aGraphic );
                            }
                            else
                                mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout );
                        }
                    }
                    else
                    {
                        displaySlideIndex( 0 );
                    }
                }
                else
                {
                    if( mpShowWindow )
                    {
                        mpShowWindow->SetEndMode();
                        if (!mpViewShell->GetDoc()->GetStartWithPresentation())
                            pause();
                    }
                }
            }
        }
    }
}

void SAL_CALL SlideshowImpl::gotoPreviousSlide(  )
{
    gotoPreviousSlide(false);
}

void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects)
{
    SolarMutexGuard aSolarGuard;

    if( !(mxShow.is() && mpSlideController) )
        return;

    try
    {
        if( mbIsPaused )
            resume();

        const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
        if( eMode == SHOWWINDOWMODE_END )
        {
            mpShowWindow->RestartShow( mpSlideController->getCurrentSlideIndex() );
        }
        else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
        {
            mpShowWindow->RestartShow();
        }
        else
        {
            if( mpSlideController->previousSlide())
                displayCurrentSlide(bSkipAllMainSequenceEffects);
            else if (bSkipAllMainSequenceEffects)
            {
                // We could not go to the previous slide (probably because
                // the current slide is already the first one).  We still
                // have to call displayCurrentSlide because the calling
                // slideshow can not determine whether there is a previous
                // slide or not and has already prepared for a slide change.
                // This slide change has to be completed now, even when
                // changing to the same slide.
                // Note that in this special case we do NOT pass
                // bSkipAllMainSequenceEffects because we display the same
                // slide as before and do not want to show all its effects.
                displayCurrentSlide();
            }
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::gotoPreviousSlide()" );
    }
}

void SAL_CALL SlideshowImpl::gotoLastSlide()
{
    SolarMutexGuard aSolarGuard;

    if( !mpSlideController )
        return;

    if( mbIsPaused )
        resume();

    const sal_Int32 nLastSlideIndex = mpSlideController->getSlideIndexCount() - 1;
    if( nLastSlideIndex >= 0 )
    {
        if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END )
        {
            mpShowWindow->RestartShow( nLastSlideIndex );
        }
        else
        {
            displaySlideIndex( nLastSlideIndex );
        }
    }
}

void SAL_CALL SlideshowImpl::gotoBookmark( const OUString& rBookmark )
{
    SolarMutexGuard aSolarGuard;

    if( mbIsPaused )
        resume();

    sal_Int32 nSlideNumber = getSlideNumberForBookmark( rBookmark );
    if( nSlideNumber != -1 )
        displaySlideNumber( nSlideNumber );
}

void SAL_CALL SlideshowImpl::gotoSlide( const Reference< XDrawPage >& xSlide )
{
    SolarMutexGuard aSolarGuard;

    if( !(mpSlideController && xSlide.is()) )
        return;

    if( mbIsPaused )
        resume();

    const sal_Int32 nSlideCount = mpSlideController->getSlideNumberCount();
    for( sal_Int32 nSlide = 0; nSlide < nSlideCount; nSlide++ )
    {
        if( mpSlideController->getSlideByNumber( nSlide ) == xSlide )
        {
            displaySlideNumber( nSlide );
        }
    }
}

void SAL_CALL SlideshowImpl::gotoSlideIndex( sal_Int32 nIndex )
{
    SolarMutexGuard aSolarGuard;

    if( mbIsPaused )
        resume();

    displaySlideIndex( nIndex );
}

void SAL_CALL SlideshowImpl::stopSound(  )
{
    SolarMutexGuard aSolarGuard;

    try
    {
        if( mxPlayer.is() )
        {
            mxPlayer->stop();
            mxPlayer.clear();
        }
    }
    catch( Exception& )
    {
        TOOLS_WARN_EXCEPTION( "sd""sd::SlideshowImpl::stopSound()" );
    }
}

// XIndexAccess

::sal_Int32 SAL_CALL SlideshowImpl::getCount(  )
{
    return getSlideCount();
}

css::uno::Any SAL_CALL SlideshowImpl::getByIndex( ::sal_Int32 Index )
{
    return Any( getSlideByIndex( Index ) );
}

css::uno::Type SAL_CALL SlideshowImpl::getElementType(  )
{
    return cppu::UnoType<XDrawPage>::get();
}

sal_Bool SAL_CALL SlideshowImpl::hasElements(  )
{
    return getSlideCount() != 0;
}

namespace
{
    class AsyncUpdateSlideshow_Impl
    {
    public:
        struct AsyncUpdateSlideshowData
        {
            SlideshowImpl* pSlideshowImpl;
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide;
            SdrHintKind eHintKind;
        };

        static ImplSVEvent* AsyncUpdateSlideshow(
            SlideshowImpl* pSlideshowImpl,
            const uno::Reference< css::drawing::XDrawPage >& rXCurrentSlide,
            SdrHintKind eHintKind)
        {
            AsyncUpdateSlideshowData* pNew(new AsyncUpdateSlideshowData);
            pNew->pSlideshowImpl = pSlideshowImpl;
            pNew->XCurrentSlide = rXCurrentSlide;
            pNew->eHintKind = eHintKind;
            return Application::PostUserEvent(LINK(nullptr, AsyncUpdateSlideshow_Impl, Update), pNew);
            // coverity[leaked_storage] - pDisruptor takes care of its own destruction at idle time
        }

        DECL_STATIC_LINK(AsyncUpdateSlideshow_Impl, Update, void*, void);
    };

    IMPL_STATIC_LINK(AsyncUpdateSlideshow_Impl, Update, void*, pData, void)
    {
        AsyncUpdateSlideshowData* pSlideData(static_cast<AsyncUpdateSlideshowData*>(pData));
        pSlideData->pSlideshowImpl->AsyncNotifyEvent(pSlideData->XCurrentSlide, pSlideData->eHintKind);
        delete pSlideData;
    }
}

void SlideshowImpl::AsyncNotifyEvent(
    const uno::Reference< css::drawing::XDrawPage >& rXCurrentSlide,
    const SdrHintKind eHintKind)
{
    switch (eHintKind)
    {
        case SdrHintKind::ObjectInserted:
        {
            mnEventObjectInserted = nullptr;

            // refresh single slide
            gotoSlide(rXCurrentSlide);
            break;
        }
        case SdrHintKind::ObjectRemoved:
        {
            mnEventObjectRemoved = nullptr;

            // refresh single slide
            gotoSlide(rXCurrentSlide);
            break;
        }
        case SdrHintKind::ObjectChange:
        {
            mnEventObjectChange = nullptr;

            // refresh single slide
            gotoSlide(rXCurrentSlide);
            break;
        }
        case SdrHintKind::PageOrderChange:
        {
            mnEventPageOrderChange = nullptr;

            // order of pages (object pages or master pages) changed (Insert/Remove/ChangePos)
            // rXCurrentSlide is the current slide before the change.
            rtl::Reference< SdXImpressDocument > xDrawPages( mpDoc->getUnoModel() );
            Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
            const sal_Int32 nNewSlideCount(xSlides.is() ? xSlides->getCount() : 0);

            if (nNewSlideCount != mpSlideController->getSlideNumberCount())
            {
                // need to reinitialize AnimationSlideController
                OUString aPresSlide( maPresSettings.maPresPage );
                createSlideList( maPresSettings.mbAll, aPresSlide );
            }

            // Check if current slide before change is still valid (maybe removed)
            const sal_Int32 nSlideCount(mpSlideController->getSlideNumberCount());
            bool bSlideStillValid(false);

            for (sal_Int32 nSlide(0); !bSlideStillValid && nSlide < nSlideCount; nSlide++)
            {
                if (rXCurrentSlide == mpSlideController->getSlideByNumber(nSlide))
                {
                    bSlideStillValid = true;
                }
            }

            if(bSlideStillValid)
            {
                // stay on that slide
                gotoSlide(rXCurrentSlide);
            }
            else
            {
                // not possible to stay on that slide, go to 1st slide (kinda restart)
                gotoFirstSlide();
            }
            break;
        }
        default:
            break;
    }
}

bool SlideshowImpl::isCurrentSlideInvolved(const SdrHint& rHint)
{
    // get current slide
    uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
    if (!XCurrentSlide.is())
        return false;

    SdrPage* pCurrentSlide(GetSdrPageFromXDrawPage(XCurrentSlide));
    if (nullptr == pCurrentSlide)
        return false;

    const SdrPage* pHintPage(rHint.GetPage());
    if (nullptr == pHintPage)
        return false;

    if (pHintPage->IsMasterPage())
    {
        if (pCurrentSlide->TRG_HasMasterPage())
        {
            // current slide uses MasterPage on which the change happened
            return pHintPage == &pCurrentSlide->TRG_GetMasterPage();
        }
    }

    // object on current slide was changed
    return pHintPage == pCurrentSlide;
}

void SlideshowImpl::sendHintSlideChanged(const SdrPage* pChangedPage) const
{
    if (nullptr == pChangedPage)
        return;

    if (!mxShow.is())
        return;

    mxShow->setProperty(
        beans::PropertyValue( u"HintSlideChanged"_ustr ,
            -1,
            Any( GetXDrawPageForSdrPage(const_cast<SdrPage*>(pChangedPage)) ),
            beans::PropertyState_DIRECT_VALUE ) );
}

void SlideshowImpl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
{
    if (SfxHintId::ThisIsAnSdrHint != rHint.GetId())
        // nothing to do for non-SdrHints
        return;

    if (nullptr == mpDoc)
        // better do nothing when no DrawModel (should not happen)
        return;

    // tdf#158664 I am surprised, but the 'this' instance keeps incarnated
    // when the slideshow was running once, so need to check for
    // SlideShow instance/running to be safe.
    // NOTE: isRunning() checks mxShow.is(), that is what we want
    if (!isRunning())
        // no SlideShow instance or not running, nothing to do
        return;

    const SdrHint& rSdrHint(static_cast<const SdrHint&>(rHint));
    const SdrHintKind eHintKind(rSdrHint.GetKind());

    switch (eHintKind)
    {
        case SdrHintKind::ObjectInserted:
        {
            if (nullptr != mnEventObjectInserted)
                // avoid multiple events
                return;

            // tdf#160669 IASS: inform about ALL changed slides due to prefetch
            sendHintSlideChanged(rSdrHint.GetPage());

            if (!isCurrentSlideInvolved(rSdrHint))
                // nothing to do when current slide is not involved
                return;

            // Refresh current slide
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
            mnEventObjectInserted = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind);
            break;
        }
        case SdrHintKind::ObjectRemoved:
        {
            if (nullptr != mnEventObjectRemoved)
                // avoid multiple events
                return;

            // tdf#160669 IASS: inform about ALL changed slides due to prefetch
            sendHintSlideChanged(rSdrHint.GetPage());

            if (!isCurrentSlideInvolved(rSdrHint))
                // nothing to do when current slide is not involved
                return;

            // Refresh current slide
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
            mnEventObjectRemoved = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind);
            break;
        }
        case SdrHintKind::ObjectChange:
        {
            if (nullptr != mnEventObjectChange)
                // avoid multiple events
                return;

            // tdf#160669 IASS: inform about ALL changed slides due to prefetch
            sendHintSlideChanged(rSdrHint.GetPage());

            if (!isCurrentSlideInvolved(rSdrHint))
                // nothing to do when current slide is not involved
                return;

            // Refresh current slide. Need to do that asynchronous, else e.g.
            // text edit changes EditEngine/Outliner are not progressed far
            // enough (ObjectChanged broadcast which we are in here seems
            // too early for some cases)
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
            mnEventObjectChange = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind);
            break;
        }
        case SdrHintKind::PageOrderChange:
        {
            // Unfortunately we get multiple events, e.g. when drag/drop position change in
            // slide sorter on left side of EditView. This includes some with page number +1,
            // then again -1 (it's a position change). Problem is that in-between already
            // a re-schedule seems to happen, so indeed AsyncNotifyEvent will change to +1/-1
            // already. Since we get even more, at least try to take the last one. I found no
            // good solution yet for this.
            if (nullptr != mnEventPageOrderChange)
                Application::RemoveUserEvent( mnEventPageOrderChange );

            // tdf#160669 IASS: inform about ALL changed slides due to prefetch
            sendHintSlideChanged(rSdrHint.GetPage());

            // order of pages (object pages or master pages) changed (Insert/Remove/ChangePos)
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
            mnEventPageOrderChange = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind);
            break;
        }
        case SdrHintKind::ModelCleared:
        {
            // immediately end presentation
            endPresentation();
            break;
        }
        default:
            break;
    }
}

Reference< XSlideShow > SAL_CALL SlideshowImpl::getSlideShow()
{
    return mxShow;
}

PresentationSettingsEx::PresentationSettingsEx( const PresentationSettingsEx& r )
: PresentationSettings( r )
, mbRehearseTimings(r.mbRehearseTimings)
, mbPreview(r.mbPreview)
, mpParentWindow( nullptr )
{
}

PresentationSettingsEx::PresentationSettingsEx( PresentationSettings const & r )
: PresentationSettings( r )
, mbRehearseTimings(false)
, mbPreview(false)
, mpParentWindow(nullptr)
{
}

void PresentationSettingsEx::SetArguments( const Sequence< PropertyValue >& rArguments )
{
    forconst PropertyValue& rValue : rArguments )
    {
        SetPropertyValue( rValue.Name, rValue.Value );
    }
}

void PresentationSettingsEx::SetPropertyValue( std::u16string_view rProperty, const Any& rValue )
{
    if ( rProperty == u"RehearseTimings" )
    {
        if( rValue >>= mbRehearseTimings )
            return;
    }
    else if ( rProperty == u"Preview" )
    {
        if( rValue >>= mbPreview )
            return;
    }
    else if ( rProperty == u"AnimationNode" )
    {
        if( rValue >>= mxAnimationNode )
            return;
    }
    else if ( rProperty == u"ParentWindow" )
    {
        Reference< XWindow > xWindow;
        if( rValue >>= xWindow )
        {
            mpParentWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow )
                                          : nullptr;
            return;
        }
    }
    else if ( rProperty == u"AllowAnimations" )
    {
        if( rValue >>= mbAnimationAllowed )
            return;
    }
    else if ( rProperty == u"FirstPage" )
    {
        OUString aPresPage;
        if( rValue >>= aPresPage )
        {
            maPresPage = getUiNameFromPageApiNameImpl(aPresPage);
            mbCustomShow = false;
            mbAll = false;
            return;
        }
        else
        {
            if( rValue >>= mxStartPage )
                return;
        }
    }
    else if ( rProperty == u"IsAlwaysOnTop" )
    {
        if( rValue >>= mbAlwaysOnTop )
            return;
    }
    else if ( rProperty == u"IsAutomatic" )
    {
        if( rValue >>= mbManual )
            return;
    }
    else if ( rProperty == u"IsEndless" )
    {
        if( rValue >>= mbEndless )
            return;
    }
    else if ( rProperty == u"IsFullScreen" )
    {
        if( rValue >>= mbFullScreen )
            return;
    }
    else if ( rProperty == u"IsMouseVisible" )
    {
        if( rValue >>= mbMouseVisible )
            return;
    }
    else if ( rProperty == u"Pause" )
    {
        sal_Int32 nPause = -1;
        if( (rValue >>= nPause) && (nPause >= 0) )
        {
            mnPauseTimeout = nPause;
            return;
        }
    }
    else if ( rProperty == u"UsePen" )
    {
        if( rValue >>= mbMouseAsPen )
            return;
    }
    throw IllegalArgumentException();
}

// XAnimationListener

SlideShowListenerProxy::SlideShowListenerProxy( rtl::Reference< SlideshowImpl > xController, css::uno::Reference< css::presentation::XSlideShow > xSlideShow )
: mxController(std::move( xController ))
, mxSlideShow(std::move( xSlideShow ))
{
}

SlideShowListenerProxy::~SlideShowListenerProxy()
{
}

void SlideShowListenerProxy::addAsSlideShowListener()
{
    if( mxSlideShow.is() )
    {
        Reference< XSlideShowListener > xSlideShowListener( this );
        mxSlideShow->addSlideShowListener( xSlideShowListener );
    }
}

void SlideShowListenerProxy::removeAsSlideShowListener()
{
    if( mxSlideShow.is() )
    {
        Reference< XSlideShowListener > xSlideShowListener( this );
        mxSlideShow->removeSlideShowListener( xSlideShowListener );
    }
}

void SlideShowListenerProxy::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape )
{
    if( mxSlideShow.is() )
    {
        Reference< XShapeEventListener > xListener( this );
        mxSlideShow->addShapeEventListener( xListener, xShape );
    }
}

void SlideShowListenerProxy::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape )
{
    if( mxSlideShow.is() )
    {
        Reference< XShapeEventListener > xListener( this );
        mxSlideShow->removeShapeEventListener( xListener, xShape );
    }
}

void SlideShowListenerProxy::addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener )
{
    std::unique_lock g(m_aMutex);
    maListeners.addInterface(g, xListener);
}

void SlideShowListenerProxy::removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener )
{
    std::unique_lock g(m_aMutex);
    maListeners.removeInterface(g, xListener);
}

void SAL_CALL SlideShowListenerProxy::beginEvent( const Reference< XAnimationNode >& xNode )
{
    std::unique_lock aGuard( m_aMutex );

    if( maListeners.getLength(aGuard) >= 0 )
    {
        maListeners.forEach(aGuard,
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->beginEvent(xNode);
            } );
    }
}

void SAL_CALL SlideShowListenerProxy::endEvent( const Reference< XAnimationNode >& xNode )
{
    std::unique_lock aGuard( m_aMutex );

    if( maListeners.getLength(aGuard) >= 0 )
    {
        maListeners.forEach(aGuard,
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->endEvent(xNode);
            } );
    }
}

void SAL_CALL SlideShowListenerProxy::repeat( const Reference< XAnimationNode >& xNode, ::sal_Int32 nRepeat )
{
    std::unique_lock aGuard( m_aMutex );

    if( maListeners.getLength(aGuard) >= 0 )
    {
        maListeners.forEach(aGuard,
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->repeat(xNode, nRepeat);
            } );
    }
}

// css::presentation::XSlideShowListener:

void SAL_CALL SlideShowListenerProxy::paused(  )
{
    std::unique_lock aGuard( m_aMutex );

    maListeners.forEach(aGuard,
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->paused();
        });
}

void SAL_CALL SlideShowListenerProxy::resumed(  )
{
    std::unique_lock aGuard( m_aMutex );

    maListeners.forEach(aGuard,
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->resumed();
        });
}

void SAL_CALL SlideShowListenerProxy::slideTransitionStarted( )
{
    std::unique_lock aGuard( m_aMutex );

    maListeners.forEach(aGuard,
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideTransitionStarted();
        });
}

void SAL_CALL SlideShowListenerProxy::slideTransitionEnded( )
{
    std::unique_lock aGuard( m_aMutex );

    maListeners.forEach(aGuard,
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideTransitionEnded ();
        });
}

void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded(  )
{
    std::unique_lock aGuard( m_aMutex );

    maListeners.forEach(aGuard,
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideAnimationsEnded ();
        });
}

void SlideShowListenerProxy::slideEnded(sal_Bool bReverse)
{
    {
        std::unique_lock aGuard( m_aMutex );

        if( maListeners.getLength(aGuard) >= 0 )
        {
            maListeners.forEach(aGuard,
                [&] (Reference<XSlideShowListener> const& xListener) {
                    return xListener->slideEnded(bReverse);
                } );
        }
    }

    {
        SolarMutexGuard aSolarGuard;
        if( mxController.is() )
            mxController->slideEnded(bReverse);
    }
}

void SlideShowListenerProxy::hyperLinkClicked( OUString const& aHyperLink )
{
    {
        std::unique_lock aGuard( m_aMutex );

        if( maListeners.getLength(aGuard) >= 0 )
        {
            maListeners.forEach(aGuard,
                [&] (Reference<XSlideShowListener> const& xListener) {
                    return xListener->hyperLinkClicked(aHyperLink);
                } );
        }
    }

    {
        SolarMutexGuard aSolarGuard;
        if( mxController.is() )
            mxController->hyperLinkClicked(aHyperLink);
    }
}

// XEventListener

void SAL_CALL SlideShowListenerProxy::disposing( const css::lang::EventObject& aDisposeEvent )
{
    std::unique_lock g(m_aMutex);
    maListeners.disposeAndClear( g, aDisposeEvent );
    mxController.clear();
    mxSlideShow.clear();
}

// XShapeEventListener

void SAL_CALL SlideShowListenerProxy::click( const Reference< XShape >& xShape, const css::awt::MouseEvent& /*aOriginalEvent*/ )
{
    SolarMutexGuard aSolarGuard;
    if( mxController.is() )
        mxController->click(xShape );
}

void SAL_CALL SlideShowListenerProxy::contextMenuShow(const css::awt::Point& point)
{
    SolarMutexGuard aSolarGuard;
    if (mxController.is())
        mxController->contextMenuShow(point);
}

// namespace ::sd

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5 in Prozent
C=94 H=94 G=93

¤ Dauer der Verarbeitung: 0.68 Sekunden  (vorverarbeitet am  2026-04-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.