/* -*- 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 <config_features.h>
 
#include <com/sun/star/frame/theAutoRecovery.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/document/XEventsSupplier.hpp>
#include <com/sun/star/drawing/XMasterPageTarget.hpp>
#include <com/sun/star/container/XNameReplace.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 <o3tl/clamp.hxx>
#include <svl/aeitem.hxx>
#include <svl/urihelper.hxx>
 
#include <toolkit/helper/vclunohelper.hxx>
 
#include <sfx2/infobar.hxx>
#include <sfx2/request.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/app.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdoole2.hxx>
#include <sfx2/templdlg.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 <AnimationChildWindow.hxx>
#include <notifydocumentevent.hxx>
#include "slideshowimpl.hxx"
#include "slideshowviewimpl.hxx"
#include <pgjump.hxx>
#include "PaneHider.hxx"
 
#include <bitmaps.hlst>
#include <vcl/canvastools.hxx>
#include <vcl/commandinfoprovider.hxx>
#include <vcl/settings.hxx>
 
#include <comphelper/anytostring.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <rtl/ref.hxx>
#include <sal/log.hxx>
#include <canvas/elapsedtime.hxx>
#include <avmedia/mediawindow.hxx>
#include <svtools/colrdlg.hxx>
#include <RemoteServer.hxx>
#include <customshowlist.hxx>
#include <unopage.hxx>
#include <sdpage.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::document;
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 */
static 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(); }
 
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) && (nIndex < static_cast<sal_Int32>(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;
};
 
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 & xSlides, Mode eMode  )
:   meMode( eMode )
,   mnStartSlideNumber(-1)
,   mnSlideCount( 0 )
,   mnCurrentSlideIndex(0)
,   mnHiddenSlideNumber( -1 )
,   mxSlides( xSlides )
{
    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& )
    {
        SAL_WARN( "sd", "sd::AnimationSlideController::getSlideAPI(), "
                    "exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
 
    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 ) )
    {
        Reference< XDrawPage > xSlide;
        Reference< XAnimationNode > xAnimNode;
        ::std::vector<PropertyValue> aProperties;
 
        const sal_Int32 nNextSlideNumber = getNextSlideNumber();
        if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode )  )
        {
            Sequence< Any > aValue(2);
            aValue[0] <<= xSlide;
            aValue[1] <<= 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) );
    }
}
 
SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow )
: SlideshowImplBase( m_aMutex )
, mxModel(pDoc->getUnoModel(),UNO_QUERY_THROW)
, mpView(pView)
, mpViewShell(pViewSh)
, mpDocSh(pDoc->GetDocSh())
, mpDoc(pDoc)
, mpParentWindow(pParentWindow)
, mpShowWindow(nullptr)
, mpTimeButton(nullptr)
, mnRestoreSlide(0)
, maPresSize( -1, -1 )
, meAnimationMode(ANIMATIONMODE_SHOW)
, mpOldActiveWindow(nullptr)
, 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 )
, msOnClick( "OnClick" )
, msBookmark( "Bookmark" )
, msVerb( "Verb" )
, mnEndShowEvent(nullptr)
, mnContextMenuEvent(nullptr)
, mxPresentation( xPresentation )
{
    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 );
 
    SvtSaveOptions aOptions;
 
        // no autosave during show
    if( aOptions.IsAutoSave() )
        mbAutoSaveWasOn = true;
 
    Application::AddEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) );
 
    mbUsePen = maPresSettings.mbMouseAsPen;
 
    SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress);
    if( pOptions )
    {
        mnUserPaintColor = pOptions->GetPresentationPenColor();
        mdUserPaintStrokeWidth = pOptions->GetPresentationPenWidth();
    }
}
 
SlideshowImpl::~SlideshowImpl()
{
    SdModule *pModule = SD_MOD();
    //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!");
        disposing();
    }
}
 
