/* -*- 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 <officecfg/Office/Common.hxx>
#include <config_wasm_strip.h>
 
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <sfx2/viewfrm.hxx>
#include <sfx2/progress.hxx>
#include <svx/srchdlg.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/ipclient.hxx>
#include <sal/log.hxx>
#include <drawdoc.hxx>
#include <swwait.hxx>
#include <crsrsh.hxx>
#include <doc.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentOutlineNodes.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentState.hxx>
#include <rootfrm.hxx>
#include <pagefrm.hxx>
#include <viewimp.hxx>
#include <frmtool.hxx>
#include <viewopt.hxx>
#include <dview.hxx>
#include <swregion.hxx>
#include <hints.hxx>
#include <docufld.hxx>
#include <txtfrm.hxx>
#include <layact.hxx>
#include <mdiexp.hxx>
#include <fntcache.hxx>
#include <ptqueue.hxx>
#include <docsh.hxx>
#include <bookmark.hxx>
#include <ndole.hxx>
#include <ndindex.hxx>
#include <accmap.hxx>
#include <vcl/bitmapex.hxx>
#include <accessibilityoptions.hxx>
#include <strings.hrc>
#include <bitmaps.hlst>
#include <pagepreviewlayout.hxx>
#include <sortedobjs.hxx>
#include <anchoredobject.hxx>
#include <DocumentSettingManager.hxx>
#include <DocumentRedlineManager.hxx>
#include <DocumentLayoutManager.hxx>
 
#include <unotxdoc.hxx>
#include <view.hxx>
#include <PostItMgr.hxx>
#include <unotools/configmgr.hxx>
#include <vcl/virdev.hxx>
#include <vcl/svapp.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/svdpagv.hxx>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
#include <tools/UnitConversion.hxx>
 
#if !HAVE_FEATURE_DESKTOP
#include <vcl/sysdata.hxx>
#endif
 
#include <frameformats.hxx>
#include <fmtcntnt.hxx>
 
bool SwViewShell::sbLstAct = false;
ShellResource *SwViewShell::spShellRes = nullptr;
 
static tools::DeleteOnDeinit<std::shared_ptr<weld::Window>>& getCareDialog()
{
    static tools::DeleteOnDeinit<std::shared_ptr<weld::Window>> spCareDialog {}; ///< Avoid this window.
    return spCareDialog;
}
 
static bool bInSizeNotify = false;
 
 
using namespace ::com::sun::star;
 
void SwViewShell::SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) {
 
    //tdf#118621 - Optionally disable floating header/footer menu
    if ( bShow )
        bShow = GetViewOptions()->IsUseHeaderFooterMenu();
 
    if ( eControl == FrameControlType::Header )
        mbShowHeaderSeparator = bShow;
    else
        mbShowFooterSeparator = bShow;
}
 
void SwViewShell::ToggleHeaderFooterEdit()
{
    mbHeaderFooterEdit = !mbHeaderFooterEdit;
    if ( !mbHeaderFooterEdit )
    {
        SetShowHeaderFooterSeparator( FrameControlType::Header, false );
        SetShowHeaderFooterSeparator( FrameControlType::Footer, false );
    }
 
    // Avoid corner case
    if ( ( GetViewOptions()->IsUseHeaderFooterMenu() ) &&
         ( !IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
           !IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) )
    {
        mbHeaderFooterEdit = false;
    }
 
    InvalidatePageAndHFSubsidiaryLines();
}
 
// Invalidate Subsidiary Lines around headers/footers and page frames to repaint
void SwViewShell::InvalidatePageAndHFSubsidiaryLines()
{
    RectangleVector aInvalidRects;
    SwPageFrame *pPg = static_cast<SwPageFrame*>(GetLayout()->Lower());
    while (pPg)
    {
        pPg->AddSubsidiaryLinesBounds(*this, aInvalidRects);
        pPg = static_cast<SwPageFrame*>(pPg->GetNext());
    }
 
    for (const auto &rRect : aInvalidRects)
        GetWin()->Invalidate(rRect);
}
 
void SwViewShell::setOutputToWindow(bool bOutputToWindow)
{
    mbOutputToWindow = bOutputToWindow;
}
 
bool SwViewShell::isOutputToWindow() const
{
    return mbOutputToWindow;
}
 
void SwViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwViewShell"));
    mpOpt->dumpAsXml(pWriter);
    (void)xmlTextWriterEndElement(pWriter);
}
 
static void
lcl_PaintTransparentFormControls(SwViewShell const & rShell, SwRect const& rRect)
{
    // Direct paint has been performed: the background of transparent child
    // windows has been painted, so need to paint the child windows now.
    if (rShell.GetWin())
    {
        vcl::Window& rWindow = *(rShell.GetWin());
        const tools::Rectangle aRectanglePixel(rShell.GetOut()->LogicToPixel(rRect.SVRect()));
        PaintTransparentChildren(rWindow, aRectanglePixel);
    }
}
 
// #i72754# 2nd set of Pre/PostPaints
// This time it uses the lock counter (mPrePostPaintRegions empty/non-empty) to allow only one activation
// and deactivation and mpPrePostOutDev to remember the OutDev from the BeginDrawLayers
// call. That way, all places where paint take place can be handled the same way, even
// when calling other paint methods. This is the case at the places where SW paints
// buffered into VDevs to avoid flicker. It is in general problematic and should be
// solved once using the BufferedOutput functionality of the DrawView.
 
void SwViewShell::PrePaint()
{
    // forward PrePaint event from VCL Window to DrawingLayer
    if(HasDrawView())
    {
        Imp()->GetDrawView()->PrePaint();
    }
}
 
void SwViewShell::DLPrePaint2(const vcl::Region& rRegion)
{
    if(mPrePostPaintRegions.empty())
    {
        mPrePostPaintRegions.push( rRegion );
        // #i75172# ensure DrawView to use DrawingLayer bufferings
        if ( !HasDrawView() )
            MakeDrawView();
 
        // Prefer window; if not available, get mpOut (e.g. printer)
        const bool bWindow = GetWin() && !comphelper::LibreOfficeKit::isActive() && !isOutputToWindow();
        mpPrePostOutDev = bWindow ? GetWin()->GetOutDev() : GetOut();
 
        // #i74769# use SdrPaintWindow now direct
        mpTargetPaintWindow = Imp()->GetDrawView()->BeginDrawLayers(mpPrePostOutDev, rRegion);
        assert(mpTargetPaintWindow && "BeginDrawLayers: Got no SdrPaintWindow (!)");
 
        // #i74769# if prerender, save OutDev and redirect to PreRenderDevice
        if(mpTargetPaintWindow->GetPreRenderDevice())
        {
            mpBufferedOut = mpOut;
            mpOut = &(mpTargetPaintWindow->GetTargetOutputDevice());
        }
        else if (isOutputToWindow())
            // In case mpOut is used without buffering and we're not printing, need to set clipping.
            mpOut->SetClipRegion(rRegion);
 
        // remember original paint MapMode for wrapped FlyFrame paints
        maPrePostMapMode = mpOut->GetMapMode();
    }
    else
    {
        // region needs to be updated to the given one
        if( mPrePostPaintRegions.top() != rRegion )
            Imp()->GetDrawView()->UpdateDrawLayersRegion(mpPrePostOutDev, rRegion);
        mPrePostPaintRegions.push( rRegion );
    }
}
 
void SwViewShell::DLPostPaint2(bool bPaintFormLayer)
{
    OSL_ENSURE(!mPrePostPaintRegions.empty(), "SwViewShell::DLPostPaint2: Pre/PostPaint encapsulation broken (!)");
 
    if( mPrePostPaintRegions.size() > 1 )
    {
        vcl::Region current = std::move(mPrePostPaintRegions.top());
        mPrePostPaintRegions.pop();
        if( current != mPrePostPaintRegions.top())
            Imp()->GetDrawView()->UpdateDrawLayersRegion(mpPrePostOutDev, mPrePostPaintRegions.top());
        return;
    }
    mPrePostPaintRegions.pop(); // clear
    if(nullptr != mpTargetPaintWindow)
    {
        // #i74769# restore buffered OutDev
        if(mpTargetPaintWindow->GetPreRenderDevice())
        {
            mpOut = mpBufferedOut;
        }
 
        // #i74769# use SdrPaintWindow now direct
        Imp()->GetDrawView()->EndDrawLayers(*mpTargetPaintWindow, bPaintFormLayer);
        mpTargetPaintWindow = nullptr;
    }
}
// end of Pre/PostPaints
 
void SwViewShell::ImplEndAction( const bool bIdleEnd )
{
    // Nothing to do for the printer?
    if ( !GetWin() || IsPreview() )
    {
        mbPaintWorks = true;
        UISizeNotify();
        // tdf#101464 print preview may generate events if another view shell
        // performs layout...
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
        if (IsPreview() && Imp()->IsAccessible())
        {
            Imp()->FireAccessibleEvents();
        }
#endif
        return;
    }
 
    mbInEndAction = true;
    //will this put the EndAction of the last shell in the sequence?
 
    SwViewShell::sbLstAct = true;
    for(SwViewShell& rShell : GetRingContainer())
    {
        if(&rShell != this && rShell.ActionPend())
        {
            SwViewShell::sbLstAct = false;
            break;
        }
    }
 
    const bool bIsShellForCheckViewLayout = ( this == GetLayout()->GetCurrShell() );
 
    CurrShell aCurr( this );
    if ( Imp()->HasDrawView() && !Imp()->GetDrawView()->areMarkHandlesHidden() )
        Imp()->StartAction();
 
    if ( Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea() )
        Imp()->DeletePaintRegion();
 
    const bool bExtraData = ::IsExtraData( GetDoc() );
 
    if ( !bIdleEnd )
    {
        SwLayAction aAction( GetLayout(), Imp() );
        aAction.SetComplete( false );
        if ( mnLockPaint )
            aAction.SetPaint( false );
        aAction.SetInputType( VclInputFlags::KEYBOARD );
        aAction.Action(GetWin()->GetOutDev());
    }
 
    if ( bIsShellForCheckViewLayout )
        GetLayout()->CheckViewLayout( GetViewOptions(), &maVisArea );
 
    //If we don't call Paints, we wait for the Paint of the system.
    //Then the clipping is set correctly; e.g. shifting of a Draw object
    if ( Imp()->HasPaintRegion()     ||
         maInvalidRect.HasArea() ||
         bExtraData )
    {
        if ( !mnLockPaint )
        {
            SolarMutexGuard aGuard;
 
            bool bPaintsFromSystem = maInvalidRect.HasArea();
            GetWin()->PaintImmediately();
            if ( maInvalidRect.HasArea() )
            {
                if ( bPaintsFromSystem )
                    Imp()->AddPaintRect( maInvalidRect );
 
                ResetInvalidRect();
                bPaintsFromSystem = true;
            }
            mbPaintWorks = true;
 
            std::optional<SwRegionRects> oRegion = Imp()->TakePaintRegion();
 
            //JP 27.11.97: what hid the selection, must also Show it,
            //             else we get Paint errors!
            // e.g. additional mode, page half visible vertically, in the
            // middle a selection and with another cursor jump to left
            // right border. Without ShowCursor the selection disappears.
            bool bShowCursor = oRegion && dynamic_cast<const SwCursorShell*>(this) !=  nullptr;
            if( bShowCursor )
                static_cast<SwCursorShell*>(this)->HideCursors();
 
            if ( oRegion )
            {
                SwRootFrame* pCurrentLayout = GetLayout();
 
                oRegion->LimitToOrigin();
                oRegion->Compress( SwRegionRects::CompressFuzzy );
 
                while ( !oRegion->empty() )
                {
                    SwRect aRect( oRegion->back() );
                    oRegion->pop_back();
 
                    if (GetWin()->SupportsDoubleBuffering())
                        InvalidateWindows(aRect);
                    else
                    {
                        // #i75172# begin DrawingLayer paint
                        // need to do begin/end DrawingLayer preparation for each single rectangle of the
                        // repaint region. I already tried to prepare only once for the whole Region. This
                        // seems to work (and does technically) but fails with transparent objects. Since the
                        // region given to BeginDrawLayers() defines the clip region for DrawingLayer paint,
                        // transparent objects in the single rectangles will indeed be painted multiple times.
                        if (!comphelper::LibreOfficeKit::isActive())
                        {
                            DLPrePaint2(vcl::Region(aRect.SVRect()));
                        }
 
                        if ( bPaintsFromSystem )
                            PaintDesktop(*GetOut(), aRect);
                        if (!comphelper::LibreOfficeKit::isActive())
                            pCurrentLayout->PaintSwFrame( *mpOut, aRect );
                        else
                            pCurrentLayout->GetCurrShell()->InvalidateWindows(aRect);
 
                        // #i75172# end DrawingLayer paint
                        if (!comphelper::LibreOfficeKit::isActive())
                        {
                            DLPostPaint2(true);
                        }
                    }
 
                    lcl_PaintTransparentFormControls(*this, aRect); // i#107365
                }
            }
            if( bShowCursor )
                static_cast<SwCursorShell*>(this)->ShowCursors( true );
        }
        else
        {
            Imp()->DeletePaintRegion();
            mbPaintWorks =  true;
        }
    }
    else
        mbPaintWorks = true;
 
    mbInEndAction = false;
    SwViewShell::sbLstAct = false;
    Imp()->EndAction();
 
    //We artificially end the action here to enable the automatic scrollbars
    //to adjust themselves correctly
    //EndAction sends a Notify, and that must call Start-/EndAction to
    //adjust the scrollbars correctly
    --mnStartAction;
    UISizeNotify();
    ++mnStartAction;
 
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if( Imp()->IsAccessible() )
        Imp()->FireAccessibleEvents();
#endif
}
 
void SwViewShell::ImplStartAction()
{
    mbPaintWorks = false;
    Imp()->StartAction();
}
 
void SwViewShell::ImplLockPaint()
{
    if ( GetWin() && GetWin()->IsVisible() && !comphelper::LibreOfficeKit::isActive())
        GetWin()->EnablePaint( false ); //Also cut off the controls.
    Imp()->LockPaint();
}
 
void SwViewShell::ImplUnlockPaint(std::vector<LockPaintReason>& rReasons, bool bVirDev)
{
    CurrShell aCurr( this );
    if ( GetWin() && GetWin()->IsVisible() )
    {
        if ( (bInSizeNotify || bVirDev ) && VisArea().HasArea() && !comphelper::LibreOfficeKit::isActive())
        {
            //Refresh with virtual device to avoid flickering.
            VclPtrInstance<VirtualDevice> pVout( *mpOut );
            pVout->SetMapMode( mpOut->GetMapMode() );
            Size aSize( VisArea().SSize() );
            aSize.AdjustWidth(20 );
            aSize.AdjustHeight(20 );
            if( pVout->SetOutputSize( aSize ) )
            {
                GetWin()->EnablePaint( true );
                GetWin()->Validate();
 
                Imp()->UnlockPaint();
                pVout->SetLineColor( mpOut->GetLineColor() );
                pVout->SetFillColor( mpOut->GetFillColor() );
 
                // #i72754# start Pre/PostPaint encapsulation before mpOut is changed to the buffering VDev
                const vcl::Region aRepaintRegion(VisArea().SVRect());
                DLPrePaint2(aRepaintRegion);
 
                OutputDevice *pOld = mpOut;
                mpOut = pVout.get();
                Paint(*mpOut, VisArea().SVRect());
                mpOut = pOld;
                mpOut->DrawOutDev( VisArea().Pos(), aSize,
                                  VisArea().Pos(), aSize, *pVout );
 
                // #i72754# end Pre/PostPaint encapsulation when mpOut is back and content is painted
                DLPostPaint2(true);
 
                lcl_PaintTransparentFormControls(*this, VisArea()); // fdo#63949
            }
            else
            {
                Imp()->UnlockPaint();
                GetWin()->EnablePaint( true );
                InvalidateAll(rReasons);
            }
            pVout.disposeAndClear();
        }
        else
        {
            Imp()->UnlockPaint();
            GetWin()->EnablePaint( true );
            InvalidateAll(rReasons);
        }
    }
    else
        Imp()->UnlockPaint();
}
 
namespace
{
    std::string_view to_string(LockPaintReason eReason)
    {
        switch(eReason)
        {
            case LockPaintReason::ViewLayout:
                return "ViewLayout";
            case LockPaintReason::OuterResize:
                return "OuterResize";
            case LockPaintReason::Undo:
                return "Undo";
            case LockPaintReason::Redo:
                return "Redo";
            case LockPaintReason::OutlineFolding:
                return "OutlineFolding";
            case LockPaintReason::EndSdrCreate:
                return "EndSdrCreate";
            case LockPaintReason::SwLayIdle:
                return "SwLayIdle";
            case LockPaintReason::InvalidateLayout:
                return "InvalidateLayout";
            case LockPaintReason::StartDrag:
                return "StartDrag";
            case LockPaintReason::DataChanged:
                return "DataChanged";
            case LockPaintReason::InsertFrame:
                return "InsertFrame";
            case LockPaintReason::GotoPage:
                return "GotoPage";
            case LockPaintReason::InsertGraphic:
                return "InsertGraphic";
            case LockPaintReason::SetZoom:
                return "SetZoom";
            case LockPaintReason::ExampleFrame:
                return "ExampleFram";
        }
        return "";
    };
}
 
void SwViewShell::InvalidateAll(std::vector<LockPaintReason>& rReasons)
{
    assert(!rReasons.empty() && "there must be a reason to InvalidateAll");
 
    for (const auto& reason : rReasons)
        SAL_INFO("sw.core", "InvalidateAll because of: " << to_string(reason));
 
    if (comphelper::LibreOfficeKit::isActive())
    {
        // https://github.com/CollaboraOnline/online/issues/6379
        // ditch OuterResize as a reason to invalidate all in the online case
        std::erase(rReasons, LockPaintReason::OuterResize);
    }
 
    if (!rReasons.empty())
        GetWin()->Invalidate(InvalidateFlags::Children);
    rReasons.clear();
}
 
bool SwViewShell::AddPaintRect( const SwRect & rRect )
{
    bool bRet = false;
    for(SwViewShell& rSh : GetRingContainer())
    {
        if( rSh.Imp() )
        {
            if ( rSh.IsPreview() && rSh.GetWin() )
                ::RepaintPagePreview( &rSh, rRect );
            else
                bRet |= rSh.Imp()->AddPaintRect( rRect );
        }
    }
    return bRet;
}
 
void SwViewShell::InvalidateWindows( const SwRect &rRect )
{
    if ( Imp()->IsCalcLayoutProgress() )
        return;
 
    if(comphelper::LibreOfficeKit::isActive())
    {
        // If we are inside tiled painting, invalidations are ignored.
        // Ignore them right now to save work, but also to avoid the problem
        // that this state could be reset before FlushPendingLOKInvalidateTiles()
        // gets called.
        if(comphelper::LibreOfficeKit::isTiledPainting())
            return;
        // First collect all invalidations and perform them only later,
        // otherwise the number of Invalidate() calls would be at least
        // O(n^2) if not worse. The problem is that if any change in a document
        // is made, SwEditShell::EndAllAction() is called, which calls EndAction()
        // for every view. And every view does it own handling of paint rectangles,
        // and then calls InvalidateWindows() based on that. On then this code
        // would call Invalidate() for all views for each rectangle.
        // So collect the rectangles, avoid duplicates (which there usually will
        // be many because of the repetitions), FlushPendingLOKInvalidateTiles()
        // will collect all rectangles from all related views, compress them
        // and only with those relatively few rectangle it'd call Invalidate()
        // for all views.
        Imp()->AddPendingLOKInvalidation(rRect);
        return;
    }
 
    for(SwViewShell& rSh : GetRingContainer())
    {
        if ( rSh.GetWin() )
        {
            if ( rSh.IsPreview() )
                ::RepaintPagePreview( &rSh, rRect );
            // In case of tiled rendering, invalidation is wanted even if
            // the rectangle is outside the visual area.
            else if ( rSh.VisArea().Overlaps( rRect ) || comphelper::LibreOfficeKit::isActive() )
                rSh.GetWin()->Invalidate( rRect.SVRect() );
        }
    }
}
 
void SwViewShell::FlushPendingLOKInvalidateTiles()
{
    assert(comphelper::LibreOfficeKit::isActive());
    SwRegionRects rects;
    for(SwViewShell& rSh : GetRingContainer())
    {
        std::vector<SwRect> tmpRects = rSh.Imp()->TakePendingLOKInvalidations();
        rects.insert( rects.end(), tmpRects.begin(), tmpRects.end());
    }
    rects.Compress( SwRegionRects::CompressFuzzy );
    if(rects.empty())
        return;
    // This is basically the loop from SwViewShell::InvalidateWindows().
    for(SwViewShell& rSh : GetRingContainer())
    {
        if ( rSh.GetWin() )
        {
            if ( rSh.IsPreview() )
            {
                for( const SwRect& rect : rects )
                    ::RepaintPagePreview( &rSh, rect );
            }
            else
            {
                for( const SwRect& rect : rects )
                    rSh.GetWin()->Invalidate( rect.SVRect() );
            }
        }
    }
}
 
const SwRect& SwViewShell::VisArea() const
{
    // when using the tiled rendering, consider the entire document as our
    // visible area
    return comphelper::LibreOfficeKit::isActive()? GetLayout()->getFrameArea(): maVisArea;
}
 
void SwViewShell::MakeVisible( const SwRect &rRect )
{
    if ( !(!VisArea().Contains( rRect ) || IsScrollMDI( this, rRect ) || GetCareDialog(*this)) )
        return;
 
    if ( IsViewLocked() )
        return;
 
    if( mpWin )
    {
        const SwFrame* pRoot = GetLayout();
        int nLoopCnt = 3;
        tools::Long nOldH;
        do{
            nOldH = pRoot->getFrameArea().Height();
            StartAction();
            ScrollMDI( this, rRect, USHRT_MAX, USHRT_MAX );
            EndAction();
        } while( nOldH != pRoot->getFrameArea().Height() && nLoopCnt-- );
    }
#if OSL_DEBUG_LEVEL > 0
    else
    {
        //MA: 04. Nov. 94, no one needs this, does one?
        OSL_ENSURE( false, "Is MakeVisible still needed for printers?" );
    }
 
#endif
}
 
weld::Window* SwViewShell::CareChildWin(SwViewShell const & rVSh)
{
    if (!rVSh.mpSfxViewShell)
        return nullptr;
#if HAVE_FEATURE_DESKTOP
    const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId();
    SfxViewFrame& rVFrame = rVSh.mpSfxViewShell->GetViewFrame();
    SfxChildWindow* pChWin = rVFrame.GetChildWindow( nId );
    if (!pChWin)
        return nullptr;
    weld::DialogController* pController = pChWin->GetController().get();
    if (!pController)
        return nullptr;
    weld::Window* pWin = pController->getDialog();
    if (pWin && pWin->get_visible())
        return pWin;
#endif
    return nullptr;
}
 
Point SwViewShell::GetPagePos( sal_uInt16 nPageNum ) const
{
    return GetLayout()->GetPagePos( nPageNum );
}
 
sal_uInt16 SwViewShell::GetNumPages() const
{
    //It is possible that no layout exists when the method from
    //root-Ctor is called.
    return GetLayout() ? GetLayout()->GetPageNum() : 0;
}
 
bool SwViewShell::IsDummyPage( sal_uInt16 nPageNum ) const
{
    return GetLayout() && GetLayout()->IsDummyPage( nPageNum );
}
 
/**
 * Forces update of each field.
 * It notifies all fields with pNewHt. If that is 0 (default), the field
 * type is sent (???).
 * @param[in] bCloseDB Passed in to GetDoc()->UpdateFields. [TODO] Purpose???
 */
