tdf#38164: sd: allow panning across pages when zoomed in

Change-Id: I513b2b8cbdc91733e551da71a1e6782fecc981a3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166542
Tested-by: Jenkins
Reviewed-by: Sarper Akdemir <sarper@libreoffice.org>
diff --git a/sd/source/ui/inc/ViewShell.hxx b/sd/source/ui/inc/ViewShell.hxx
index 6ee126d..9e5e8e0 100644
--- a/sd/source/ui/inc/ViewShell.hxx
+++ b/sd/source/ui/inc/ViewShell.hxx
@@ -382,6 +382,7 @@ public:
    */
    virtual void ShowUIControls (bool bVisible);
    bool IsPageFlipMode() const;
    bool CanPanAcrossPages() const;

    /** Set the given window as new parent window.  This is not possible for
        all views, so the return value tells the caller if the relocation
diff --git a/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx
index 9203c06..e8fc847 100644
--- a/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx
+++ b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx
@@ -176,6 +176,7 @@ void CurrentSlideManager::SetCurrentSlideAtViewShellBase (const SharedPageDescri
            pDrawViewShell->SwitchPage(nPageNumber);
            TabControl& rPageTabControl = pDrawViewShell->GetPageTabControl();
            rPageTabControl.SetCurPageId(rPageTabControl.GetPageId(nPageNumber));
            pDrawViewShell->UpdateScrollBars();
        }
    }
}
diff --git a/sd/source/ui/view/drawview.cxx b/sd/source/ui/view/drawview.cxx
index 6792250..99c1660 100644
--- a/sd/source/ui/view/drawview.cxx
+++ b/sd/source/ui/view/drawview.cxx
@@ -407,6 +407,7 @@ void DrawView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
        if ( mnPOCHSmph == 0 && eHintKind == SdrHintKind::PageOrderChange )
        {
            mpDrawViewShell->ResetActualPage();
            mpDrawViewShell->UpdateScrollBars();
        }
        else if ( eHintKind == SdrHintKind::LayerChange || eHintKind == SdrHintKind::LayerOrderChange )
        {
diff --git a/sd/source/ui/view/sdwindow.cxx b/sd/source/ui/view/sdwindow.cxx
index ab8a7bb..8f71c17 100644
--- a/sd/source/ui/view/sdwindow.cxx
+++ b/sd/source/ui/view/sdwindow.cxx
@@ -667,8 +667,6 @@ void Window::SetVisibleXY(double fX, double fY)
double Window::GetVisibleWidth() const
{
    Size aWinSize = PixelToLogic(GetOutputSizePixel());
    if ( aWinSize.Width() > maViewSize.Width() )
        aWinSize.setWidth( maViewSize.Width() );
    return
        maViewSize.Width() == 0 ? 0 : (static_cast<double>(aWinSize.Width()) / maViewSize.Width());
}
@@ -680,8 +678,6 @@ double Window::GetVisibleWidth() const
double Window::GetVisibleHeight() const
{
    Size aWinSize = PixelToLogic(GetOutputSizePixel());
    if ( aWinSize.Height() > maViewSize.Height() )
        aWinSize.setHeight( maViewSize.Height() );
    return maViewSize.Height() == 0
        ? 0 : (static_cast<double>(aWinSize.Height()) / maViewSize.Height());
}
@@ -705,7 +701,7 @@ Point Window::GetVisibleCenter()
 */
double Window::GetScrlLineWidth() const
{
    return (GetVisibleWidth() * SCROLL_LINE_FACT);
    return std::min(1.0, GetVisibleWidth()) * SCROLL_LINE_FACT;
}

/**
@@ -714,7 +710,7 @@ double Window::GetScrlLineWidth() const
 */
double Window::GetScrlLineHeight() const
{
    return (GetVisibleHeight() * SCROLL_LINE_FACT);
    return std::min(1.0, GetVisibleHeight()) * SCROLL_LINE_FACT;
}

/**
@@ -723,7 +719,7 @@ double Window::GetScrlLineHeight() const
 */