void SAL_CALL SlideshowImpl::disposing()
{
#ifdef ENABLE_SDREMOTE
    RemoteServer::presentationStopped();
#endif
    if( mxShow.is() && mpDoc )
        NotifyDocumentEvent(
            *mpDoc,
            "OnEndPresentation" );
 
    if( mbAutoSaveWasOn )
        setAutoSaveState( true );
 
    if( mnEndShowEvent )
        Application::RemoveUserEvent( mnEndShowEvent );
    if( mnContextMenuEvent )
        Application::RemoveUserEvent( mnContextMenuEvent );
 
    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.get() );
 
        Reference< XComponent > xComponent( mxShow, UNO_QUERY );
        if( xComponent.is() )
            xComponent->dispose();
 
        if( mxView.is() )
            mxView->dispose();
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::stop(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
 
    mxShow.clear();
    mxView.clear();
    mxListenerProxy.clear();
    mpSlideController.reset();
 
    // take DrawView from presentation window, but give the old window back
    if( mpShowWindow && mpView )
        mpView->DeleteWindowFromPaintView( mpShowWindow );
 
    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( mpTimeButton )
        mpTimeButton->Hide();
    mpTimeButton.disposeAndClear();
 
    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);
                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::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() );
            for ( sal_Int32 pos = supportedServices.getLength(); pos--; ) {
                if ( supportedServices[pos] == "com.sun.star.drawing.MasterPage" ) {
                    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;
 
        Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW );
        Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
        mpSlideController.reset( new AnimationSlideController( xSlides, AnimationSlideController::PREVIEW ) );
 
        sal_Int32 nSlideNumber = 0;
        Reference< XPropertySet > xSet( mxPreviewDrawPage, UNO_QUERY_THROW );
        xSet->getPropertyValue( "Number" ) >>= 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->AddWindowToPaintView( mpShowWindow, 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);
        aProperties[0].Name = "AutomaticAdvancement";
        aProperties[0].Value <<= 1.0; // one second timeout
 
        if( mxPreviewAnimationNode.is() )
        {
            aProperties[1].Name = "NoSlideTransitions";
            aProperties[1].Value <<= true;
        }
 
        bRet = startShowImpl( aProperties );
 
        if( mpShowWindow != nullptr && meAnimationMode == ANIMATIONMODE_PREVIEW )
            mpShowWindow->SetPreviewMode();
 
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::startPreview(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
        bRet = false;
    }
 
    return bRet;
}
 
bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings )
{
    const rtl::Reference<SlideshowImpl> this_(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()->IsStartWithPresentation()){
        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 nPgNum = ( pStartPage->GetPageNum() - 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 )
            {
                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->AddWindowToPaintView( mpShowWindow, 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(
                SD_MOD()->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[0], aProperties.size() ) );
 
        }
 
        setActiveXToolbarsVisible( false );
    }
    catch (const Exception&)
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::startShow(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
        bRet = false;
    }
 
    return bRet;
}
 