void SwViewShell::UpdateFields(bool bCloseDB, bool bSetModified)
{
    CurrShell aCurr( this );
 
    auto pCursorShell = dynamic_cast<SwCursorShell*>( this );
    if ( pCursorShell )
        pCursorShell->StartAction();
    else
        StartAction();
 
    GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB, bSetModified);
 
    if ( pCursorShell )
        pCursorShell->EndAction();
    else
        EndAction();
}
 
void SwViewShell::UpdateOleObjectPreviews()
{
    SwDoc* pDoc = GetDoc();
    for(sw::SpzFrameFormat* pFormat: *pDoc->GetSpzFrameFormats())
    {
        if (pFormat->Which() != RES_FLYFRMFMT)
        {
            continue;
        }
 
        const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
        if (!pNodeIndex || !pNodeIndex->GetNodes().IsDocNodes())
        {
            continue;
        }
 
        SwNode* pNode = pDoc->GetNodes()[pNodeIndex->GetIndex() + 1];
        SwOLENode* pOleNode = pNode->GetOLENode();
        if (!pOleNode)
        {
            continue;
        }
 
        SwOLEObj& rOleObj = pOleNode->GetOLEObj();
        svt::EmbeddedObjectRef& rObject = rOleObj.GetObject();
        rObject.UpdateReplacement( true );
        // Trigger the repaint.
        pOleNode->SetChanged();
    }
}
 