double Window::GetScrlPageWidth() const
{
    return (GetVisibleWidth() * SCROLL_PAGE_FACT);
    return std::min(1.0, GetVisibleWidth()) * SCROLL_PAGE_FACT;
}

/**
@@ -732,7 +728,7 @@ double Window::GetScrlPageWidth() const
 */
double Window::GetScrlPageHeight() const
{
    return (GetVisibleHeight() * SCROLL_PAGE_FACT);
    return std::min(1.0, GetVisibleHeight()) * SCROLL_PAGE_FACT;
}

/**
diff --git a/sd/source/ui/view/viewshe2.cxx b/sd/source/ui/view/viewshe2.cxx
index b7ae44f..18658fc 100644
--- a/sd/source/ui/view/viewshe2.cxx
+++ b/sd/source/ui/view/viewshe2.cxx
@@ -63,6 +63,19 @@

using namespace com::sun::star;

namespace
{
inline double getViewToScrollScalarForPanAcrossPages(sal_uInt16 nTotalPages, double fVisibleHeight,
                                                     ::tools::Long nScrollRangeMax)
{
    // fTotalScrollableRange is (1 - fVisibleHeight) for all of the
    // pages except the last one. Because switch to the next page
    // happens when the view reaches bottom.
    double fTotalScrollableRange = (nTotalPages - 1) * (1 - fVisibleHeight) + 1.0;
    return nScrollRangeMax / fTotalScrollableRange;
}
}

namespace sd {

/**
@@ -72,7 +85,7 @@ void ViewShell::UpdateScrollBars()
{
    if (mpHorizontalScrollBar)
    {
        ::tools::Long nW = static_cast<::tools::Long>(mpContentWindow->GetVisibleWidth() * 32000);
        ::tools::Long nW = static_cast<::tools::Long>(std::min(1.0, mpContentWindow->GetVisibleWidth()) * 32000);
        ::tools::Long nX = static_cast<::tools::Long>(mpContentWindow->GetVisibleX() * 32000);
        mpHorizontalScrollBar->SetVisibleSize(nW);
        mpHorizontalScrollBar->SetThumbPos(nX);
@@ -85,10 +98,32 @@ void ViewShell::UpdateScrollBars()

    if (mpVerticalScrollBar)
    {
        ::tools::Long nH = static_cast<::tools::Long>(mpContentWindow->GetVisibleHeight() * 32000);
        ::tools::Long nY = static_cast<::tools::Long>(mpContentWindow->GetVisibleY() * 32000);
        if (CanPanAcrossPages())
        {
            SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage();
            sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) / 2;
            sal_uInt16 nTotalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind());

        if(IsPageFlipMode()) // ie in zoom mode where no panning
            // nRangeMax is max int, and not ::tools::Long since the underlying
            // implementation weld::Scrollbar uses int
            ::tools::Long nRangeMax = std::numeric_limits<int>::max();
            double fVisibleHeight = std::min(mpContentWindow->GetVisibleHeight(), 1.0);
            double fMappingFactor
                = getViewToScrollScalarForPanAcrossPages(nTotalPages, fVisibleHeight, nRangeMax);
            double fVisibleY = std::max(0.0, mpContentWindow->GetVisibleY());
            double fCurrentThumbPos = nCurPage * (1 - fVisibleHeight) + fVisibleY;
            double fScrollLineHeight
                = mpContentWindow->GetScrlLineHeight() * (1.0 - fVisibleHeight);
            double fScrollPageHeight
                = mpContentWindow->GetScrlPageHeight() * (1.0 - fVisibleHeight);

            mpVerticalScrollBar->SetRange(Range(0, nRangeMax));
            mpVerticalScrollBar->SetVisibleSize(fVisibleHeight * fMappingFactor);
            mpVerticalScrollBar->SetThumbPos(fCurrentThumbPos * fMappingFactor);
            mpVerticalScrollBar->SetLineSize(fScrollLineHeight * fMappingFactor);
            mpVerticalScrollBar->SetPageSize(fScrollPageHeight * fMappingFactor);
        }
        else if (IsPageFlipMode()) // ie in zoom mode where no panning
        {
            SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage();
            sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) / 2;
@@ -99,8 +134,11 @@ void ViewShell::UpdateScrollBars()
            mpVerticalScrollBar->SetLineSize(256);
            mpVerticalScrollBar->SetPageSize(256);
        }
        else
        else // single page pan mode
        {
            ::tools::Long nH = static_cast<::tools::Long>(std::min(1.0, mpContentWindow->GetVisibleHeight()) * 32000);
            ::tools::Long nY = static_cast<::tools::Long>(mpContentWindow->GetVisibleY() * 32000);

            mpVerticalScrollBar->SetRange(Range(0,32000));
            mpVerticalScrollBar->SetVisibleSize(nH);
            mpVerticalScrollBar->SetThumbPos(nY);
@@ -180,18 +218,8 @@ IMPL_LINK_NOARG(ViewShell, VScrollHdl, weld::Scrollbar&, void)
 */