bool SlideshowImpl::startShowImpl( const Sequence< beans::PropertyValue >& aProperties )
{
    try
    {
        mxShow.set( createSlideShow(), UNO_QUERY_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( "WaitSymbolBitmap" ,
                        -1,
                        makeAny( 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( "PointerSymbolBitmap" ,
                        -1,
                        makeAny( xPointerBitmap ),
                        beans::PropertyState_DIRECT_VALUE ) );
            }
        }
 
        const sal_Int32 nCount = aProperties.getLength();
        sal_Int32 nIndex;
        for( nIndex = 0; nIndex < nCount; nIndex++ )
            mxShow->setProperty( aProperties[nIndex] );
 
        mxShow->addView( mxView.get() );
 
        mxListenerProxy.set( new SlideShowListenerProxy( this, mxShow ) );
        mxListenerProxy->addAsSlideShowListener();
 
        NotifyDocumentEvent(
            *mpDoc,
            "OnStartPresentation");
        displaySlideIndex( mpSlideController->getStartSlideIndex() );
 
        return true;
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::startShowImpl(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
        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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::paint(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
void SAL_CALL SlideshowImpl::addSlideShowListener( const Reference< XSlideShowListener >& 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 CommandSwipeData &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 CommandLongPressData &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() ) try
    {
        WrappedShapeEventImplMap::iterator aIter;
        const WrappedShapeEventImplMap::iterator aEnd( maShapeEventMap.end() );
 
        for( aIter = maShapeEventMap.begin(); aIter != aEnd; ++aIter )
        {
            mxListenerProxy->removeShapeEventListener( (*aIter).first );
            mxShow->setShapeCursor( (*aIter).first, awt::SystemPointer::ARROW );
        }
 
        maShapeEventMap.clear();
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::removeShapeEvents(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
void SlideshowImpl::registerShapeEvents(sal_Int32 nSlideNumber)
{
    if( nSlideNumber >= 0 ) try
    {
        Reference< XDrawPagesSupplier > xDrawPages( mxModel, UNO_QUERY_THROW );
        Reference< XIndexAccess > xPages( xDrawPages->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(), UNO_QUERY );
                if( xMasterPage.is() )
                    registerShapeEvents( xMasterPage );
            }
            registerShapeEvents( xDrawPage );
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::registerShapeEvents(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
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( msOnClick ) )
                continue;
 
            WrappedShapeEventImplPtr pEvent( new WrappedShapeEventImpl );
            xSet->getPropertyValue( msOnClick ) >>= 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( msBookmark ) )
                    xSet->getPropertyValue( msBookmark ) >>= pEvent->maStrBookmark;
                if( getSlideNumberForBookmark( pEvent->maStrBookmark ) == -1 )
                    continue;
                break;
            case ClickAction_DOCUMENT:
            case ClickAction_SOUND:
            case ClickAction_PROGRAM:
            case ClickAction_MACRO:
                if( xSetInfo->hasPropertyByName( msBookmark ) )
                    xSet->getPropertyValue( msBookmark ) >>= pEvent->maStrBookmark;
                break;
            case ClickAction_VERB:
                if( xSetInfo->hasPropertyByName( msVerb ) )
                    xSet->getPropertyValue( msVerb ) >>= pEvent->mnVerb;
                break;
            default:
                continue; // skip all others
            }
 
            maShapeEventMap[ xShape ] = pEvent;
 
            if( mxListenerProxy.is() )
                mxListenerProxy->addShapeEventListener( xShape );
            mxShow->setShapeCursor( xShape, awt::SystemPointer::REFHAND );
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::registerShapeEvents(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects)
{
    stopSound();
    removeShapeEvents();
 
    if( mpSlideController.get() && mxShow.is() )
    {
        Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(),
                                                    UNO_QUERY_THROW );
        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)
    {
        Reference< XMultiServiceFactory > xDocFactory(mpDoc->getUnoModel(), UNO_QUERY );
        if( xDocFactory.is() )
            mxShow->registerUserPaintPolygons(xDocFactory);
    }
 
    if( !mnEndShowEvent )
        mnEndShowEvent = Application::PostUserEvent( LINK(this, SlideshowImpl, endPresentationHdl) );
}
 
IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void)
{
    mnEndShowEvent = nullptr;
 
    if( mxPresentation.is() )
        mxPresentation->end();
}
 
void SAL_CALL SlideshowImpl::pause()
{
    SolarMutexGuard aSolarGuard;
 
    if( !mbIsPaused ) try
    {
        mbIsPaused = true;
        if( mxShow.is() )
        {
            mxShow->pause(true);
 
            if( mxListenerProxy.is() )
                mxListenerProxy->paused();
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::pause(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
void SAL_CALL SlideshowImpl::resume()
{
    SolarMutexGuard aSolarGuard;
 
    if( mbIsPaused ) try
    {
        if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK )
        {
            mpShowWindow->RestartShow();
        }
        else
        {
            mbIsPaused = false;
            if( mxShow.is() )
            {
                mxShow->pause(false);
                update();
 
                if( mxListenerProxy.is() )
                    mxListenerProxy->resumed();
            }
        }
    }
    catch( Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::resume(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
#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(nColor) ) )
        {
            pause();
        }
    }
}
 
// XShapeEventListener
 
void SlideshowImpl::click( const Reference< XShape >& xShape )
{
    SolarMutexGuard aSolarGuard;
 
    WrappedShapeEventImplPtr pEvent = maShapeEventMap[xShape];
    if( !pEvent.get() )
        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, ""/*TODO?*/), uno::UNO_QUERY_THROW );
            mxPlayer->start();
        }
        catch( uno::Exception& )
        {
            OSL_FAIL("sd::SlideshowImpl::click(), exception caught!" );
        }
#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 );
 
            SfxViewFrame* pViewFrm = SfxViewFrame::Current();
            if (pViewFrm)
            {
                pViewFrm->GetDispatcher()->ExecuteList( SID_OPENDOC,
                    SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
                    { &aUrl, &aBrowsing });
            }
        }
    }
    break;
 
    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"
            OUString aMacroName = aMacro.getToken(0, '.');
            OUString aModulName = aMacro.getToken(1, '.');
 
            // todo: is the limitation still given that only
            // Modulname+Macroname can be used here?
            OUString aExecMacro = aModulName + "." + aMacroName;
            mpDocSh->GetBasic()->Call(aExecMacro);
        }
    }
    break;
 
    case ClickAction_VERB:
    {
        // todo, better do it async?
        SdrObject* pObj = 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::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.get() )
    {
        if( mpSlideController->jumpToSlideNumber( nSlideNumber ) )
        {
            displayCurrentSlide();
        }
    }
}
 