/** update all charts for which any table exists */
void SwViewShell::UpdateAllCharts()
{
    CurrShell aCurr( this );
    // Start-/EndAction handled in the SwDoc-Method!
    GetDoc()->UpdateAllCharts();
}
 
bool SwViewShell::HasCharts() const
{
    bool bRet = false;
    SwNodeIndex aIdx( *GetDoc()->GetNodes().GetEndOfAutotext().
                        StartOfSectionNode(), 1 );
    while (aIdx.GetNode().GetStartNode())
    {
        ++aIdx;
        const SwOLENode *pNd = aIdx.GetNode().GetOLENode();
        if( pNd && !pNd->GetChartTableName().isEmpty() )
        {
            bRet = true;
            break;
        }
    }
    return bRet;
}
 
void SwViewShell::LayoutIdle()
{
    if( !mpOpt->IsIdle() || !GetWin() || HasDrawViewDrag() )
        return;
 
    //No idle when printing is going on.
    for(const SwViewShell& rSh : GetRingContainer())
    {
        if ( !rSh.GetWin() )
            return;
    }
 
    CurrShell aCurr( this );
 
#ifdef DBG_UTIL
    // If Test5 has been set, the IdleFormatter is disabled.
    if( mpOpt->IsTest5() )
        return;
#endif
 
    {
        // Preserve top of the text frame cache.
        SwSaveSetLRUOfst aSaveLRU;
        // #125243# there are lots of stacktraces indicating that Imp() returns NULL
        // this SwViewShell seems to be invalid - but it's not clear why
        // this return is only a workaround!
        OSL_ENSURE(Imp(), "SwViewShell already deleted?");
        if(!Imp())
            return;
        SwLayIdle aIdle( GetLayout(), Imp() );
    }
}
 
static void lcl_InvalidateAllContent( SwViewShell& rSh, SwInvalidateFlags nInv )
{
    auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
    if ( pCursorShell )
        pCursorShell->StartAction();
    else
        rSh.StartAction();
    rSh.GetLayout()->InvalidateAllContent( nInv );
    if ( pCursorShell )
        pCursorShell->EndAction();
    else
        rSh.EndAction();
 
    rSh.GetDoc()->getIDocumentState().SetModified();
}
 
/** local method to invalidate/re-calculate positions of floating screen
 *  objects (Writer fly frame and drawing objects), which are anchored
 *  to paragraph or to character. #i11860#
 */
static void lcl_InvalidateAllObjPos( SwViewShell &_rSh )
{
    auto pCursorShell = dynamic_cast<SwCursorShell*>( &_rSh);
    if ( pCursorShell )
        pCursorShell->StartAction();
    else
        _rSh.StartAction();
 
    _rSh.GetLayout()->InvalidateAllObjPos();
 
    if ( pCursorShell )
        pCursorShell->EndAction();
    else
        _rSh.EndAction();
 
    _rSh.GetDoc()->getIDocumentState().SetModified();
}
 
void SwViewShell::SetParaSpaceMax( bool bNew )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) != bNew )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::PARA_SPACE_MAX, bNew );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this,  nInv );
    }
}
 
void SwViewShell::SetParaSpaceMaxAtPages( bool bNew )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) != bNew )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, bNew );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this,  nInv );
    }
}
 
void SwViewShell::SetTabCompat( bool bNew )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if( rIDSA.get(DocumentSettingId::TAB_COMPAT) != bNew  )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::TAB_COMPAT, bNew );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
void SwViewShell::SetAddExtLeading( bool bNew )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) != bNew )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::ADD_EXT_LEADING, bNew );
        SwDrawModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel();
        if ( pTmpDrawModel )
            pTmpDrawModel->SetAddExtLeading( bNew );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
/** Sets if paragraph and table spacing is added at bottom of table cells.
 * #106629#
 * @param[in] (bool) setting of the new value
 */
void SwViewShell::SetAddParaSpacingToTableCells( bool _bAddParaSpacingToTableCells )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells
        || rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells)
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells );
        // note: the dialog can't change the value to indeterminate, so only false/false and true/true
        rIDSA.set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
/**
 * Sets if former formatting of text lines with proportional line spacing should used.
 * #i11859#
 * @param[in] (bool) setting of the new value
 */
void SwViewShell::SetUseFormerLineSpacing( bool _bUseFormerLineSpacing )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) != _bUseFormerLineSpacing )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::OLD_LINE_SPACING, _bUseFormerLineSpacing );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
/**
 * Sets IDocumentSettingAccess if former object positioning should be used.
 * #i11860#
 * @param[in] (bool) setting the new value
 */
void SwViewShell::SetUseFormerObjectPositioning( bool _bUseFormerObjPos )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::USE_FORMER_OBJECT_POS) != _bUseFormerObjPos )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::USE_FORMER_OBJECT_POS, _bUseFormerObjPos );
        lcl_InvalidateAllObjPos( *this );
    }
}
 
// #i28701#
void SwViewShell::SetConsiderWrapOnObjPos( bool _bConsiderWrapOnObjPos )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) != _bConsiderWrapOnObjPos )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, _bConsiderWrapOnObjPos );
        lcl_InvalidateAllObjPos( *this );
    }
}
 
void SwViewShell::SetUseFormerTextWrapping( bool _bUseFormerTextWrapping )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) != _bUseFormerTextWrapping )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, _bUseFormerTextWrapping );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
// #i45491#
void SwViewShell::SetDoNotJustifyLinesWithManualBreak( bool _bDoNotJustifyLinesWithManualBreak )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if ( rIDSA.get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK) != _bDoNotJustifyLinesWithManualBreak )
    {
        SwWait aWait( *GetDoc()->GetDocShell(), true );
        rIDSA.set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, _bDoNotJustifyLinesWithManualBreak );
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent( *this, nInv );
    }
}
 
void SwViewShell::SetProtectForm( bool _bProtectForm )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    rIDSA.set(DocumentSettingId::PROTECT_FORM, _bProtectForm );
}
 
void SwViewShell::SetMsWordCompTrailingBlanks( bool _bMsWordCompTrailingBlanks )
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS) != _bMsWordCompTrailingBlanks)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, _bMsWordCompTrailingBlanks);
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::SetSubtractFlysAnchoredAtFlys(bool bSubtractFlysAnchoredAtFlys)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    rIDSA.set(DocumentSettingId::SUBTRACT_FLYS, bSubtractFlysAnchoredAtFlys);
}
 
void SwViewShell::SetEmptyDbFieldHidesPara(bool bEmptyDbFieldHidesPara)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA) == bEmptyDbFieldHidesPara)
        return;
 
    SwWait aWait(*GetDoc()->GetDocShell(), true);
    rIDSA.set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, bEmptyDbFieldHidesPara);
    StartAction();
    GetDoc()->getIDocumentState().SetModified();
    for (auto const & pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes())
    {
        if(pFieldType->Which() == SwFieldIds::Database)
            pFieldType->UpdateFields();
    }
    EndAction();
}
 
void SwViewShell::SetNoGapAfterNoteNumber(bool bNew)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER) != bNew)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::NO_GAP_AFTER_NOTE_NUMBER, bNew);
        const SwInvalidateFlags nInv = SwInvalidateFlags::Size | SwInvalidateFlags::Pos | SwInvalidateFlags::PrtArea;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::SetTabsRelativeToIndent(bool bNew)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) != bNew)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::TABS_RELATIVE_TO_INDENT, bNew);
        const SwInvalidateFlags nInv = SwInvalidateFlags::Size | SwInvalidateFlags::Section | SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Pos;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::SetTabOverMargin(bool bNew)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN) != bNew)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::TAB_OVER_MARGIN, bNew);
        const SwInvalidateFlags nInv = SwInvalidateFlags::Size | SwInvalidateFlags::Section | SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Pos;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::SetDoNotMirrorRtlDrawObjs(bool bDoNotMirrorRtlDrawObjs)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS) != bDoNotMirrorRtlDrawObjs)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::DO_NOT_MIRROR_RTL_DRAW_OBJS, bDoNotMirrorRtlDrawObjs);
        lcl_InvalidateAllObjPos(*this);
    }
}
 
void SwViewShell::SetContinuousEndnotes(bool bContinuousEndnotes)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::CONTINUOUS_ENDNOTES) != bContinuousEndnotes)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::CONTINUOUS_ENDNOTES, bContinuousEndnotes);
        StartAction();
        GetLayout()->RemoveFootnotes(/*pPage=*/nullptr, /*pPageOnly=*/false, /*bEndNotes=*/true);
        EndAction();
        GetDoc()->getIDocumentState().SetModified();
    }
}
 
void SwViewShell::SetMsWordCompGridMetrics(bool _bMsWordCompGridMetrics)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::MS_WORD_COMP_GRID_METRICS) != _bMsWordCompGridMetrics)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::MS_WORD_COMP_GRID_METRICS, _bMsWordCompGridMetrics);
        const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Pos
                                       | SwInvalidateFlags::Size | SwInvalidateFlags::Table
                                       | SwInvalidateFlags::Section;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::SetIgnoreTabsAndBlanksForLineCalculation(bool val)
{
    IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
    if (rIDSA.get(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION) != val)
    {
        SwWait aWait(*GetDoc()->GetDocShell(), true);
        rIDSA.set(DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION, val);
        const SwInvalidateFlags nInv = SwInvalidateFlags::Size | SwInvalidateFlags::Section
                                        | SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table
                                        | SwInvalidateFlags::Pos;
        lcl_InvalidateAllContent(*this, nInv);
    }
}
 
void SwViewShell::Reformat()
{
    SwWait aWait( *GetDoc()->GetDocShell(), true );
 
    // we go for safe: get rid of the old font information,
    // when the printer resolution or zoom factor changes.
    // Init() and Reformat() are the safest locations.
    pFntCache->Flush( );
 
    if( GetLayout()->IsCallbackActionEnabled() )
    {
        StartAction();
        GetLayout()->InvalidateAllContent( SwInvalidateFlags::Size | SwInvalidateFlags::Pos | SwInvalidateFlags::PrtArea );
        EndAction();
    }
}
 
void SwViewShell::ChgNumberDigits()
{
    SdrModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel();
    if ( pTmpDrawModel )
           pTmpDrawModel->ReformatAllTextObjects();
    Reformat();
}
 
void SwViewShell::CalcLayout()
{
    // extremely likely to be a Bad Idea to call this without StartAction
    // (except the Page Preview apparently only has a non-subclassed ViewShell)
    assert((typeid(*this) == typeid(SwViewShell)) || mnStartAction);
 
    CurrShell aCurr( this );
    SwWait aWait( *GetDoc()->GetDocShell(), true );
 
    // Preserve top of the text frame cache.
    SwSaveSetLRUOfst aSaveLRU;
 
    //switch on Progress when none is running yet.
    const bool bEndProgress = SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) == nullptr;
    if ( bEndProgress )
    {
        tools::Long nEndPage = GetLayout()->GetPageNum();
        nEndPage += nEndPage * 10 / 100;
        ::StartProgress( STR_STATSTR_REFORMAT, 0, nEndPage, GetDoc()->GetDocShell() );
    }
 
    SwLayAction aAction( GetLayout(), Imp() );
    aAction.SetPaint( false );
    aAction.SetStatBar( true );
    aAction.SetCalcLayout( true );
    aAction.SetReschedule( true );
    GetDoc()->getIDocumentFieldsAccess().LockExpFields();
    aAction.Action(GetOut());
    GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
 
    //the SetNewFieldLst() on the Doc was cut off and must be fetched again
    //(see flowfrm.cxx, txtfld.cxx)
    if ( aAction.IsExpFields() )
    {
        aAction.Reset();
        aAction.SetPaint( false );
        aAction.SetStatBar( true );
        aAction.SetReschedule( true );
 
        GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(0);
        GetDoc()->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
 
        aAction.Action(GetOut());
    }
 
    if ( VisArea().HasArea() )
        InvalidateWindows( VisArea() );
    if ( bEndProgress )
        ::EndProgress( GetDoc()->GetDocShell() );
}
 
void SwViewShell::SetFirstVisPageInvalid()
{
    for(SwViewShell& rSh : GetRingContainer())
    {
        if ( rSh.Imp() )
            rSh.Imp()->SetFirstVisPageInvalid();
    }
}
 
