Update SlideShow on DrawModel changes II

Change-Id: Idfbbb7744cc7b2182647b45f509399535a2d4df2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159930
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx
index 97216f9..7619624 100644
--- a/sd/source/ui/slideshow/slideshowimpl.cxx
+++ b/sd/source/ui/slideshow/slideshowimpl.cxx
@@ -517,6 +517,8 @@ SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, 
, mdUserPaintStrokeWidth ( 150.0 )
, mnEndShowEvent(nullptr)
, mnContextMenuEvent(nullptr)
, mnEventObjectChange(nullptr)
, mnEventPageOrderChange(nullptr)
, mxPresentation( xPresentation )
{
    if( mpViewShell )
@@ -598,6 +600,10 @@ void SlideshowImpl::disposing(std::unique_lock<std::mutex>&)
        Application::RemoveUserEvent( mnEndShowEvent );
    if( mnContextMenuEvent )
        Application::RemoveUserEvent( mnContextMenuEvent );
    if( mnEventObjectChange )
        Application::RemoveUserEvent( mnEventObjectChange );
    if( mnEventPageOrderChange )
        Application::RemoveUserEvent( mnEventPageOrderChange );

    maInputFreezeTimer.Stop();

@@ -3094,16 +3100,19 @@ namespace
        {
            SlideshowImpl* pSlideshowImpl;
            uno::Reference< css::drawing::XDrawPage > XCurrentSlide;
            SdrHintKind eHintKind;
        };

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

@@ -3113,11 +3122,64 @@ namespace
    IMPL_STATIC_LINK(AsyncUpdateSlideshow_Impl, Update, void*, pData, void)
    {
        AsyncUpdateSlideshowData* pSlideData(static_cast<AsyncUpdateSlideshowData*>(pData));
        pSlideData->pSlideshowImpl->gotoSlide(pSlideData->XCurrentSlide);
        pSlideData->pSlideshowImpl->AsyncNotifyEvent(pSlideData->XCurrentSlide, pSlideData->eHintKind);
        delete pSlideData;
    }
}

void SlideshowImpl::AsyncNotifyEvent(
    const uno::Reference< css::drawing::XDrawPage >& rXCurrentSlide,
    const SdrHintKind eHintKind)
{
    if (SdrHintKind::ObjectChange == eHintKind)
    {
        mnEventObjectChange = nullptr;

        // refresh single slide
        gotoSlide(rXCurrentSlide);
    }
    else if (SdrHintKind::PageOrderChange == eHintKind)
    {
        mnEventPageOrderChange = nullptr;

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

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

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

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

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

void SlideshowImpl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
{
    if (SfxHintId::ThisIsAnSdrHint != rHint.GetId())
@@ -3132,6 +3194,10 @@ void SlideshowImpl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)

    if (SdrHintKind::ObjectChange == eHintKind)
    {
        if (nullptr != mnEventObjectChange)
            // avoid multiple events
            return;

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

        // order of pages (object pages or master pages) changed (Insert/Remove/ChangePos)
        // probably needs refresh of AnimationSlideController in mpSlideController
        gotoFirstSlide();
        uno::Reference< css::drawing::XDrawPage > XCurrentSlide(getCurrentSlide());
        mnEventPageOrderChange = AsyncUpdateSlideshow_Impl::AsyncUpdateSlideshow(this, XCurrentSlide, eHintKind);
    }
    else if (SdrHintKind::ModelCleared == eHintKind)
    {
        // immediately end presentation
        endPresentation();
    }

    // maybe need to add reactions here to other Hint-Types
diff --git a/sd/source/ui/slideshow/slideshowimpl.hxx b/sd/source/ui/slideshow/slideshowimpl.hxx
index b235a78..9c0ae75 100644
--- a/sd/source/ui/slideshow/slideshowimpl.hxx
+++ b/sd/source/ui/slideshow/slideshowimpl.hxx
@@ -198,6 +198,10 @@ public:
    /// ends the presentation async
    void endPresentation();

    // possibly triggered from events @SlideshowImpl::Notify if needed, but asychron to
    // allow the noted event to completely finish in the core
    void AsyncNotifyEvent(const css::uno::Reference< css::drawing::XDrawPage >&, const SdrHintKind);

    ViewShell* getViewShell() const { return mpViewShell; }

    void paint();
@@ -339,6 +343,8 @@ private:

    ImplSVEvent * mnEndShowEvent;
    ImplSVEvent * mnContextMenuEvent;
    ImplSVEvent * mnEventObjectChange;
    ImplSVEvent * mnEventPageOrderChange;

    css::uno::Reference< css::presentation::XPresentation2 > mxPresentation;
    ::rtl::Reference< SlideShowListenerProxy > mxListenerProxy;