/** nSlideIndex == -1 displays current slide again */
void SlideshowImpl::displaySlideIndex( sal_Int32 nSlideIndex )
{
    if( mpSlideController.get() )
    {
        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()
{
    return mpSlideController.get() ? 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> this_(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))
            {
                // 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 = o3tl::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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::updateSlideShow(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
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.get() && (ANIMATIONMODE_SHOW == meAnimationMode) )
                {
                    if( mpSlideController->getCurrentSlideNumber() != -1 )
                        mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
                }
                endPresentation();
                break;
 
            // advance show
            case KEY_PAGEDOWN:
                if(rKEvt.GetKeyCode().IsMod2())
                {
                    gotoNextSlide();
                    break;
                }
                SAL_FALLTHROUGH;
            case KEY_SPACE:
            case KEY_RIGHT:
            case KEY_DOWN:
            case KEY_N:
                gotoNextEffect();
                break;
 
            case KEY_RETURN:
            {
                if( !maCharBuffer.isEmpty() )
                {
                    if( mpSlideController.get() )
                    {
                        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 += OUStringLiteral1( rKEvt.GetCharCode() );
                break;
 
            case KEY_PAGEUP:
                if(rKEvt.GetKeyCode().IsMod2())
                {
                    gotoPreviousSlide();
                    break;
                }
                SAL_FALLTHROUGH;
            case KEY_LEFT:
            case KEY_UP:
            case KEY_P:
            case KEY_BACKSPACE:
                gotoPreviousEffect();
                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;
        SAL_WARN( "sd", "sd::SlideshowImpl::keyInput(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
 
    return bRet;
}
 
IMPL_LINK( SlideshowImpl, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void )
{
    if( !mxShow.is() || mbInputFreeze )
        return;
 
    if( (rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData() )
    {
        const CommandEvent& rEvent = *static_cast<const CommandEvent*>(static_cast<VclWindowEvent*>(&rSimpleEvent)->GetData());
 
        if( rEvent.GetCommand() == CommandEventId::Media )
        {
            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.get() && (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.get() == nullptr )
        return;
 
    mbWasPaused = mbIsPaused;
    if( !mbWasPaused )
        pause();
 
    VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/simpress/ui/slidecontextmenu.ui", "");
    VclPtr<PopupMenu> pMenu(aBuilder.get_menu("menu"));
 
    // Adding button to display if in Pen  mode
    pMenu->CheckItem("pen", mbUsePen);
 
    const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
    pMenu->EnableItem(pMenu->GetItemId("next"), mpSlideController->getNextSlideIndex() != -1);
    pMenu->EnableItem(pMenu->GetItemId("prev"), (mpSlideController->getPreviousSlideIndex() != -1 ) || (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK));
    pMenu->EnableItem(pMenu->GetItemId("edit"), mpViewShell->GetDoc()->IsStartWithPresentation());
 
    PopupMenu* pPageMenu = pMenu->GetPopupMenu(pMenu->GetItemId("goto"));
 
    SfxViewFrame* pViewFrame = getViewFrame();
    if( pViewFrame )
    {
        Reference< css::frame::XFrame > xFrame( pViewFrame->GetFrame().GetFrameInterface() );
        if( xFrame.is() )
        {
            pMenu->SetItemImage(pMenu->GetItemId("next"), vcl::CommandInfoProvider::GetImageForCommand(".uno:NextRecord", xFrame));
            pMenu->SetItemImage(pMenu->GetItemId("prev"), vcl::CommandInfoProvider::GetImageForCommand(".uno:PrevRecord", xFrame));
 
            if( pPageMenu )
            {
                pPageMenu->SetItemImage(pPageMenu->GetItemId("first"), vcl::CommandInfoProvider::GetImageForCommand(".uno:FirstRecord", xFrame));
                pPageMenu->SetItemImage(pPageMenu->GetItemId("last"), vcl::CommandInfoProvider::GetImageForCommand(".uno:LastRecord", xFrame));
            }
        }
    }
 
    // populate slide goto list
    if( pPageMenu )
    {
        const sal_Int32 nPageNumberCount = mpSlideController->getSlideNumberCount();
        if( nPageNumberCount <= 1 )
        {
            pMenu->EnableItem(pMenu->GetItemId("goto"), false);
        }
        else
        {
            sal_Int32 nCurrentSlideNumber = mpSlideController->getCurrentSlideNumber();
            if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
                nCurrentSlideNumber = -1;
 
            pPageMenu->EnableItem(pPageMenu->GetItemId("first"), mpSlideController->getSlideNumber(0) != nCurrentSlideNumber);
            pPageMenu->EnableItem(pPageMenu->GetItemId("last"), 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)
                    {
                        pPageMenu->InsertItem( static_cast<sal_uInt16>(CM_SLIDES + nPageNumber), pPage->GetName() );
                        if( nPageNumber == nCurrentSlideNumber )
                            pPageMenu->CheckItem( static_cast<sal_uInt16>(CM_SLIDES + nPageNumber) );
                    }
                }
            }
        }
    }
 
    if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK )
    {
        PopupMenu* pBlankMenu = pMenu->GetPopupMenu(pMenu->GetItemId("screen"));
        if( pBlankMenu )
        {
            pBlankMenu->CheckItem((mpShowWindow->GetBlankColor() == COL_WHITE) ? "white" : "black");
        }
    }
 
    PopupMenu* pWidthMenu = pMenu->GetPopupMenu(pMenu->GetItemId("width"));
 
    // populate color width list
    if( pWidthMenu )
    {
        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)
                pWidthMenu->CheckItem(OString::number(nWidth));
        }
    }
 
    pMenu->SetSelectHdl( LINK( this, SlideshowImpl, ContextMenuSelectHdl ) );
    pMenu->Execute( mpShowWindow, maPopupMousePos );
 
    if( mxView.is() )
        mxView->ignoreNextMouseReleased();
 
    if( !mbWasPaused )
        resume();
}
 
IMPL_LINK( SlideshowImpl, ContextMenuSelectHdl, Menu *, pMenu, bool )
{
    if (!pMenu)
        return false;
 
    OString sMenuId = pMenu->GetCurItemIdent();
 
    if (sMenuId == "prev")
    {
        gotoPreviousSlide();
        mbWasPaused = false;
    }
    else if(sMenuId == "next")
    {
        gotoNextSlide();
        mbWasPaused = false;
    }
    else if (sMenuId == "first")
    {
        gotoFirstSlide();
        mbWasPaused = false;
    }
    else if (sMenuId == "last")
    {
        gotoLastSlide();
        mbWasPaused = false;
    }
    else if (sMenuId == "black" || sMenuId == "white")
    {
        const Color aBlankColor(sMenuId == "white" ? COL_WHITE : COL_BLACK);
        if( mbWasPaused )
        {
            if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK )
            {
                if( mpShowWindow->GetBlankColor() == aBlankColor )
                {
                    mbWasPaused = false;
                    mpShowWindow->RestartShow();
                    return false;
                }
            }
            mpShowWindow->RestartShow();
        }
        if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), aBlankColor ) )
        {
            pause();
            mbWasPaused = true;
        }
    }
    else if (sMenuId == "color")
    {
        //Open a color picker based on SvColorDialog
        ::Color aColor( mnUserPaintColor );
        SvColorDialog aColorDlg;
        aColorDlg.SetColor( aColor );
 
        if (aColorDlg.Execute(mpShowWindow->GetFrameWeld()))
        {
            aColor = aColorDlg.GetColor();
            setPenColor(sal_Int32(aColor));
        }
        mbWasPaused = false;
    }
    else if (sMenuId == "4")
    {
        setPenWidth(4.0);
        mbWasPaused = false;
    }
    else if (sMenuId == "100")
    {
        setPenWidth(100.0);
        mbWasPaused = false;
    }
    else if (sMenuId == "150")
    {
        setPenWidth(150.0);
        mbWasPaused = false;
    }
    else if (sMenuId == "200")
    {
        setPenWidth(200.0);
        mbWasPaused = false;
    }
    else if (sMenuId == "400")
    {
        setPenWidth(400.0);
        mbWasPaused = false;
    }
    else if (sMenuId == "erase")
    {
        setEraseAllInk(true);
            mbWasPaused = false;
    }
    else if (sMenuId == "pen")
    {
        setUsePen(!mbUsePen);
        mbWasPaused = false;
    }
    else if (sMenuId == "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.get() && (ANIMATIONMODE_SHOW == meAnimationMode) )
        {
            if( mpSlideController->getCurrentSlideNumber() != -1 )
            {
                mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
            }
        }
        endPresentation();
    }
    else if (sMenuId == "end")
    {
        // in case the user cancels the presentation, switch to current slide
        // in edit mode
        if( mpSlideController.get() && (ANIMATIONMODE_SHOW == meAnimationMode) )
        {
            if( mpSlideController->getCurrentSlideNumber() != -1 )
            {
                mnRestoreSlide = mpSlideController->getCurrentSlideNumber();
            }
        }
        endPresentation();
    }
    else
    {
        sal_Int32 nPageNumber = pMenu->GetCurItemId() - 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;
    }
 
    return false;
}
 