void SwViewShell::SizeChgNotify()
{
    if ( !mpWin )
        mbDocSizeChgd = true;
    else if( ActionPend() || Imp()->IsCalcLayoutProgress() || mbPaintInProgress )
    {
        mbDocSizeChgd = true;
 
        if ( !Imp()->IsCalcLayoutProgress() && dynamic_cast<const SwCursorShell*>( this ) !=  nullptr )
        {
            PageNumNotify(this);
 
            if (comphelper::LibreOfficeKit::isActive())
            {
                Size aDocSize = GetDocSize();
                OString sPayload = OString::number(aDocSize.Width() + 2 * DOCUMENTBORDER) +
                    ", " + OString::number(aDocSize.Height() + 2 * DOCUMENTBORDER);
 
                SwXTextDocument* pModel = comphelper::getFromUnoTunnel<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument());
                SfxLokHelper::notifyDocumentSizeChanged(GetSfxViewShell(), sPayload, pModel);
            }
        }
    }
    else
    {
        mbDocSizeChgd = false;
        ::SizeNotify( this, GetDocSize() );
    }
}
 
void SwViewShell::VisPortChgd( const SwRect &rRect)
{
    OSL_ENSURE( GetWin(), "VisPortChgd without Window." );
 
    if ( rRect == VisArea() )
        return;
 
    // Is someone spuriously rescheduling again?
    SAL_WARN_IF(mbInEndAction, "sw.core", "Scrolling during EndAction");
 
    //First get the old visible page, so we don't have to look
    //for it afterwards.
    const SwFrame *pOldPage = Imp()->GetFirstVisPage(GetWin()->GetOutDev());
 
    const SwRect aPrevArea( VisArea() );
    const bool bFull = aPrevArea.IsEmpty();
    maVisArea = rRect;
    SetFirstVisPageInvalid();
 
    //When there a PaintRegion still exists and the VisArea has changed,
    //the PaintRegion is at least by now obsolete. The PaintRegion can
    //have been created by RootFrame::PaintSwFrame.
    if ( !mbInEndAction &&
         Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea() )
        Imp()->DeletePaintRegion();
 
    CurrShell aCurr( this );
 
    bool bScrolled = false;
 
    SwPostItMgr* pPostItMgr = GetPostItMgr();
 
    if ( bFull )
        GetWin()->Invalidate();
    else
    {
        //Calculate amount to be scrolled.
        const tools::Long nXDiff = aPrevArea.Left() - VisArea().Left();
        const tools::Long nYDiff = aPrevArea.Top()  - VisArea().Top();
 
        if( !nXDiff && !GetViewOptions()->getBrowseMode() &&
            (!Imp()->HasDrawView() || !Imp()->GetDrawView()->IsGridVisible() ) )
        {
            // If possible, don't scroll the application background
            // (PaintDesktop).  Also limit the left and right side of
            // the scroll range to the pages.
            const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
            if ( pPage->getFrameArea().Top() > pOldPage->getFrameArea().Top() )
                pPage = static_cast<const SwPageFrame*>(pOldPage);
            SwRect aBoth( VisArea() );
            aBoth.Union( aPrevArea );
            const SwTwips nBottom = aBoth.Bottom();
            SwTwips nMinLeft = SAL_MAX_INT32;
            SwTwips nMaxRight= 0;
 
            const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode();
 
            while ( pPage && pPage->getFrameArea().Top() <= nBottom )
            {
                SwRect aPageRect( pPage->GetBoundRect(GetWin()->GetOutDev()) );
                if ( bBookMode )
                {
                    const SwPageFrame& rFormatPage = pPage->GetFormatPage();
                    aPageRect.SSize( rFormatPage.GetBoundRect(GetWin()->GetOutDev()).SSize() );
                }
 
                // #i9719# - consider new border and shadow width
                if ( aPageRect.Overlaps( aBoth ) )
                {
                    SwTwips nPageLeft = 0;
                    SwTwips nPageRight = 0;
                    const sw::sidebarwindows::SidebarPosition aSidebarPos = pPage->SidebarPosition();
 
                    if( aSidebarPos != sw::sidebarwindows::SidebarPosition::NONE )
                    {
                        nPageLeft = aPageRect.Left();
                        nPageRight = aPageRect.Right();
                    }
 
                    if( nPageLeft < nMinLeft )
                        nMinLeft = nPageLeft;
                    if( nPageRight > nMaxRight )
                        nMaxRight = nPageRight;
                    //match with the draw objects
                    //take nOfst into account as the objects have been
                    //selected and have handles attached.
                    if ( pPage->GetSortedObjs() )
                    {
                        const tools::Long nOfst = GetOut()->PixelToLogic(
                            Size(Imp()->GetDrawView()->GetMarkHdlSizePixel()/2,0)).Width();
                        for (SwAnchoredObject* pObj : *pPage->GetSortedObjs())
                        {
                            // ignore objects that are not actually placed on the page
                            if (pObj->IsFormatPossible())
                            {
                                const tools::Rectangle aBound = pObj->GetObjRect().SVRect();
                                if (aBound.Left() != FAR_AWAY) {
                                    // OD 03.03.2003 #107927# - use correct datatype
                                    const SwTwips nL = std::max( SwTwips(0), SwTwips(aBound.Left() - nOfst) );
                                    if ( nL < nMinLeft )
                                        nMinLeft = nL;
                                    if( aBound.Right() + nOfst > nMaxRight )
                                        nMaxRight = aBound.Right() + nOfst;
                                }
                            }
                        }
                    }
                }
                pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
            }
            tools::Rectangle aRect( aPrevArea.SVRect() );
            aRect.SetLeft( nMinLeft );
            aRect.SetRight( nMaxRight );
            if( VisArea().Overlaps( aPrevArea ) && !mnLockPaint )
            {
                bScrolled = true;
                maVisArea.Pos() = aPrevArea.Pos();
                if ( SmoothScroll( nXDiff, nYDiff, &aRect ) )
                    return;
                maVisArea.Pos() = rRect.Pos();
            }
            else if (!comphelper::LibreOfficeKit::isActive())
                GetWin()->Invalidate( aRect );
        }
        else if ( !mnLockPaint ) //will be released in Unlock
        {
            if( VisArea().Overlaps( aPrevArea ) )
            {
                bScrolled = true;
                maVisArea.Pos() = aPrevArea.Pos();
                if ( SmoothScroll( nXDiff, nYDiff, nullptr ) )
                    return;
                maVisArea.Pos() = rRect.Pos();
            }
            else
                GetWin()->Invalidate();
        }
    }
 
    // When tiled rendering, the map mode of the window is disabled, avoid
    // enabling it here.
    if (!comphelper::LibreOfficeKit::isActive())
    {
        Point aPt( VisArea().Pos() );
        aPt.setX( -aPt.X() ); aPt.setY( -aPt.Y() );
        MapMode aMapMode( GetWin()->GetMapMode() );
        aMapMode.SetOrigin( aPt );
        GetWin()->SetMapMode( aMapMode );
    }
 
    if ( HasDrawView() )
    {
        Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() );
        Imp()->GetDrawView()->SetActualWin( GetWin()->GetOutDev() );
    }
    GetWin()->PaintImmediately();
 
    if ( pPostItMgr ) // #i88070#
    {
        pPostItMgr->Rescale();
        pPostItMgr->CalcRects();
        pPostItMgr->LayoutPostIts();
    }
 
    if ( !bScrolled && pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
        pPostItMgr->CorrectPositions();
 
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if( Imp()->IsAccessible() )
        Imp()->UpdateAccessible();
#endif
}
 
bool SwViewShell::SmoothScroll( tools::Long lXDiff, tools::Long lYDiff, const tools::Rectangle *pRect )
{
#if !defined(MACOSX) && !defined(ANDROID) && !defined(IOS)
    // #i98766# - disable smooth scrolling for Mac
 
    const sal_uLong nBitCnt = mpOut->GetBitCount();
    tools::Long lMult = 1, lMax = LONG_MAX;
    if ( nBitCnt == 16 )
    {
        lMax = 7000;
        lMult = 2;
    }
    if ( nBitCnt == 24 )
    {
        lMax = 5000;
        lMult = 6;
    }
    else if ( nBitCnt == 1 )
    {
        lMax = 3000;
        lMult = 12;
    }
 
    // #i75172# isolated static conditions
    const bool bOnlyYScroll(!lXDiff && std::abs(lYDiff) != 0 && std::abs(lYDiff) < lMax);
    const bool bAllowedWithChildWindows(GetWin()->GetWindowClipRegionPixel().IsNull());
    const bool bSmoothScrollAllowed(bOnlyYScroll && mbEnableSmooth && GetViewOptions()->IsSmoothScroll() &&  bAllowedWithChildWindows);
 
    if(bSmoothScrollAllowed)
    {
        Imp()->m_bStopSmooth = false;
 
        const SwRect aOldVis( VisArea() );
 
        //create virtual device and set.
        const Size aPixSz = GetWin()->PixelToLogic(Size(1,1));
        VclPtrInstance<VirtualDevice> pVout( *GetWin()->GetOutDev() );
        pVout->SetLineColor( GetWin()->GetOutDev()->GetLineColor() );
        pVout->SetFillColor( GetWin()->GetOutDev()->GetFillColor() );
        MapMode aMapMode( GetWin()->GetMapMode() );
        pVout->SetMapMode( aMapMode );
        Size aSize( maVisArea.Width()+2*aPixSz.Width(), std::abs(lYDiff)+(2*aPixSz.Height()) );
        if ( pRect )
            aSize.setWidth( std::min(aSize.Width(), pRect->GetWidth()+2*aPixSz.Width()) );
        if ( pVout->SetOutputSize( aSize ) )
        {
            mnLockPaint++;
 
            //First Paint everything in the virtual device.
            SwRect aRect( VisArea() );
            aRect.Height( aSize.Height() );
            if ( pRect )
            {
                aRect.Pos().setX( std::max(aRect.Left(),pRect->Left()-aPixSz.Width()) );
                aRect.Right( std::min(aRect.Right()+2*aPixSz.Width(), pRect->Right()+aPixSz.Width()));
            }
            else
                aRect.AddWidth(2*aPixSz.Width() );
            aRect.Pos().setY( lYDiff < 0 ? aOldVis.Bottom() - aPixSz.Height()
                                         : aRect.Top() - aSize.Height() + aPixSz.Height() );
            aRect.Pos().setX( std::max( tools::Long(0), aRect.Left()-aPixSz.Width() ) );
            aRect.Pos()  = GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.Pos()));
            aRect.SSize( GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.SSize())) );
            maVisArea = aRect;
            const Point aPt( -aRect.Left(), -aRect.Top() );
            aMapMode.SetOrigin( aPt );
            pVout->SetMapMode( aMapMode );
            OutputDevice *pOld = mpOut;
            mpOut = pVout.get();
 
            {
                // #i75172# To get a clean repaint, a new ObjectContact is needed here. Without, the
                // repaint would not be correct since it would use the wrong DrawPage visible region.
                // This repaint IS about painting something currently outside the visible part (!).
                // For that purpose, AddDeviceToPaintView is used which creates a new SdrPageViewWindow
                // and all the necessary stuff. It's not cheap, but necessary here. Alone because repaint
                // target really is NOT the current window.
                // Also will automatically NOT use PreRendering and overlay (since target is VirtualDevice)
                if(!HasDrawView())
                    MakeDrawView();
                SdrView* pDrawView = GetDrawView();
                pDrawView->AddDeviceToPaintView(*pVout, nullptr);
 
                // clear mpWin during DLPrePaint2 to get paint preparation for mpOut, but set it again
                // immediately afterwards. There are many decisions in SW which imply that Printing
                // is used when mpWin == 0 (wrong but widely used).
                vcl::Window* pOldWin = mpWin;
                mpWin = nullptr;
                DLPrePaint2(vcl::Region(aRect.SVRect()));
                mpWin = pOldWin;
 
                // SW paint stuff
                PaintDesktop(*GetOut(), aRect);
                SwViewShell::sbLstAct = true;
                GetLayout()->PaintSwFrame( *GetOut(), aRect );
                SwViewShell::sbLstAct = false;
 
                // end paint and destroy ObjectContact again
                DLPostPaint2(true);
                pDrawView->DeleteDeviceFromPaintView(*pVout);
            }
 
            mpOut = pOld;
            maVisArea = aOldVis;
 
            //Now shift in parts and copy the new Pixel from the virtual device.
 
            // ??????????????????????
            // or is it better to get the scrollfactor from the User
            // as option?
            // ??????????????????????
            tools::Long lMaDelta = aPixSz.Height();
            if ( std::abs(lYDiff) > ( maVisArea.Height() / 3 ) )
                lMaDelta *= 6;
            else
                lMaDelta *= 2;
 
            lMaDelta *= lMult;
 
            if ( lYDiff < 0 )
                lMaDelta = -lMaDelta;
 
            tools::Long lDiff = lYDiff;
            while ( lDiff )
            {
                tools::Long lScroll;
                if ( Imp()->m_bStopSmooth || std::abs(lDiff) <= std::abs(lMaDelta) )
                {
                    lScroll = lDiff;
                    lDiff = 0;
                }
                else
                {
                    lScroll = lMaDelta;
                    lDiff -= lMaDelta;
                }
 
                const SwRect aTmpOldVis = VisArea();
                maVisArea.Pos().AdjustY( -lScroll );
                maVisArea.Pos() = GetWin()->PixelToLogic( GetWin()->LogicToPixel( VisArea().Pos()));
                lScroll = aTmpOldVis.Top() - VisArea().Top();
                if ( pRect )
                {
                    tools::Rectangle aTmp( aTmpOldVis.SVRect() );
                    aTmp.SetLeft( pRect->Left() );
                    aTmp.SetRight( pRect->Right() );
                    GetWin()->Scroll( 0, lScroll, aTmp, ScrollFlags::Children);
                }
                else
                    GetWin()->Scroll( 0, lScroll, ScrollFlags::Children );
 
                const Point aTmpPt( -VisArea().Left(), -VisArea().Top() );
                MapMode aTmpMapMode( GetWin()->GetMapMode() );
                aTmpMapMode.SetOrigin( aTmpPt );
                GetWin()->SetMapMode( aTmpMapMode );
 
                if ( Imp()->HasDrawView() )
                    Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() );
 
                SetFirstVisPageInvalid();
                if ( !Imp()->m_bStopSmooth )
                {
                    const bool bScrollDirectionIsUp(lScroll > 0);
                    Imp()->m_aSmoothRect = VisArea();
 
                    if(bScrollDirectionIsUp)
                    {
                        Imp()->m_aSmoothRect.Bottom( VisArea().Top() + lScroll + aPixSz.Height());
                    }
                    else
                    {
                        Imp()->m_aSmoothRect.Top( VisArea().Bottom() + lScroll - aPixSz.Height());
                    }
 
                    Imp()->m_bSmoothUpdate = true;
                    GetWin()->PaintImmediately();
                    Imp()->m_bSmoothUpdate = false;
 
                    if(!Imp()->m_bStopSmooth)
                    {
                            // start paint on logic base
                            const tools::Rectangle aTargetLogic(Imp()->m_aSmoothRect.SVRect());
                            DLPrePaint2(vcl::Region(aTargetLogic));
 
                            // get target rectangle in discrete pixels
                            OutputDevice& rTargetDevice = mpTargetPaintWindow->GetTargetOutputDevice();
                            const tools::Rectangle aTargetPixel(rTargetDevice.LogicToPixel(aTargetLogic));
 
                            // get source top-left in discrete pixels
                            const Point aSourceTopLeft(pVout->LogicToPixel(aTargetLogic.TopLeft()));
 
                            // switch off MapModes
                            const bool bMapModeWasEnabledDest(rTargetDevice.IsMapModeEnabled());
                            const bool bMapModeWasEnabledSource(pVout->IsMapModeEnabled());
                            rTargetDevice.EnableMapMode(false);
                            pVout->EnableMapMode(false);
 
                            rTargetDevice.DrawOutDev(
                                aTargetPixel.TopLeft(), aTargetPixel.GetSize(), // dest
                                aSourceTopLeft, aTargetPixel.GetSize(), // source
                                *pVout);
 
                            // restore MapModes
                            rTargetDevice.EnableMapMode(bMapModeWasEnabledDest);
                            pVout->EnableMapMode(bMapModeWasEnabledSource);
 
                            // end paint on logoc base
                            DLPostPaint2(true);
                    }
                    else
                        --mnLockPaint;
                }
            }
            pVout.disposeAndClear();
            GetWin()->PaintImmediately();
            if ( !Imp()->m_bStopSmooth )
                --mnLockPaint;
            SetFirstVisPageInvalid();
            return true;
        }
        pVout.disposeAndClear();
    }