void ViewShell::VirtVScrollHdl(ScrollAdaptor* pVScroll)
{
    if(IsPageFlipMode())
    auto doScrollView = [&](double fY)
    {
        SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage();
        sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) >> 1;
        sal_uInt16 nNewPage = static_cast<sal_uInt16>(pVScroll->GetThumbPos())/256;
        if( nCurPage != nNewPage )
            static_cast<DrawViewShell*>(this)->SwitchPage(nNewPage);
    }
    else //panning mode
    {
        double fY = static_cast<double>(pVScroll->GetThumbPos()) / pVScroll->GetRange().Len();

        ::sd::View* pView = GetView();
        OutlinerView* pOLV = nullptr;

@@ -222,7 +250,44 @@ void ViewShell::VirtVScrollHdl(ScrollAdaptor* pVScroll)

        if (mbHasRulers)
            UpdateVRuler();
    };

    if (CanPanAcrossPages())
    {
        SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage();
        sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) >> 1;
        sal_uInt16 nTotalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind());

        double fVisibleHeight = mpContentWindow->GetVisibleHeight();
        double fMappingFactor = getViewToScrollScalarForPanAcrossPages(nTotalPages, fVisibleHeight,
                                                                       pVScroll->GetRange().Max());

        double fScrollableDistancePerPage = 1 - std::min(fVisibleHeight, 1.0);

        sal_uInt16 nNewPage
            = std::min((pVScroll->GetThumbPos() / fMappingFactor) / fScrollableDistancePerPage,
                       static_cast<double>(nTotalPages - 1));

        if (nCurPage != nNewPage)
            static_cast<DrawViewShell*>(this)->SwitchPage(nNewPage);

        double fNewPageStart = nNewPage * fScrollableDistancePerPage;
        double fY = (pVScroll->GetThumbPos() / fMappingFactor) - fNewPageStart;

        doScrollView(fY);
    }
    else if (IsPageFlipMode())
    {
        SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage();
        sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) >> 1;
        sal_uInt16 nNewPage = static_cast<sal_uInt16>(pVScroll->GetThumbPos())/256;
        if( nCurPage != nNewPage )
            static_cast<DrawViewShell*>(this)->SwitchPage(nNewPage);
    }
    else // single page panning mode
    {
        double fY = static_cast<double>(pVScroll->GetThumbPos()) / pVScroll->GetRange().Len();
        doScrollView(fY);
    }
}

diff --git a/sd/source/ui/view/viewshel.cxx b/sd/source/ui/view/viewshel.cxx
index 9d0c7c9..b341c8a 100644
--- a/sd/source/ui/view/viewshel.cxx
+++ b/sd/source/ui/view/viewshel.cxx
@@ -111,6 +111,13 @@ private:

namespace sd {

/// When true, scrolling to bottom of a page switches to the next page.
bool ViewShell::CanPanAcrossPages() const
{
    return dynamic_cast<const DrawViewShell*>(this) && mpContentWindow &&
        mpContentWindow->GetVisibleHeight() < 1.0;
}

bool ViewShell::IsPageFlipMode() const
{
    return dynamic_cast< const DrawViewShell *>( this ) !=  nullptr && mpContentWindow &&