Reference< XSlideShow > SlideshowImpl::createSlideShow()
{
    Reference< XSlideShow > xShow;
 
    try
    {
        Reference< uno::XComponentContext > xContext =
            ::comphelper::getProcessComponentContext();
 
        xShow.set( presentation::SlideShow::create(xContext), UNO_QUERY_THROW );
    }
    catch( uno::Exception& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::createSlideShow(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
 
    return xShow;
}
 
void SlideshowImpl::createSlideList( bool bAll, const OUString& rPresSlide )
{
    const sal_uInt16 nSlideCount = mpDoc->GetSdPageCount( PageKind::Standard );
 
    if( nSlideCount )
    {
        SdCustomShow*   pCustomShow;
 
        if( mpDoc->GetCustomShowList() && maPresSettings.mbCustomShow )
            pCustomShow = mpDoc->GetCustomShowList()->GetCurObject();
        else
            pCustomShow = nullptr;
 
        // create animation slide controller
        AnimationSlideController::Mode eMode =
            ( pCustomShow && pCustomShow->PagesVector().size() ) ? AnimationSlideController::CUSTOM :
                (bAll ? AnimationSlideController::ALL : AnimationSlideController::FROM);
 
        Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW );
        Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW );
        mpSlideController.reset( new AnimationSlideController( xSlides, eMode ) );
 
        if( eMode != AnimationSlideController::CUSTOM )
        {
            sal_Int32 nFirstVisibleSlide = 0;
 
            // normal presentation
            if( !rPresSlide.isEmpty() )
            {
                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.isEmpty() )
            {
                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) );
            }
 
            sal_Int32 nSlideIndex = 0;
            for( SdCustomShow::PageVec::iterator it = pCustomShow->PagesVector().begin();
                 it != pCustomShow->PagesVector().end(); ++it, nSlideIndex++ )
            {
                const sal_uInt16 nSdSlide = ( (*it)->GetPageNum() - 1 ) / 2;
 
                if( ! mpDoc->GetSdPage( nSdSlide, PageKind::Standard )->IsExcluded())
                    mpSlideController->insertSlideNumber( nSdSlide );
            }
        }
    }
}
 