#endif
 
    maVisArea.Pos().AdjustX( -lXDiff );
    maVisArea.Pos().AdjustY( -lYDiff );
    if ( pRect )
        GetWin()->Scroll( lXDiff, lYDiff, *pRect, ScrollFlags::Children);
    else
        GetWin()->Scroll( lXDiff, lYDiff, ScrollFlags::Children);
    return false;
}
 
void SwViewShell::PaintDesktop(const vcl::RenderContext& rRenderContext, const SwRect &rRect)
{
    if ( !GetWin() && !GetOut()->GetConnectMetaFile() )
        return;                     //for the printer we don't do anything here.
 
    if(comphelper::LibreOfficeKit::isActive())
        return;
 
    //Catch exceptions, so that it doesn't look so surprising.
    //Can e.g. happen during Idle.
    //Unfortunately we must at any rate Paint the rectangles next to the pages,
    //as these are not painted at VisPortChgd.
    bool bBorderOnly = false;
    const SwRootFrame *pRoot = GetLayout();
    if ( rRect.Top() > pRoot->getFrameArea().Bottom() )
    {
        const SwFrame *pPg = pRoot->Lower();
        while ( pPg && pPg->GetNext() )
            pPg = pPg->GetNext();
        if ( !pPg || !pPg->getFrameArea().Overlaps( VisArea() ) )
            bBorderOnly = true;
    }
 
    const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode();
 
    SwRegionRects aRegion( rRect );
 
    //mod #i6193: remove sidebar area to avoid flickering
    const SwPostItMgr* pPostItMgr = GetPostItMgr();
    const SwTwips nSidebarWidth = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ?
                                  pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() :
                                  0;
 
    if ( bBorderOnly )
    {
        const SwFrame *pPage =pRoot->Lower();
        SwRect aLeft( rRect ), aRight( rRect );
        while ( pPage )
        {
            tools::Long nTmp = pPage->getFrameArea().Left();
            if ( nTmp < aLeft.Right() )
                aLeft.Right( nTmp );
            nTmp = pPage->getFrameArea().Right();
            if ( nTmp > aRight.Left() )
            {
                aRight.Left( nTmp + nSidebarWidth );
            }
            pPage = pPage->GetNext();
        }
        aRegion.clear();
        if ( aLeft.HasArea() )
            aRegion.push_back( aLeft );
        if ( aRight.HasArea() )
            aRegion.push_back( aRight );
    }
    else
    {
        const SwFrame *pPage = Imp()->GetFirstVisPage(&rRenderContext);
        const SwTwips nBottom = rRect.Bottom();
        while ( pPage && !aRegion.empty() &&
                (pPage->getFrameArea().Top() <= nBottom) )
        {
            SwRect aPageRect( pPage->getFrameArea() );
            if ( bBookMode )
            {
                const SwPageFrame& rFormatPage = static_cast<const SwPageFrame*>(pPage)->GetFormatPage();
                aPageRect.SSize( rFormatPage.getFrameArea().SSize() );
            }
 
            const bool bSidebarRight =
                static_cast<const SwPageFrame*>(pPage)->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
            aPageRect.Pos().AdjustX( -(bSidebarRight ? 0 : nSidebarWidth) );
            aPageRect.AddWidth(nSidebarWidth );
 
            if ( aPageRect.Overlaps( rRect ) )
                aRegion -= aPageRect;
 
            pPage = pPage->GetNext();
        }
    }
    if ( !aRegion.empty() )
        PaintDesktop_(aRegion);
}
 
// PaintDesktop is split in two, this part is also used by PreviewPage
void SwViewShell::PaintDesktop_(const SwRegionRects &rRegion)
{
    // OD 2004-04-23 #116347#
    GetOut()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
    GetOut()->SetLineColor();
 
    for ( auto &rRgn : rRegion )
    {
        const tools::Rectangle aRectangle(rRgn.SVRect());
 
        // #i93170#
        // Here we have a real Problem. On the one hand we have the buffering for paint
        // and overlay which needs an embracing pair of DLPrePaint2/DLPostPaint2 calls,
        // on the other hand the MapMode is not set correctly when this code is executed.
        // This is done in the users of this method, for each SWpage before painting it.
        // Since the MapMode is not correct here, the call to DLPostPaint2 will paint
        // existing FormControls due to the current MapMode.
 
        // There are basically three solutions for this:
 
        // (1) Set the MapMode correct, move the background painting to the users of
        //     this code
 
        // (2) Do no DLPrePaint2/DLPostPaint2 here; no SdrObjects are allowed to lie in
        //     the desktop region. Disadvantage: the desktop will not be part of the
        //     buffers, e.g. overlay. Thus, as soon as overlay will be used over the
        //     desktop, it will not work.
 
        // (3) expand DLPostPaint2 with a flag to signal if FormControl paints shall
        //     be done or not
 
        // Currently, (3) will be the best possible solution. It will keep overlay and
        // buffering intact and work without MapMode for single pages. In the medium
        // to long run, (1) will need to be used and the bool bPaintFormLayer needs
        // to be removed again
 
        // #i68597# inform Drawinglayer about display change
        DLPrePaint2(vcl::Region(aRectangle));
 
        // #i75172# needed to move line/Fill color setters into loop since DLPrePaint2
        // may exchange GetOut(), that's it's purpose. This happens e.g. at print preview.
        GetOut()->SetFillColor( GetViewOptions()->GetAppBackgroundColor());
        GetOut()->SetLineColor();
        GetOut()->DrawRect(aRectangle);
 
        DLPostPaint2(false);
    }
 
    GetOut()->Pop();
}
 
bool SwViewShell::CheckInvalidForPaint( const SwRect &rRect )
{
    if ( !GetWin() )
        return false;
 
    const SwPageFrame *pPage = Imp()->GetFirstVisPage(GetOut());
    const SwTwips nBottom = VisArea().Bottom();
    const SwTwips nRight  = VisArea().Right();
    bool bRet = false;
    while ( !bRet && pPage && ((pPage->getFrameArea().Top() <= nBottom) &&
                                (pPage->getFrameArea().Left() <= nRight)))
    {
        if ( pPage->IsInvalid() || pPage->IsInvalidFly() )
            bRet = true;
        pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
    }
 
    if ( bRet )
    {
        //Unfortunately Start/EndAction won't help here, as the Paint originated
        //from GUI and so Clipping has been set against getting through.
        //Ergo: do it all yourself (see ImplEndAction())
        if ( Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea())
             Imp()->DeletePaintRegion();
 
        SwLayAction aAction( GetLayout(), Imp() );
        aAction.SetComplete( false );
        // We increment the action counter to avoid a recursive call of actions
        // e.g. from a SwFEShell::RequestObjectResize(..) in bug 95829.
        // A recursive call of actions is no good idea because the inner action
        // can't format frames which are locked by the outer action. This may
        // cause and endless loop.
        ++mnStartAction;
        aAction.Action(GetWin()->GetOutDev());
        --mnStartAction;
 
        std::optional<SwRegionRects> oRegion = Imp()->TakePaintRegion();
        if ( oRegion && aAction.IsBrowseActionStop() )
        {
            //only of interest when something has changed in the visible range
            bool bAllNoOverlap = std::all_of(oRegion->begin(), oRegion->end(), [this](const SwRect &rTmp) {
                return rTmp.Overlaps( VisArea() );
            });
            if ( bAllNoOverlap )
                oRegion.reset();
        }
 
        if ( oRegion )
        {
            oRegion->LimitToOrigin();
            oRegion->Compress( SwRegionRects::CompressFuzzy );
            bRet = false;
            if ( !oRegion->empty() )
            {
                SwRegionRects aRegion( rRect );
                for ( const SwRect &rTmp : *oRegion )
                {
                    if ( !rRect.Contains( rTmp ) )
                    {
                        InvalidateWindows( rTmp );
                        if ( rTmp.Overlaps( VisArea() ) )
                        {   aRegion -= rTmp;
                            bRet = true;
                        }
                    }
                }
                if ( bRet )
                {
                    for ( size_t i = 0; i < aRegion.size(); ++i )
                        GetWin()->Invalidate( aRegion[i].SVRect() );
 
                    if ( rRect != VisArea() )
                    {
                        //rRect == VisArea is the special case for new or
                        //Shift-Ctrl-R, when it shouldn't be necessary to
                        //hold the rRect again in Document coordinates.
                        if ( maInvalidRect.IsEmpty() )
                            maInvalidRect = rRect;
                        else
                            maInvalidRect.Union( rRect );
                    }
                }
            }
            else
                bRet = false;
        }
        else
            bRet = false;
    }
    return bRet;
}
 
