diff options
Diffstat (limited to 'sw/source/core/view/viewsh.cxx')
-rw-r--r-- | sw/source/core/view/viewsh.cxx | 2619 |
1 files changed, 2619 insertions, 0 deletions
diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx new file mode 100644 index 000000000..ef26275ab --- /dev/null +++ b/sw/source/core/view/viewsh.cxx @@ -0,0 +1,2619 @@ +/* -*- 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 <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 <cntfrm.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 <pagedesc.hxx> +#include <ndole.hxx> +#include <ndindex.hxx> +#include <accmap.hxx> +#include <vcl/bitmapex.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <accessibilityoptions.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <pagepreviewlayout.hxx> +#include <sortedobjs.hxx> +#include <anchoredobject.hxx> +#include <DocumentSettingManager.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/sdr/overlay/overlaymanager.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/svdpagv.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +#if !HAVE_FEATURE_DESKTOP +#include <vcl/sysdata.hxx> +#endif + +bool SwViewShell::mbLstAct = false; +ShellResource *SwViewShell::mpShellRes = nullptr; +vcl::DeleteOnDeinit<std::shared_ptr<weld::Window>> SwViewShell::mpCareDialog(new std::shared_ptr<weld::Window>); + +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; + } + + // Repaint everything + GetWin()->Invalidate(); +} + +void SwViewShell::setOutputToWindow(bool bOutputToWindow) +{ + mbOutputToWindow = bOutputToWindow; +} + +bool SwViewShell::isOutputToWindow() const +{ + return mbOutputToWindow; +} + +void SwViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + xmlTextWriterStartElement(pWriter, BAD_CAST("SwViewShell")); + 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(): GetOut(); + + // #i74769# use SdrPaintWindow now direct + mpTargetPaintWindow = Imp()->GetDrawView()->BeginDrawLayers(mpPrePostOutDev, rRegion); + OSL_ENSURE(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 (IsPreview() && Imp()->IsAccessible()) + { + Imp()->FireAccessibleEvents(); + } + return; + } + + mbInEndAction = true; + //will this put the EndAction of the last shell in the sequence? + + SwViewShell::mbLstAct = true; + for(SwViewShell& rShell : GetRingContainer()) + { + if(&rShell != this && rShell.ActionPend()) + { + SwViewShell::mbLstAct = false; + break; + } + } + + const bool bIsShellForCheckViewLayout = ( this == GetLayout()->GetCurrShell() ); + + SET_CURR_SHELL( this ); + if ( Imp()->HasDrawView() && !Imp()->GetDrawView()->areMarkHandlesHidden() ) + Imp()->StartAction(); + + if ( Imp()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea() ) + Imp()->DelRegion(); + + const bool bExtraData = ::IsExtraData( GetDoc() ); + + if ( !bIdleEnd ) + { + SwLayAction aAction( GetLayout(), Imp() ); + aAction.SetComplete( false ); + if ( mnLockPaint ) + aAction.SetPaint( false ); + aAction.Action(GetWin()); + } + + 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()->GetRegion() || + 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::unique_ptr<SwRegionRects> pRegion = std::move(Imp()->m_pRegion); + + //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 = pRegion && dynamic_cast<const SwCursorShell*>(this) != nullptr; + if( bShowCursor ) + static_cast<SwCursorShell*>(this)->HideCursors(); + + if ( pRegion ) + { + SwRootFrame* pCurrentLayout = GetLayout(); + + //First Invert then Compress, never the other way round! + pRegion->Invert(); + + pRegion->Compress(); + + ScopedVclPtr<VirtualDevice> pVout; + while ( !pRegion->empty() ) + { + SwRect aRect( pRegion->back() ); + pRegion->pop_back(); + + bool bPaint = true; + if ( IsEndActionByVirDev() ) + { + //create virtual device and set. + if ( !pVout ) + pVout = VclPtr<VirtualDevice>::Create( *GetOut() ); + MapMode aMapMode( GetOut()->GetMapMode() ); + pVout->SetMapMode( aMapMode ); + + bool bSizeOK = true; + + tools::Rectangle aTmp1( aRect.SVRect() ); + aTmp1 = GetOut()->LogicToPixel( aTmp1 ); + tools::Rectangle aTmp2( GetOut()->PixelToLogic( aTmp1 ) ); + if ( aTmp2.Left() > aRect.Left() ) + aTmp1.SetLeft( std::max( 0L, aTmp1.Left() - 1 ) ); + if ( aTmp2.Top() > aRect.Top() ) + aTmp1.SetTop( std::max( 0L, aTmp1.Top() - 1 ) ); + aTmp1.AdjustRight(1 ); + aTmp1.AdjustBottom(1 ); + aTmp1 = GetOut()->PixelToLogic( aTmp1 ); + aRect = SwRect( aTmp1 ); + + const Size aTmp( pVout->GetOutputSize() ); + if ( aTmp.Height() < aRect.Height() || + aTmp.Width() < aRect.Width() ) + { + bSizeOK = pVout->SetOutputSize( aRect.SSize() ); + } + if ( bSizeOK ) + { + bPaint = false; + + // --> OD 2007-07-26 #i79947# + // #i72754# start Pre/PostPaint encapsulation before mpOut is changed to the buffering VDev + const vcl::Region aRepaintRegion(aRect.SVRect()); + DLPrePaint2(aRepaintRegion); + // <-- + + OutputDevice *pOld = GetOut(); + pVout->SetLineColor( pOld->GetLineColor() ); + pVout->SetFillColor( pOld->GetFillColor() ); + Point aOrigin( aRect.Pos() ); + aOrigin.setX( -aOrigin.X() ); aOrigin.setY( -aOrigin.Y() ); + aMapMode.SetOrigin( aOrigin ); + pVout->SetMapMode( aMapMode ); + + mpOut = pVout.get(); + if ( bPaintsFromSystem ) + PaintDesktop(*mpOut, aRect); + pCurrentLayout->PaintSwFrame( *mpOut, aRect ); + pOld->DrawOutDev( aRect.Pos(), aRect.SSize(), + aRect.Pos(), aRect.SSize(), *pVout ); + mpOut = pOld; + + // #i72754# end Pre/PostPaint encapsulation when mpOut is back and content is painted + DLPostPaint2(true); + } + } + if ( bPaint ) + { + if (GetWin()->SupportsDoubleBuffering()) + InvalidateWindows(aRect.SVRect()); + 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 BeginDarwLayers() defines the clip region for DrawingLayer paint, + // transparent objects in the single rectangles will indeed be painted multiple times. + DLPrePaint2(vcl::Region(aRect.SVRect())); + + if ( bPaintsFromSystem ) + PaintDesktop(*GetOut(), aRect); + if (!comphelper::LibreOfficeKit::isActive()) + pCurrentLayout->PaintSwFrame( *mpOut, aRect ); + else + pCurrentLayout->GetCurrShell()->InvalidateWindows(aRect.SVRect()); + + // #i75172# end DrawingLayer paint + DLPostPaint2(true); + } + } + + lcl_PaintTransparentFormControls(*this, aRect); // i#107365 + } + } + if( bShowCursor ) + static_cast<SwCursorShell*>(this)->ShowCursors( true ); + } + else + { + Imp()->DelRegion(); + mbPaintWorks = true; + } + } + else + mbPaintWorks = true; + + mbInEndAction = false; + SwViewShell::mbLstAct = 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( Imp()->IsAccessible() ) + Imp()->FireAccessibleEvents(); +} + +void SwViewShell::ImplStartAction() +{ + mbPaintWorks = false; + Imp()->StartAction(); +} + +void SwViewShell::ImplLockPaint() +{ + if ( GetWin() && GetWin()->IsVisible() ) + GetWin()->EnablePaint( false ); //Also cut off the controls. + Imp()->LockPaint(); +} + +void SwViewShell::ImplUnlockPaint( bool bVirDev ) +{ + SET_CURR_SHELL( this ); + if ( GetWin() && GetWin()->IsVisible() ) + { + if ( (bInSizeNotify || bVirDev ) && VisArea().HasArea() ) + { + //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 ); + GetWin()->Invalidate( InvalidateFlags::Children ); + } + pVout.disposeAndClear(); + } + else + { + Imp()->UnlockPaint(); + GetWin()->EnablePaint( true ); + GetWin()->Invalidate( InvalidateFlags::Children ); + } + } + else + Imp()->UnlockPaint(); +} + +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() ) + { + 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().IsOver( rRect ) || comphelper::LibreOfficeKit::isActive() ) + rSh.GetWin()->Invalidate( rRect.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().IsInside( rRect ) || IsScrollMDI( this, rRect ) || GetCareDialog(*this) ) + { + if ( !IsViewLocked() ) + { + if( mpWin ) + { + const SwFrame* pRoot = GetLayout(); + int nLoopCnt = 3; + 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* pVFrame = rVSh.mpSfxViewShell->GetViewFrame(); + SfxChildWindow* pChWin = pVFrame->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) +{ + SET_CURR_SHELL( this ); + + bool bCursor = dynamic_cast<const SwCursorShell*>( this ) != nullptr; + if ( bCursor ) + static_cast<SwCursorShell*>(this)->StartAction(); + else + StartAction(); + + GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB); + + if ( bCursor ) + static_cast<SwCursorShell*>(this)->EndAction(); + else + EndAction(); +} + +/** update all charts for which any table exists */ +void SwViewShell::UpdateAllCharts() +{ + SET_CURR_SHELL( 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; + } + + SET_CURR_SHELL( 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 ) +{ + bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr; + if ( bCursor ) + static_cast<SwCursorShell&>(rSh).StartAction(); + else + rSh.StartAction(); + rSh.GetLayout()->InvalidateAllContent( nInv ); + if ( bCursor ) + static_cast<SwCursorShell&>(rSh).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 ) +{ + const bool bIsCursorShell = dynamic_cast<const SwCursorShell*>( &_rSh) != nullptr; + if ( bIsCursorShell ) + static_cast<SwCursorShell&>(_rSh).StartAction(); + else + _rSh.StartAction(); + + _rSh.GetLayout()->InvalidateAllObjPos(); + + if ( bIsCursorShell ) + static_cast<SwCursorShell&>(_rSh).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 ); + } +} + +void SwViewShell::SetUseVirDev( bool bNewVirtual ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::USE_VIRTUAL_DEVICE) != bNewVirtual ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + // this sets the flag at the document and calls PrtDataChanged + IDocumentDeviceAccess& rIDDA = getIDocumentDeviceAccess(); + rIDDA.setReferenceDeviceType( bNewVirtual, true ); + } +} + +/** 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) + { + 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->ModifyNotification(nullptr, nullptr); + } + } + EndAction(); + } +} + +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); + + SET_CURR_SHELL( 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 ) + { + 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 ); + + SwDocPosUpdate aMsgHint( 0 ); + GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint ); + 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 ) + { + const SwFrame *pCnt = static_cast<SwCursorShell*>(this)->GetCurrFrame( false ); + const SwPageFrame *pPage; + if ( pCnt && nullptr != (pPage = pCnt->FindPageFrame()) ) + { + const sal_uInt16 nVirtNum = pPage->GetVirtPageNum(); + const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType(); + OUString sDisplay = rNum.GetNumStr( nVirtNum ); + PageNumNotify( this, pCnt->GetPhyPageNum(), nVirtNum, sDisplay ); + + if (comphelper::LibreOfficeKit::isActive()) + { + Size aDocSize = GetDocSize(); + std::stringstream ss; + ss << aDocSize.Width() + 2 * DOCUMENTBORDER << ", " << aDocSize.Height() + 2 * DOCUMENTBORDER; + OString sSize = ss.str().c_str(); + + SwXTextDocument* pModel = comphelper::getUnoTunnelImplementation<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument()); + SfxLokHelper::notifyDocumentSizeChanged(GetSfxViewShell(), sSize, 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()); + + 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()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea() ) + Imp()->DelRegion(); + + SET_CURR_SHELL( this ); + + bool bScrolled = false; + + SwPostItMgr* pPostItMgr = GetPostItMgr(); + + if ( bFull ) + GetWin()->Invalidate(); + else + { + //Calculate amount to be scrolled. + const long nXDiff = aPrevArea.Left() - VisArea().Left(); + const 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()) ); + if ( bBookMode ) + { + const SwPageFrame& rFormatPage = pPage->GetFormatPage(); + aPageRect.SSize( rFormatPage.GetBoundRect(GetWin()).SSize() ); + } + + // #i9719# - consider new border and shadow width + if ( aPageRect.IsOver( 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 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 &rBound = pObj->GetObjRect().SVRect(); + if (rBound.Left() != FAR_AWAY) { + // OD 03.03.2003 #107927# - use correct datatype + const SwTwips nL = std::max( 0L, rBound.Left() - nOfst ); + if ( nL < nMinLeft ) + nMinLeft = nL; + if( rBound.Right() + nOfst > nMaxRight ) + nMaxRight = rBound.Right() + nOfst; + } + } + } + } + } + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + } + tools::Rectangle aRect( aPrevArea.SVRect() ); + aRect.SetLeft( nMinLeft ); + aRect.SetRight( nMaxRight ); + if( VisArea().IsOver( aPrevArea ) && !mnLockPaint ) + { + bScrolled = true; + maVisArea.Pos() = aPrevArea.Pos(); + if ( SmoothScroll( nXDiff, nYDiff, &aRect ) ) + return; + maVisArea.Pos() = rRect.Pos(); + } + else + GetWin()->Invalidate( aRect ); + } + else if ( !mnLockPaint ) //will be released in Unlock + { + if( VisArea().IsOver( 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() ); + Imp()->GetDrawView()->SetActualWin( GetWin() ); + } + GetWin()->PaintImmediately(); + + if ( pPostItMgr ) // #i88070# + { + pPostItMgr->Rescale(); + pPostItMgr->CalcRects(); + pPostItMgr->LayoutPostIts(); + } + + if ( !bScrolled && pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ) + pPostItMgr->CorrectPositions(); + + if( Imp()->IsAccessible() ) + Imp()->UpdateAccessible(); +} + +bool SwViewShell::SmoothScroll( long lXDiff, 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(); + 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() ); + pVout->SetLineColor( GetWin()->GetLineColor() ); + pVout->SetFillColor( GetWin()->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( 0L, 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, AddWindowToPaintView 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->AddWindowToPaintView(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::mbLstAct = true; + GetLayout()->PaintSwFrame( *GetOut(), aRect ); + SwViewShell::mbLstAct = false; + + // end paint and destroy ObjectContact again + DLPostPaint2(true); + pDrawView->DeleteWindowFromPaintView(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? + // ?????????????????????? + long lMaDelta = aPixSz.Height(); + if ( std::abs(lYDiff) > ( maVisArea.Height() / 3 ) ) + lMaDelta *= 6; + else + lMaDelta *= 2; + + lMaDelta *= lMult; + + if ( lYDiff < 0 ) + lMaDelta = -lMaDelta; + + long lDiff = lYDiff; + while ( lDiff ) + { + 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() ); + + 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(vcl::RenderContext& rRenderContext, const SwRect &rRect) +{ + if ( !GetWin() && !GetOut()->GetConnectMetaFile() ) + return; //for the printer we don't do anything here. + + //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().IsOver( 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 ) + { + 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.IsOver( 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( PushFlags::FILLCOLOR|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( SwViewOption::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()->GetRegion() && Imp()->GetRegion()->GetOrigin() != VisArea()) + Imp()->DelRegion(); + + 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()); + --mnStartAction; + + SwRegionRects *pRegion = Imp()->GetRegion(); + if ( pRegion && aAction.IsBrowseActionStop() ) + { + //only of interest when something has changed in the visible range + bool bStop = true; + for ( size_t i = 0; i < pRegion->size(); ++i ) + { + const SwRect &rTmp = (*pRegion)[i]; + bStop = rTmp.IsOver( VisArea() ); + if ( !bStop ) + break; + } + if ( bStop ) + { + Imp()->DelRegion(); + pRegion = nullptr; + } + } + + if ( pRegion ) + { + //First Invert then Compress, never the other way round! + pRegion->Invert(); + pRegion->Compress(); + bRet = false; + if ( !pRegion->empty() ) + { + SwRegionRects aRegion( rRect ); + for ( size_t i = 0; i < pRegion->size(); ++i ) + { const SwRect &rTmp = (*pRegion)[i]; + if ( !rRect.IsInside( rTmp ) ) + { + InvalidateWindows( rTmp ); + if ( rTmp.IsOver( 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; + Imp()->DelRegion(); + } + 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()) + { + SdrView* pDrawView(pShell->Imp()->GetDrawView()); + + if (nullptr != pDrawView) + { + SdrPageView* pSdrPageView(pDrawView->GetSdrPageView()); + + if (nullptr != pSdrPageView) + { + m_pPatchedPageWindow = pSdrPageView->FindPageWindow(*pShell->GetWin()); + + 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.IsInside( 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; + SET_CURR_SHELL( 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.IsInside( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::mbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::mbLstAct = 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.IsInside( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::mbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::mbLstAct = 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(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() ) + { + // #i68597# + const vcl::Region aDLRegion(rRect); + DLPrePaint2(aDLRegion); + + rRenderContext.Push( PushFlags::FILLCOLOR|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, long tileWidth, 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. + Fraction scaleX = Fraction(contextWidth, 96) * Fraction(1440) / Fraction(tileWidth); + Fraction scaleY = Fraction(contextHeight, 96) * Fraction(1440) / Fraction(tileHeight); + 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(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; + + SET_CURR_SHELL( 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(); + 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(); + 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.SetFieldName( rOpt.IsFieldName() ); + aOpt.SetShowHiddenField( rOpt.IsShowHiddenField() ); + aOpt.SetShowHiddenPara( rOpt.IsShowHiddenPara() ); + aOpt.SetShowHiddenChar( rOpt.IsShowHiddenChar() ); + aOpt.SetViewLayoutBookMode( rOpt.IsViewLayoutBookMode() ); + aOpt.SetHideWhitespaceMode(rOpt.IsHideWhitespaceMode()); + aOpt.SetViewLayoutColumns(rOpt.GetViewLayoutColumns()); + aOpt.SetPostIts(rOpt.IsPostIts()); + if ( !(aOpt == *rSh.GetViewOptions()) ) + rSh.ImplApplyViewOptions( aOpt ); + } + // End of disabled multiple window + + for(SwViewShell& rSh : GetRingContainer()) + rSh.EndAction(); +} + +void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt ) +{ + if (*mpOpt == rOpt) + return; + + vcl::Window *pMyWin = GetWin(); + if( !pMyWin ) + { + OSL_ENSURE( pMyWin, "SwViewShell::ApplyViewOptions: no window" ); + return; + } + + SET_CURR_SHELL( 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() ) + { + SwMsgPoolItem aHint( RES_HIDDENPARA_PRINT ); + pFieldType->ModifyNotification( &aHint, nullptr); + } + bReformat = true; + } + if ( !bReformat && mpOpt->IsShowHiddenChar() != rOpt.IsShowHiddenChar() ) + { + bReformat = GetDoc()->ContainsHiddenChars(); + } + + // 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 ... + bReformat = bReformat || mpOpt->IsFieldName() != rOpt.IsFieldName(); + + // 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 ); + } + + pMyWin->Invalidate(); + if ( bReformat ) + { + // Nothing helps, we need to send all ContentFrames a + // Prepare, we format anew: + StartAction(); + Reformat(); + EndAction(); + } + + if( bOnlineSpellChgd ) + { + 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() ) + { + // so that the flags can be queried properly. + mpOpt->SetReadonly( false ); + + bool bReformat = mpOpt->IsFieldName(); + + mpOpt->SetReadonly( bSet ); + + if( bReformat ) + { + StartAction(); + Reformat(); + if ( GetWin() ) + GetWin()->Invalidate(); + EndAction(); + } + else if ( GetWin() ) + GetWin()->Invalidate(); + if( Imp()->IsAccessible() ) + Imp()->InvalidateAccessibleEditableState( false ); + } +} + +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(); +} + +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(SvtAccessibilityOptions const & rAccessibilityOptions) +{ + if (utl::ConfigManager::IsFuzzing()) + return; + if (mpOpt->IsPagePreview() && !rAccessibilityOptions.GetIsForPagePreviews()) + { + mpAccOptions->SetAlwaysAutoColor(false); + mpAccOptions->SetStopAnimatedGraphics(false); + } + else + { + mpAccOptions->SetAlwaysAutoColor(rAccessibilityOptions.GetIsAutomaticFontColor()); + mpAccOptions->SetStopAnimatedGraphics(! rAccessibilityOptions.GetIsAllowAnimatedGraphics()); + + // Form view + // Always set this option, not only if document is read-only: + mpOpt->SetSelectionInReadonly(rAccessibilityOptions.IsSelectionInReadonly()); + } +} + +ShellResource* SwViewShell::GetShellRes() +{ + return mpShellRes; +} + +void SwViewShell::SetCareDialog(const std::shared_ptr<weld::Window>& rNew) +{ + (*mpCareDialog.get()) = rNew; +} + +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; +} + +// #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; +} + +/* + * 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: */ |