typedef sal_uInt16 (*FncGetChildWindowId)();
 
static 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 )
    {
        SfxViewFrame* pViewFrame = getViewFrame();
 
        if( pViewFrame )
        {
            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 |= 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 & ( 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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::resize(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
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() )
    {
        const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(mpDocSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false);
        if ( pItem && pItem->GetValue() )
        {
            // 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 )
            {
                try
                {
                    Reference< frame::XLayoutManager > xLayoutManager;
                    Reference< beans::XPropertySet > xFrameProps( pViewFrame->GetFrame().GetFrameInterface(), UNO_QUERY_THROW );
                    if ( ( xFrameProps->getPropertyValue( "LayoutManager" )
                                >>= xLayoutManager )
                      && xLayoutManager.is() )
                    {
                        xLayoutManager->setVisible( bVisible );
                    }
                }
                catch( uno::Exception& )
                {}
            }
        }
    }
}
 
void SAL_CALL SlideshowImpl::activate()
{
    SolarMutexGuard aSolarGuard;
 
    maDeactivateTimer.Stop();
 
    if( !mbActive && mxShow.is() )
    {
        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() )
    {
        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
    {
        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(1);
        aArgs[0].Name = "AutoSaveState";
        aArgs[0].Value <<= bOn;
 
        uno::Reference< frame::XDispatch > xAutoSave = frame::theAutoRecovery::get(xContext);
        xAutoSave->dispatch(aURL, aArgs);
    }
    catch( Exception& )
    {
        OSL_FAIL("sd::SlideshowImpl::setAutoSaveState(), exception caught!");
    }
}
 
Reference< XDrawPage > SAL_CALL SlideshowImpl::getCurrentSlide()
{
    SolarMutexGuard aSolarGuard;
 
    Reference< XDrawPage > xSlide;
    if( mxShow.is() && mpSlideController.get() )
    {
        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.get() ? mpSlideController->getCurrentSlideIndex() : -1;
}
 
// css::presentation::XSlideShowController:
 
::sal_Int32 SAL_CALL SlideshowImpl::getSlideCount()
{
    return mpSlideController.get() ? mpSlideController->getSlideIndexCount() : 0;
}
 
Reference< XDrawPage > SAL_CALL SlideshowImpl::getSlideByIndex(::sal_Int32 Index)
{
    if( (mpSlideController.get() == 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() ) try
    {
        // For Pencolor;
        Any aValue;
        if( mbUsePen )
            aValue <<= mnUserPaintColor;
        beans::PropertyValue aPenProp;
        aPenProp.Name = "UserPaintColor";
        aPenProp.Value = aValue;
        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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::setUsePen(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
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 SlideshowImpl::setEraseAllInk(bool bEraseAllInk)
{
    if( bEraseAllInk )
    {
        SolarMutexGuard aSolarGuard;
        if( mxShow.is() ) try
        {
            beans::PropertyValue aPenPropEraseAllInk;
            aPenPropEraseAllInk.Name = "EraseAllInk";
            aPenPropEraseAllInk.Value <<= bEraseAllInk;
            mxShow->setProperty( aPenPropEraseAllInk );
        }
        catch( Exception& )
        {
            SAL_WARN( "sd.slideshow", "sd::SlideshowImpl::setEraseAllInk(), "
                "exception caught: " << comphelper::anyToString( cppu::getCaughtException() ));
        }
    }
}
 
// XSlideShowController Methods
sal_Bool SAL_CALL SlideshowImpl::isRunning(  )
{
    SolarMutexGuard aSolarGuard;
    return mxShow.is();
}
 
void SAL_CALL SlideshowImpl::gotoNextEffect(  )
{
    SolarMutexGuard aSolarGuard;
 
    if( mxShow.is() && mpSlideController.get() && mpShowWindow )
    {
        if( mbIsPaused )
            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.get() && mpShowWindow )
    {
        if( mbIsPaused )
            resume();
 
        const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode();
        if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) )
        {
            mpShowWindow->RestartShow();
        }
        else
        {
            mxShow->previousEffect();
            update();
        }
    }
}
 
void SAL_CALL SlideshowImpl::gotoFirstSlide(  )
{
    SolarMutexGuard aSolarGuard;
 
    if( mpShowWindow && mpSlideController.get() )
    {
        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.get() )
        {
            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()->IsStartWithPresentation() )
                            pause();
                    }
                }
            }
        }
    }
}
 
void SAL_CALL SlideshowImpl::gotoPreviousSlide(  )
{
    gotoPreviousSlide(false);
}
 
void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects)
{
    SolarMutexGuard aSolarGuard;
 
    if( mxShow.is() && mpSlideController.get() ) 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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::gotoPreviousSlide(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
void SAL_CALL SlideshowImpl::gotoLastSlide()
{
    SolarMutexGuard aSolarGuard;
 
    if( mpSlideController.get() )
    {
        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.get() && xSlide.is() )
    {
        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& )
    {
        SAL_WARN( "sd", "sd::SlideshowImpl::stopSound(), exception caught: "
                    << comphelper::anyToString( cppu::getCaughtException() ) );
    }
}
 
// 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;
}
 
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 )
{
    sal_Int32 nArguments = rArguments.getLength();
    const PropertyValue* pValue = rArguments.getConstArray();
 
    while( nArguments-- )
    {
        SetPropertyValue( pValue->Name, pValue->Value );
        pValue++;
    }
}
 