namespace
{
/// Similar to comphelper::FlagRestorationGuard, but for vcl::RenderContext.
class RenderContextGuard
{
    std::unique_ptr<SdrPaintWindow> m_TemporaryPaintWindow;
    SdrPageWindow* m_pPatchedPageWindow;
    SdrPaintWindow* m_pPreviousPaintWindow = nullptr;
 
public:
    RenderContextGuard(VclPtr<vcl::RenderContext>& pRef, vcl::RenderContext* pValue, SwViewShell* pShell)
        : m_pPatchedPageWindow(nullptr)
    {
        pRef = pValue;
 
        if (pValue == pShell->GetWin()->GetOutDev())
            return;
 
        SdrView* pDrawView(pShell->Imp()->GetDrawView());
 
        if (nullptr == pDrawView)
            return;
 
        SdrPageView* pSdrPageView(pDrawView->GetSdrPageView());
 
        if (nullptr != pSdrPageView)
        {
            m_pPatchedPageWindow = pSdrPageView->FindPageWindow(*pShell->GetWin()->GetOutDev());
 
            if (nullptr != m_pPatchedPageWindow)
            {
                m_TemporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, *pValue));
                m_pPreviousPaintWindow = m_pPatchedPageWindow->patchPaintWindow(*m_TemporaryPaintWindow);
            }
        }
    }
 
    ~RenderContextGuard()
    {
        if(nullptr != m_pPatchedPageWindow)
        {
            m_pPatchedPageWindow->unpatchPaintWindow(m_pPreviousPaintWindow);
        }
    }
};
}
 
void SwViewShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect)
{
    RenderContextGuard aGuard(mpOut, &rRenderContext, this);
    if ( mnLockPaint )
    {
        if ( Imp()->m_bSmoothUpdate )
        {
            SwRect aTmp( rRect );
            if ( !Imp()->m_aSmoothRect.Contains( aTmp ) )
                Imp()->m_bStopSmooth = true;
            else
            {
                Imp()->m_aSmoothRect = aTmp;
                return;
            }
        }
        else
            return;
    }
 
    if ( SwRootFrame::IsInPaint() )
    {
        //During the publication of a page at printing the Paint is buffered.
        SwPaintQueue::Add( this, SwRect( rRect ) );
        return;
    }
 
    //With !nStartAction I try to protect me against erroneous code at other places.
    //Hopefully it will not lead to problems!?
    if ( mbPaintWorks && !mnStartAction )
    {
        if( GetWin() && GetWin()->IsVisible() )
        {
            SwRect aRect( rRect );
            if ( mbPaintInProgress ) //Guard against double Paints!
            {
                GetWin()->Invalidate( rRect );
                return;
            }
 
            mbPaintInProgress = true;
            CurrShell aCurr( this );
            SwRootFrame::SetNoVirDev( true );
 
            //We don't want to Clip to and from, we trust that all are limited
            //to the rectangle and only need to calculate the clipping once.
            //The ClipRect is removed here once and not recovered, as externally
            //no one needs it anymore anyway.
            //Not when we paint a Metafile.
            if( !GetOut()->GetConnectMetaFile() && GetOut()->IsClipRegion())
                GetOut()->SetClipRegion();
 
            if ( IsPreview() )
            {
                //When useful, process or destroy the old InvalidRect.
                if ( aRect.Contains( maInvalidRect ) )
                    ResetInvalidRect();
                SwViewShell::sbLstAct = true;
                GetLayout()->PaintSwFrame( rRenderContext, aRect );
                SwViewShell::sbLstAct = false;
            }
            else
            {
                //When one of the visible pages still has anything entered for
                //Repaint, Repaint must be triggered.
                if ( !CheckInvalidForPaint( aRect ) )
                {
                    // --> OD 2009-08-12 #i101192#
                    // start Pre/PostPaint encapsulation to avoid screen blinking
                    const vcl::Region aRepaintRegion(aRect.SVRect());
                    DLPrePaint2(aRepaintRegion);
 
                    // <--
                    PaintDesktop(rRenderContext, aRect);
 
                    //When useful, process or destroy the old InvalidRect.
                    if ( aRect.Contains( maInvalidRect ) )
                        ResetInvalidRect();
                    SwViewShell::sbLstAct = true;
                    GetLayout()->PaintSwFrame( rRenderContext, aRect );
                    SwViewShell::sbLstAct = false;
                    // --> OD 2009-08-12 #i101192#
                    // end Pre/PostPaint encapsulation
                    DLPostPaint2(true);
                    // <--
                }
            }
            SwRootFrame::SetNoVirDev( false );
            mbPaintInProgress = false;
            UISizeNotify();
        }
    }
    else
    {
        if ( maInvalidRect.IsEmpty() )
            maInvalidRect = SwRect( rRect );
        else
            maInvalidRect.Union( SwRect( rRect ) );
 
        if ( mbInEndAction && GetWin() )
        {
            const vcl::Region aRegion(GetWin()->GetPaintRegion());
            RectangleVector aRectangles;
            aRegion.GetRegionRectangles(aRectangles);
 
            for(const auto& rRectangle : aRectangles)
            {
                Imp()->AddPaintRect(SwRect(rRectangle));
            }
 
            //RegionHandle hHdl( aRegion.BeginEnumRects() );
            //Rectangle aRect;
            //while ( aRegion.GetEnumRects( hHdl, aRect ) )
            //  Imp()->AddPaintRect( aRect );
            //aRegion.EndEnumRects( hHdl );
        }
        else if ( SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) &&
                  GetOut() == GetWin()->GetOutDev() )
        {
            // #i68597#
            const vcl::Region aDLRegion(rRect);
            DLPrePaint2(aDLRegion);
 
            rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
            rRenderContext.SetFillColor( Imp()->GetRetoucheColor() );
            rRenderContext.SetLineColor();
            rRenderContext.DrawRect( rRect );
            rRenderContext.Pop();
            // #i68597#
            DLPostPaint2(true);
        }
    }
}
 
void SwViewShell::PaintTile(VirtualDevice &rDevice, int contextWidth, int contextHeight, int tilePosX, int tilePosY, tools::Long tileWidth, tools::Long tileHeight)
{
    // SwViewShell's output device setup
    // TODO clean up SwViewShell's approach to output devices (the many of
    // them - mpBufferedOut, mpOut, mpWin, ...)
    OutputDevice *pSaveOut = mpOut;
    comphelper::LibreOfficeKit::setTiledPainting(true);
    mpOut = &rDevice;
 
    // resizes the virtual device so to contain the entries context
    rDevice.SetOutputSizePixel(Size(contextWidth, contextHeight));
 
    // setup the output device to draw the tile
    MapMode aMapMode(rDevice.GetMapMode());
    aMapMode.SetMapUnit(MapUnit::MapTwip);
    aMapMode.SetOrigin(Point(-tilePosX, -tilePosY));
 
    // Scaling. Must convert from pixels to twips. We know
    // that VirtualDevices use a DPI of 96.
    const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
    Fraction scaleX = Fraction(contextWidth, tileWidth) * scale;
    Fraction scaleY = Fraction(contextHeight, tileHeight) * scale;
    aMapMode.SetScaleX(scaleX);
    aMapMode.SetScaleY(scaleY);
    rDevice.SetMapMode(aMapMode);
 
    // Update scaling of SwEditWin and its sub-widgets, needed for comments.
    sal_uInt16 nOldZoomValue = 0;
    if (GetWin() && GetWin()->GetMapMode().GetScaleX() != scaleX)
    {
        double fScale = double(scaleX);
        SwViewOption aOption(*GetViewOptions());
        nOldZoomValue = aOption.GetZoom();
        aOption.SetZoom(fScale * 100);
        ApplyViewOptions(aOption);
        // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled.
        GetWin()->EnableMapMode(false);
    }
 
    tools::Rectangle aOutRect(Point(tilePosX, tilePosY),
                              rDevice.PixelToLogic(Size(contextWidth, contextHeight)));
 
    // Make the requested area visible -- we can't use MakeVisible as that will
    // only scroll the contents, but won't zoom/resize if needed.
    // Without this, items/text that are outside the visible area (in the SwView)
    // won't be painted when rendering tiles (at least when using either the
    // tiledrendering app, or the gtktiledviewer) -- although ultimately we
    // probably want to fix things so that the SwView's area doesn't affect
    // tiled rendering?
    VisPortChgd(SwRect(aOutRect));
 
    // Invoke SwLayAction if layout is not yet ready.
    CheckInvalidForPaint(SwRect(aOutRect));
 
    // draw - works in logic coordinates
    Paint(rDevice, aOutRect);
 
    SwPostItMgr* pPostItMgr = GetPostItMgr();
    if (GetViewOptions()->IsPostIts() && pPostItMgr)
        pPostItMgr->PaintTile(rDevice);
 
    // SwViewShell's output device tear down
 
    // A view shell can get a PaintTile call for a tile at a zoom level
    // different from the one, the related client really is.
    // In such a case it is better to reset the current scale value to
    // the original one, since such a value should be in synchronous with
    // the zoom level in the client (see setClientZoom).
    // At present the zoom value returned by GetViewOptions()->GetZoom() is
    // used in SwXTextDocument methods (postMouseEvent and setGraphicSelection)
    // for passing the correct mouse position to an edited chart (if any).
    if (nOldZoomValue !=0)
    {
        SwViewOption aOption(*GetViewOptions());
        aOption.SetZoom(nOldZoomValue);
        ApplyViewOptions(aOption);
 
        // Changing the zoom value doesn't always trigger the updating of
        // the client ole object area, so we call it directly.
        SfxInPlaceClient* pIPClient = GetSfxViewShell()->GetIPClient();
        if (pIPClient)
        {
            pIPClient->VisAreaChanged();
        }
        // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled.
        GetWin()->EnableMapMode(false);
    }
 
    mpOut = pSaveOut;
    comphelper::LibreOfficeKit::setTiledPainting(false);
}
 
void SwViewShell::SetBrowseBorder( const Size& rNew )
{
    if( rNew != maBrowseBorder )
    {
        maBrowseBorder = rNew;
        if ( maVisArea.HasArea() )
            InvalidateLayout( false );
    }
}
 
const Size& SwViewShell::GetBrowseBorder() const
{
    return maBrowseBorder;
}
 
sal_Int32 SwViewShell::GetBrowseWidth() const
{
    const SwPostItMgr* pPostItMgr = GetPostItMgr();
    if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
    {
        Size aBorder( maBrowseBorder );
        aBorder.AdjustWidth(maBrowseBorder.Width() );
        aBorder.AdjustWidth(pPostItMgr->GetSidebarWidth(true) + pPostItMgr->GetSidebarBorderWidth(true) );
        return maVisArea.Width() - GetOut()->PixelToLogic(aBorder).Width();
    }
    else
        return maVisArea.Width() - 2 * GetOut()->PixelToLogic(maBrowseBorder).Width();
}
 
void SwViewShell::InvalidateLayout( bool bSizeChanged )
{
    if ( !bSizeChanged && !GetViewOptions()->getBrowseMode() &&
         !GetViewOptions()->IsWhitespaceHidden() )
        return;
 
    CurrShell aCurr( this );
 
    OSL_ENSURE( GetLayout(), "Layout not ready" );
 
    // When the Layout doesn't have a height yet, nothing is formatted.
    // That leads to problems with Invalidate, e.g. when setting up a new View
    // the content is inserted and formatted (regardless of empty VisArea).
    // Therefore the pages must be roused for formatting.
    if( !GetLayout()->getFrameArea().Height() )
    {
        SwFrame* pPage = GetLayout()->Lower();
        while( pPage )
        {
            pPage->InvalidateSize_();
            pPage = pPage->GetNext();
        }
        return;
    }
 
    LockPaint(LockPaintReason::InvalidateLayout);
    StartAction();
 
    SwPageFrame *pPg = static_cast<SwPageFrame*>(GetLayout()->Lower());
    do
    {   pPg->InvalidateSize();
        pPg->InvalidatePrt_();
        pPg->InvaPercentLowers();
        if ( bSizeChanged )
        {
            pPg->PrepareHeader();
            pPg->PrepareFooter();
        }
        pPg = static_cast<SwPageFrame*>(pPg->GetNext());
    } while ( pPg );
 
    // When the size ratios in browse mode change,
    // the Position and PrtArea of the Content and Tab frames must be Invalidated.
    SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Pos;
    // In case of layout or mode change, the ContentFrames need a size-Invalidate
    // because of printer/screen formatting.
    if ( bSizeChanged )
        nInv |= SwInvalidateFlags::Size | SwInvalidateFlags::Direction;
 
    GetLayout()->InvalidateAllContent( nInv );
 
    SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(GetLayout()->Lower()) );
 
    EndAction();
    UnlockPaint();
}
 
SwRootFrame *SwViewShell::GetLayout() const
{
    return mpLayout.get();
}
 
vcl::RenderContext& SwViewShell::GetRefDev() const
{
    OutputDevice* pTmpOut = nullptr;
    if (  GetWin() &&
          GetViewOptions()->getBrowseMode() &&
         !GetViewOptions()->IsPrtFormat() )
        pTmpOut = GetWin()->GetOutDev();
    else
        pTmpOut = GetDoc()->getIDocumentDeviceAccess().getReferenceDevice( true );
 
    return *pTmpOut;
}
 
const SwNodes& SwViewShell::GetNodes() const
{
    return mxDoc->GetNodes();
}
 
void SwViewShell::DrawSelChanged()
{
}
 
Size SwViewShell::GetDocSize() const
{
    Size aSz;
    const SwRootFrame* pRoot = GetLayout();
    if( pRoot )
        aSz = pRoot->getFrameArea().SSize();
 
    return aSz;
}
 
SfxItemPool& SwViewShell::GetAttrPool()
{
    return GetDoc()->GetAttrPool();
}
 
void SwViewShell::ApplyViewOptions( const SwViewOption &rOpt )
{
    for(SwViewShell& rSh : GetRingContainer())
        rSh.StartAction();
 
    ImplApplyViewOptions( rOpt );
 
    // With one layout per view it is no longer necessary
    // to sync these "layout related" view options
    // But as long as we have to disable "multiple layout"
 
    for(SwViewShell& rSh : GetRingContainer())
    {
        if(&rSh == this)
            continue;
        SwViewOption aOpt( *rSh.GetViewOptions() );
        aOpt.SyncLayoutRelatedViewOptions(rOpt);
        if ( !(aOpt == *rSh.GetViewOptions()) )
            rSh.ImplApplyViewOptions( aOpt );
    }
    // End of disabled multiple window
 
    for(SwViewShell& rSh : GetRingContainer())
        rSh.EndAction();
}
 
static bool
IsCursorInFieldmarkHidden(SwPaM const& rCursor, sw::FieldmarkMode const eMode)
{
    if (eMode == sw::FieldmarkMode::ShowBoth)
    {
        return false;
    }
    IDocumentMarkAccess const& rIDMA(*rCursor.GetDoc().getIDocumentMarkAccess());
    // iterate, for nested fieldmarks
    for (auto iter = rIDMA.getFieldmarksBegin(); iter != rIDMA.getFieldmarksEnd(); ++iter)
    {
        if (*rCursor.GetPoint() <= (**iter).GetMarkStart())
        {
            return false;
        }
        if (*rCursor.GetPoint() < (**iter).GetMarkEnd())
        {
            SwPosition const sepPos(sw::mark::FindFieldSep(**iter));
            if (eMode == sw::FieldmarkMode::ShowResult)
            {
                if (*rCursor.GetPoint() <= sepPos
                    && *rCursor.GetPoint() != (**iter).GetMarkStart())
                {
                    return true;
                }
            }
            else
            {
                if (sepPos < *rCursor.GetPoint())
                {
                    return true;
                }
            }
        }
    }
    return false;
}
 
void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt )
{
    if (*mpOpt == rOpt)
        return;
 
    vcl::Window *pMyWin = GetWin();
    if( !pMyWin )
    {
        OSL_ENSURE( pMyWin, "SwViewShell::ApplyViewOptions: no window" );
        return;
    }
 
    CurrShell aCurr( this );
 
    bool bReformat   = false;
 
    if( mpOpt->IsShowHiddenField() != rOpt.IsShowHiddenField() )
    {
        static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))->
                                            SetHiddenFlag( !rOpt.IsShowHiddenField() );
        bReformat = true;
    }
    if ( mpOpt->IsShowHiddenPara() != rOpt.IsShowHiddenPara() )
    {
        SwHiddenParaFieldType* pFieldType = static_cast<SwHiddenParaFieldType*>(GetDoc()->
                                          getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenPara));
        if( pFieldType && pFieldType->HasWriterListeners() )
            pFieldType->PrintHiddenPara();
        bReformat = true;
    }
    if ( !bReformat && mpOpt->IsShowHiddenChar() != rOpt.IsShowHiddenChar() )
    {
        bReformat = GetDoc()->ContainsHiddenChars();
    }
    if ( mpOpt->IsShowChangesInMargin() != rOpt.IsShowChangesInMargin() ||
            mpOpt->IsShowChangesInMargin2() != rOpt.IsShowChangesInMargin2() )
    {
        if (rOpt.IsShowChangesInMargin())
            GetDoc()->GetDocumentRedlineManager().HideAll(
                   /*bDeletion=*/!rOpt.IsShowChangesInMargin2() );
        else
            GetDoc()->GetDocumentRedlineManager().ShowAll();
        GetLayout()->SetHideRedlines( false );
    }
 
    // bReformat becomes true, if ...
    // - fieldnames apply or not ...
    // ( - SwEndPortion must _no_ longer be generated. )
    // - Of course, the screen is something completely different than the printer ...
    bool const isToggleFieldNames(mpOpt->IsFieldName() != rOpt.IsFieldName());
 
    if (mpOpt->IsFieldName() != rOpt.IsFieldName()
        || mpOpt->IsParagraph() != rOpt.IsParagraph())
    {
        GetLayout()->SetFieldmarkMode( rOpt.IsFieldName()
                    ? sw::FieldmarkMode::ShowCommand
                    : sw::FieldmarkMode::ShowResult,
                rOpt.IsParagraph()
                    ? sw::ParagraphBreakMode::Shown
                    : sw::ParagraphBreakMode::Hidden);
        bReformat = true;
    }
 
    // The map mode is changed, minima/maxima will be attended by UI
    if( mpOpt->GetZoom() != rOpt.GetZoom() && !IsPreview() )
    {
        MapMode aMode( pMyWin->GetMapMode() );
        Fraction aNewFactor( rOpt.GetZoom(), 100 );
        aMode.SetScaleX( aNewFactor );
        aMode.SetScaleY( aNewFactor );
        pMyWin->SetMapMode( aMode );
        // if not a reference device (printer) is used for formatting,
        // but the screen, new formatting is needed for zoomfactor changes.
        if (mpOpt->getBrowseMode() || mpOpt->IsWhitespaceHidden())
            bReformat = true;
    }
 
    bool bBrowseModeChanged = false;
    if( mpOpt->getBrowseMode() != rOpt.getBrowseMode() )
    {
        bBrowseModeChanged = true;
        bReformat = true;
    }
    else if( mpOpt->getBrowseMode() && mpOpt->IsPrtFormat() != rOpt.IsPrtFormat() )
        bReformat = true;
 
    bool bHideWhitespaceModeChanged = false;
    if (mpOpt->IsWhitespaceHidden() != rOpt.IsWhitespaceHidden())
    {
        // When whitespace is hidden, view change needs reformatting.
        bHideWhitespaceModeChanged = true;
        bReformat = true;
    }
 
    if ( HasDrawView() || rOpt.IsGridVisible() )
    {
        if ( !HasDrawView() )
            MakeDrawView();
 
        SwDrawView *pDView = Imp()->GetDrawView();
        if ( pDView->IsDragStripes() != rOpt.IsCrossHair() )
            pDView->SetDragStripes( rOpt.IsCrossHair() );
 
        if ( pDView->IsGridSnap() != rOpt.IsSnap() )
            pDView->SetGridSnap( rOpt.IsSnap() );
 
        if ( pDView->IsGridVisible() != rOpt.IsGridVisible() )
            pDView->SetGridVisible( rOpt.IsGridVisible() );
 
        const Size &rSz = rOpt.GetSnapSize();
        pDView->SetGridCoarse( rSz );
 
        const Size aFSize
            ( rSz.Width() ? rSz.Width() / (rOpt.GetDivisionX()+1) : 0,
              rSz.Height()? rSz.Height()/ (rOpt.GetDivisionY()+1) : 0);
        pDView->SetGridFine( aFSize );
        Fraction aSnGrWdtX(rSz.Width(), rOpt.GetDivisionX() + 1);
        Fraction aSnGrWdtY(rSz.Height(), rOpt.GetDivisionY() + 1);
        pDView->SetSnapGridWidth( aSnGrWdtX, aSnGrWdtY );
 
        // set handle size to 9 pixels, always
        pDView->SetMarkHdlSizePixel(9);
    }
 
    bool bOnlineSpellChgd = mpOpt->IsOnlineSpell() != rOpt.IsOnlineSpell();
 
    *mpOpt = rOpt;   // First the options are taken.
    mpOpt->SetUIOptions(rOpt);
 
    mxDoc->GetDocumentSettingManager().set(DocumentSettingId::HTML_MODE, 0 != ::GetHtmlMode(mxDoc->GetDocShell()));
 
    if( bBrowseModeChanged || bHideWhitespaceModeChanged )
    {
        // #i44963# Good occasion to check if page sizes in
        // page descriptions are still set to (LONG_MAX, LONG_MAX) (html import)
        mxDoc->CheckDefaultPageFormat();
        InvalidateLayout( true );
    }
 
    SwXTextDocument* pModel = comphelper::getFromUnoTunnel<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument());
    SfxLokHelper::notifyViewRenderState(GetSfxViewShell(), pModel);
 
    pMyWin->Invalidate();
    if ( bReformat )
    {
        // Nothing helps, we need to send all ContentFrames a
        // Prepare, we format anew:
        StartAction();
        Reformat();
        EndAction();
    }
 
    if (isToggleFieldNames)
    {
        for(SwViewShell& rSh : GetRingContainer())
        {
            if (SwCursorShell *const pSh = dynamic_cast<SwCursorShell *>(&rSh))
            {
                if ((mpOpt->IsFieldName() && pSh->CursorInsideInputField())
                    || IsCursorInFieldmarkHidden(*pSh->GetCursor(),
                            pSh->GetLayout()->GetFieldmarkMode()))
                {   // move cursor out of field
                    pSh->Left(1, SwCursorSkipMode::Chars);
                }
            }
        }
        // the layout changes but SetModified() wasn't called so do it here:
        mxDoc->GetDocumentLayoutManager().ClearSwLayouterEntries();
    }
 
    if( !bOnlineSpellChgd )
        return;
 
    if ( !comphelper::LibreOfficeKit::isActive() )
    {
        bool bOnlineSpl = rOpt.IsOnlineSpell();
        for(SwViewShell& rSh : GetRingContainer())
        {
            if(&rSh == this)
                continue;
            rSh.mpOpt->SetOnlineSpell( bOnlineSpl );
            vcl::Window *pTmpWin = rSh.GetWin();
            if( pTmpWin )
                pTmpWin->Invalidate();
        }
    }
}
 
void SwViewShell::SetUIOptions( const SwViewOption &rOpt )
{
    mpOpt->SetUIOptions(rOpt);
    //the API-Flag of the view options is set but never reset
    //it is required to set scroll bars in readonly documents
    if(rOpt.IsStarOneSetting())
        mpOpt->SetStarOneSetting(true);
 
    mpOpt->SetSymbolFont(rOpt.GetSymbolFont());
}
 
void SwViewShell::SetReadonlyOption(bool bSet)
{
    //JP 01.02.99: at readonly flag query properly
    //              and if need be format; Bug 61335
 
    // Are we switching from readonly to edit?
    if( bSet == mpOpt->IsReadonly() )
        return;
 
    // so that the flags can be queried properly.
    mpOpt->SetReadonly( false );
 
    bool bReformat = mpOpt->IsFieldName();
 
    mpOpt->SetReadonly( bSet );
 
    if( bReformat )
    {
        StartAction();
        Reformat();
        if ( GetWin() && !comphelper::LibreOfficeKit::isActive() )
            GetWin()->Invalidate();
        EndAction();
    }
    else if ( GetWin() && !comphelper::LibreOfficeKit::isActive() )
        GetWin()->Invalidate();
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
    if( Imp()->IsAccessible() )
        Imp()->InvalidateAccessibleEditableState( false );
#endif
}
 
void  SwViewShell::SetPDFExportOption(bool bSet)
{
    if( bSet != mpOpt->IsPDFExport() )
    {
        if( bSet && mpOpt->getBrowseMode() )
            mpOpt->SetPrtFormat( true );
        mpOpt->SetPDFExport(bSet);
    }
}
 
void  SwViewShell::SetReadonlySelectionOption(bool bSet)
{
    if( bSet != mpOpt->IsSelectionInReadonly() )
    {
        mpOpt->SetSelectionInReadonly(bSet);
    }
}
 
void SwViewShell::SetPrtFormatOption( bool bSet )
{
    mpOpt->SetPrtFormat( bSet );
}
 
void SwViewShell::UISizeNotify()
{
    if ( mbDocSizeChgd )
    {
        mbDocSizeChgd = false;
        bool bOld = bInSizeNotify;
        bInSizeNotify = true;
        ::SizeNotify( this, GetDocSize() );
        bInSizeNotify = bOld;
    }
}
 
void    SwViewShell::SetRestoreActions(sal_uInt16 nSet)
{
    OSL_ENSURE(!GetRestoreActions()||!nSet, "multiple restore of the Actions ?");
    Imp()->SetRestoreActions(nSet);
}
sal_uInt16  SwViewShell::GetRestoreActions() const
{
    return Imp()->GetRestoreActions();
}
 