void PresentationSettingsEx::SetPropertyValue( const OUString& rProperty, const Any& rValue )
{
    if ( rProperty == "RehearseTimings" )
    {
        if( rValue >>= mbRehearseTimings )
            return;
    }
    else if ( rProperty == "Preview" )
    {
        if( rValue >>= mbPreview )
            return;
    }
    else if ( rProperty == "AnimationNode" )
    {
        if( rValue >>= mxAnimationNode )
            return;
    }
    else if ( rProperty == "ParentWindow" )
    {
        Reference< XWindow > xWindow;
        if( rValue >>= xWindow )
        {
            mpParentWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow )
                                          : VclPtr<vcl::Window>();
            return;
        }
    }
    else if ( rProperty == "AllowAnimations" )
    {
        if( rValue >>= mbAnimationAllowed )
            return;
    }
    else if ( rProperty == "FirstPage" )
    {
        OUString aPresPage;
        if( rValue >>= aPresPage )
        {
            maPresPage = getUiNameFromPageApiNameImpl(aPresPage);
            mbCustomShow = false;
            mbAll = false;
            return;
        }
        else
        {
            if( rValue >>= mxStartPage )
                return;
        }
    }
    else if ( rProperty == "IsAlwaysOnTop" )
    {
        if( rValue >>= mbAlwaysOnTop )
            return;
    }
    else if ( rProperty == "IsAutomatic" )
    {
        if( rValue >>= mbManual )
            return;
    }
    else if ( rProperty == "IsEndless" )
    {
        if( rValue >>= mbEndless )
            return;
    }
    else if ( rProperty == "IsFullScreen" )
    {
        if( rValue >>= mbFullScreen )
            return;
    }
    else if ( rProperty == "IsMouseVisible" )
    {
        if( rValue >>= mbMouseVisible )
            return;
    }
    else if ( rProperty == "Pause" )
    {
        sal_Int32 nPause = -1;
        if( (rValue >>= nPause) && (nPause >= 0) )
        {
            mnPauseTimeout = nPause;
            return;
        }
    }
    else if ( rProperty == "UsePen" )
    {
        if( rValue >>= mbMouseAsPen )
            return;
    }
    throw IllegalArgumentException();
}
 