bool SwViewShell::IsNewLayout() const
{
    return GetLayout()->IsNewLayout();
}
 
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
uno::Reference< css::accessibility::XAccessible > SwViewShell::CreateAccessible()
{
    uno::Reference< css::accessibility::XAccessible > xAcc;
 
    // We require a layout and an XModel to be accessible.
    OSL_ENSURE( mpLayout, "no layout, no access" );
    OSL_ENSURE( GetWin(), "no window, no access" );
 
    if( mxDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && GetWin() )
        xAcc = Imp()->GetAccessibleMap().GetDocumentView();
 
    return xAcc;
}
 
uno::Reference< css::accessibility::XAccessible > SwViewShell::CreateAccessiblePreview()
{
    OSL_ENSURE( IsPreview(),
                "Can't create accessible preview for non-preview SwViewShell" );
 
    // We require a layout and an XModel to be accessible.
    OSL_ENSURE( mpLayout, "no layout, no access" );
    OSL_ENSURE( GetWin(), "no window, no access" );
 
    if ( IsPreview() && GetLayout()&& GetWin() )
    {
        return Imp()->GetAccessibleMap().GetDocumentPreview(
                    PagePreviewLayout()->maPreviewPages,
                    GetWin()->GetMapMode().GetScaleX(),
                    GetLayout()->GetPageByPageNum( PagePreviewLayout()->mnSelectedPageNum ),
                    PagePreviewLayout()->maWinSize );
    }
    return nullptr;
}
 
void SwViewShell::InvalidateAccessibleFocus()
{
    if( Imp() && Imp()->IsAccessible() )
        Imp()->GetAccessibleMap().InvalidateFocus();
}
 
/**
 * invalidate CONTENT_FLOWS_FROM/_TO relation for paragraphs #i27138#
 */
void SwViewShell::InvalidateAccessibleParaFlowRelation( const SwTextFrame* _pFromTextFrame,
                                                      const SwTextFrame* _pToTextFrame )
{
    if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
    {
        Imp()->InvalidateAccessibleParaFlowRelation_( _pFromTextFrame, _pToTextFrame );
    }
}
 
/**
 * invalidate text selection for paragraphs #i27301#
 */
void SwViewShell::InvalidateAccessibleParaTextSelection()
{
    if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
    {
        Imp()->InvalidateAccessibleParaTextSelection_();
    }
}
 
/**
 * invalidate attributes for paragraphs #i88069#
 */
void SwViewShell::InvalidateAccessibleParaAttrs( const SwTextFrame& rTextFrame )
{
    if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
    {
        Imp()->InvalidateAccessibleParaAttrs_( rTextFrame );
    }
}
 
SwAccessibleMap* SwViewShell::GetAccessibleMap()
{
    if ( Imp()->IsAccessible() )
    {
        return &(Imp()->GetAccessibleMap());
    }
 
    return nullptr;
}
 
void SwViewShell::ApplyAccessibilityOptions()
{
    if (comphelper::IsFuzzing())
        return;
    if (mpOpt->IsPagePreview() && !officecfg::Office::Common::Accessibility::IsForPagePreviews::get())
    {
        mpAccOptions->SetAlwaysAutoColor(false);
        mpAccOptions->SetStopAnimatedGraphics(false);
    }
    else
    {
        mpAccOptions->SetAlwaysAutoColor(officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get());
        // tdf#161765: Let user choose which animation settings to use: OS's / LO's
        // New options: "System"/"No"/"Yes".
        // Do respect OS's animation setting if the user has selected the option "System"
        mpAccOptions->SetStopAnimatedGraphics(! MiscSettings::IsAnimatedGraphicAllowed());
 
        // Form view
        // Always set this option, not only if document is read-only:
        mpOpt->SetSelectionInReadonly(officecfg::Office::Common::Accessibility::IsSelectionInReadonly::get());
    }
}
#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
 
ShellResource* SwViewShell::GetShellRes()
{
    return spShellRes;
}
 
//static
void SwViewShell::SetCareDialog(const std::shared_ptr<weld::Window>& rNew)
{
    auto& spCareDialog = getCareDialog();
    (*spCareDialog.get()) = rNew;
}
 
//static
weld::Window* SwViewShell::GetCareDialog(SwViewShell const & rVSh)
{
    auto& spCareDialog = getCareDialog();
    return (*spCareDialog.get()) ? spCareDialog.get()->get() : CareChildWin(rVSh);
}
 
sal_uInt16 SwViewShell::GetPageCount() const
{
    return GetLayout() ? GetLayout()->GetPageNum() : 1;
}
 
Size SwViewShell::GetPageSize( sal_uInt16 nPageNum, bool bSkipEmptyPages ) const
{
    Size aSize;
    const SwRootFrame* pTmpRoot = GetLayout();
    if( pTmpRoot && nPageNum )
    {
        const SwPageFrame* pPage = static_cast<const SwPageFrame*>
                                 (pTmpRoot->Lower());
 
        while( --nPageNum && pPage->GetNext() )
            pPage = static_cast<const SwPageFrame*>( pPage->GetNext() );
 
        if( !bSkipEmptyPages && pPage->IsEmptyPage() && pPage->GetNext() )
            pPage = static_cast<const SwPageFrame*>( pPage->GetNext() );
 
        aSize = pPage->getFrameArea().SSize();
    }
    return aSize;
}
 
void SwViewShell::OnGraphicArrived(const SwRect& rRect)
{
    for(SwViewShell& rShell : GetRingContainer())
    {
        CurrShell aCurr(&rShell);
        if(rShell.IsPreview())
        {
            if(rShell.GetWin())
                ::RepaintPagePreview(&rShell, rRect);
        }
        else if(rShell.VisArea().Overlaps(rRect) && OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType())
        {
            // invalidate instead of painting
            rShell.GetWin()->Invalidate(rRect.SVRect());
        }
    }
}
// #i12836# enhanced pdf export
sal_Int32 SwViewShell::GetPageNumAndSetOffsetForPDF( OutputDevice& rOut, const SwRect& rRect ) const
{
    OSL_ENSURE( GetLayout(), "GetPageNumAndSetOffsetForPDF assumes presence of layout" );
 
    sal_Int32 nRet = -1;
 
    // #i40059# Position out of bounds:
    SwRect aRect( rRect );
    aRect.Pos().setX( std::max( aRect.Left(), GetLayout()->getFrameArea().Left() ) );
 
    const SwPageFrame* pPage = GetLayout()->GetPageAtPos( aRect.Center() );
    if ( pPage )
    {
        OSL_ENSURE( pPage, "GetPageNumAndSetOffsetForPDF: No page found" );
 
        Point aOffset( pPage->getFrameArea().Pos() );
        aOffset.setX( -aOffset.X() );
        aOffset.setY( -aOffset.Y() );
 
        MapMode aMapMode( rOut.GetMapMode() );
        aMapMode.SetOrigin( aOffset );
        rOut.SetMapMode( aMapMode );
 
        nRet = pPage->GetPhyPageNum() - 1;
    }
 
    return nRet;
}
 
// --> PB 2007-05-30 #146850#
const BitmapEx& SwViewShell::GetReplacementBitmap( bool bIsErrorState )
{
    if (bIsErrorState)
    {
        if (!m_xErrorBmp)
            m_xErrorBmp.reset(new BitmapEx(RID_GRAPHIC_ERRORBMP));
        return *m_xErrorBmp;
    }
 
    if (!m_xReplaceBmp)
        m_xReplaceBmp.reset(new BitmapEx(RID_GRAPHIC_REPLACEBMP));
    return *m_xReplaceBmp;
}
 
void SwViewShell::DeleteReplacementBitmaps()
{
    m_xErrorBmp.reset();
    m_xReplaceBmp.reset();
}
 
SwPostItMgr* SwViewShell::GetPostItMgr()
{
    SwView* pView =  GetDoc()->GetDocShell() ? GetDoc()->GetDocShell()->GetView() : nullptr;
    if ( pView )
        return pView->GetPostItMgr();
 
    return nullptr;
}
 
void SwViewShell::GetFirstLastVisPageNumbers(SwVisiblePageNumbers& rVisiblePageNumbers, SwView& rView)
{
    SwRect rViewVisArea(rView.GetVisArea());
    vcl::RenderContext* pRenderContext = GetOut();
    const SwPageFrame* pPageFrame = Imp()->GetFirstVisPage(pRenderContext);
    SwRect rPageRect = pPageFrame->getFrameArea();
    rPageRect.AddBottom(-pPageFrame->GetBottomMargin());
    while (!rPageRect.Overlaps(rViewVisArea) && pPageFrame->GetNext())
    {
        pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
        rPageRect = pPageFrame->getFrameArea();
        if (rPageRect.Top() > 0)
            rPageRect.AddBottom(-pPageFrame->GetBottomMargin());
    }
    rVisiblePageNumbers.nFirstPhy = pPageFrame->GetPhyPageNum();
    rVisiblePageNumbers.nFirstVirt = pPageFrame->GetVirtPageNum();
    const SvxNumberType& rFirstVisNum = pPageFrame->GetPageDesc()->GetNumType();
    rVisiblePageNumbers.sFirstCustomPhy = rFirstVisNum.GetNumStr(rVisiblePageNumbers.nFirstPhy);
    rVisiblePageNumbers.sFirstCustomVirt = rFirstVisNum.GetNumStr(rVisiblePageNumbers.nFirstVirt);
    pPageFrame = Imp()->GetLastVisPage(pRenderContext);
    rPageRect = pPageFrame->getFrameArea();
    rPageRect.AddTop(pPageFrame->GetTopMargin());
    while (!rPageRect.Overlaps(rViewVisArea) && pPageFrame->GetPrev())
    {
        pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetPrev());
        rPageRect = pPageFrame->getFrameArea();
        rPageRect.AddTop(pPageFrame->GetTopMargin());
    }
    rVisiblePageNumbers.nLastPhy = pPageFrame->GetPhyPageNum();
    rVisiblePageNumbers.nLastVirt = pPageFrame->GetVirtPageNum();
    const SvxNumberType& rLastVisNum = pPageFrame->GetPageDesc()->GetNumType();
    rVisiblePageNumbers.sLastCustomPhy = rLastVisNum.GetNumStr(rVisiblePageNumbers.nLastPhy);
    rVisiblePageNumbers.sLastCustomVirt = rLastVisNum.GetNumStr(rVisiblePageNumbers.nLastVirt);
}
 
/*
 * Document Interface Access
 */
const IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() const { return mxDoc->GetDocumentSettingManager(); }
IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() { return mxDoc->GetDocumentSettingManager(); }
const IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() const { return mxDoc->getIDocumentDeviceAccess(); }
IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() { return mxDoc->getIDocumentDeviceAccess(); }
const IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() const { return mxDoc->getIDocumentMarkAccess(); }
IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() { return mxDoc->getIDocumentMarkAccess(); }
const IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() const { return mxDoc->getIDocumentDrawModelAccess(); }
IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() { return mxDoc->getIDocumentDrawModelAccess(); }
const IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() const { return mxDoc->getIDocumentRedlineAccess(); }
IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() { return mxDoc->getIDocumentRedlineAccess(); }
const IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() const { return mxDoc->getIDocumentLayoutAccess(); }
IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() { return mxDoc->getIDocumentLayoutAccess(); }
IDocumentContentOperations& SwViewShell::getIDocumentContentOperations() { return mxDoc->getIDocumentContentOperations(); }
IDocumentStylePoolAccess& SwViewShell::getIDocumentStylePoolAccess() { return mxDoc->getIDocumentStylePoolAccess(); }
const IDocumentStatistics& SwViewShell::getIDocumentStatistics() const { return mxDoc->getIDocumentStatistics(); }
 
IDocumentUndoRedo      & SwViewShell::GetIDocumentUndoRedo()
{ return mxDoc->GetIDocumentUndoRedo(); }
IDocumentUndoRedo const& SwViewShell::GetIDocumentUndoRedo() const
{ return mxDoc->GetIDocumentUndoRedo(); }
 
// --> OD 2007-11-14 #i83479#
const IDocumentListItems* SwViewShell::getIDocumentListItemsAccess() const
{
    return &mxDoc->getIDocumentListItems();
}
 
const IDocumentOutlineNodes* SwViewShell::getIDocumentOutlineNodesAccess() const
{
    return &mxDoc->getIDocumentOutlineNodes();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'Union' is required to be utilized.

V530 The return value of function 'Union' is required to be utilized.

V530 The return value of function 'Union' is required to be utilized.

V547 Expression '!rSh.GetWin()' is always false.