// XAnimationListener
 
SlideShowListenerProxy::SlideShowListenerProxy( const rtl::Reference< SlideshowImpl >& xController, const css::uno::Reference< css::presentation::XSlideShow >& xSlideShow )
: maListeners( m_aMutex )
, mxController( xController )
, mxSlideShow( 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 )
{
    maListeners.addInterface(xListener);
}
 
void SlideShowListenerProxy::removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener )
{
    maListeners.removeInterface(xListener);
}
 
void SAL_CALL SlideShowListenerProxy::beginEvent( const Reference< XAnimationNode >& xNode )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    if( maListeners.getLength() >= 0 )
    {
        maListeners.forEach<XSlideShowListener>(
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->beginEvent(xNode);
            } );
    }
}
 
void SAL_CALL SlideShowListenerProxy::endEvent( const Reference< XAnimationNode >& xNode )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    if( maListeners.getLength() >= 0 )
    {
        maListeners.forEach<XSlideShowListener>(
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->endEvent(xNode);
            } );
    }
}
 
void SAL_CALL SlideShowListenerProxy::repeat( const Reference< XAnimationNode >& xNode, ::sal_Int32 nRepeat )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    if( maListeners.getLength() >= 0 )
    {
        maListeners.forEach<XSlideShowListener>(
            [&] (Reference<XAnimationListener> const& xListener) {
                return xListener->repeat(xNode, nRepeat);
            } );
    }
}
 
// css::presentation::XSlideShowListener:
 
void SAL_CALL SlideShowListenerProxy::paused(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    maListeners.forEach<XSlideShowListener>(
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->paused();
        });
}
 
void SAL_CALL SlideShowListenerProxy::resumed(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    maListeners.forEach<XSlideShowListener>(
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->resumed();
        });
}
 
void SAL_CALL SlideShowListenerProxy::slideTransitionStarted( )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    maListeners.forEach<XSlideShowListener>(
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideTransitionStarted();
        });
}
 
void SAL_CALL SlideShowListenerProxy::slideTransitionEnded( )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    maListeners.forEach<XSlideShowListener>(
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideTransitionEnded ();
        });
}
 
void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
 
    maListeners.forEach<XSlideShowListener>(
        [](uno::Reference<presentation::XSlideShowListener> const& xListener)
        {
            xListener->slideAnimationsEnded ();
        });
}
 
void SlideShowListenerProxy::slideEnded(sal_Bool bReverse)
{
    {
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if( maListeners.getLength() >= 0 )
        {
            maListeners.forEach<XSlideShowListener>(
                [&] (Reference<XSlideShowListener> const& xListener) {
                    return xListener->slideEnded(bReverse);
                } );
        }
    }
 
    {
        SolarMutexGuard aSolarGuard;
        if( mxController.is() )
            mxController->slideEnded(bReverse);
    }
}
 
void SlideShowListenerProxy::hyperLinkClicked( OUString const& aHyperLink )
{
    {
        ::osl::MutexGuard aGuard( m_aMutex );
 
        if( maListeners.getLength() >= 0 )
        {
            maListeners.forEach<XSlideShowListener>(
                [&] (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 )
{
    maListeners.disposeAndClear( 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 );
}
 
} // namespace ::sd
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V595 The 'pStartPage' pointer was utilized before it was verified against nullptr. Check lines: 896, 907.

V773 Visibility scope of the 'pInArgs' pointer was exited without releasing the memory. A memory leak is possible.