/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !HAVE_FEATURE_DESKTOP #include #endif #include #include bool SwViewShell::sbLstAct = false; ShellResource *SwViewShell::spShellRes = nullptr; vcl::DeleteOnDeinit> SwViewShell::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; } // Repaint everything GetWin()->Invalidate(); } 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")); (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); 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 !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::unique_ptr pRegion = 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 = pRegion && dynamic_cast(this) != nullptr; if( bShowCursor ) static_cast(this)->HideCursors(); if ( pRegion ) { SwRootFrame* pCurrentLayout = GetLayout(); pRegion->LimitToOrigin(); pRegion->Compress( SwRegionRects::CompressFuzzy ); ScopedVclPtr 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::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( tools::Long(0), aTmp1.Left() - 1 ) ); if ( aTmp2.Top() > aRect.Top() ) aTmp1.SetTop( std::max( 0, 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); 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(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() ) GetWin()->EnablePaint( false ); //Also cut off the controls. Imp()->LockPaint(); } void SwViewShell::ImplUnlockPaint( bool bVirDev ) { CurrShell aCurr( this ); if ( GetWin() && GetWin()->IsVisible() ) { if ( (bInSizeNotify || bVirDev ) && VisArea().HasArea() ) { //Refresh with virtual device to avoid flickering. VclPtrInstance 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() ) 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 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* 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) { CurrShell aCurr( this ); auto pCursorShell = dynamic_cast( this ); if ( pCursorShell ) pCursorShell->StartAction(); else StartAction(); GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB); if ( pCursorShell ) pCursorShell->EndAction(); else EndAction(); } void SwViewShell::UpdateOleObjectPreviews() { SwDoc* pDoc = GetDoc(); const SwFrameFormats* const pFormats = pDoc->GetSpzFrameFormats(); if (pFormats->empty()) { return; } for (size_t i = 0; i < pFormats->size(); ++i) { SwFrameFormat* pFormat = (*pFormats)[i]; 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( &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( &_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 ); } } 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) 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::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 ); 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( this ) != nullptr ) { PageNumNotify(this); 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::getFromUnoTunnel(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()->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(GetLayout()->Lower()); if ( pPage->getFrameArea().Top() > pOldPage->getFrameArea().Top() ) pPage = static_cast(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 &rBound = pObj->GetObjRect().SVRect(); if (rBound.Left() != FAR_AWAY) { // OD 03.03.2003 #107927# - use correct datatype const SwTwips nL = std::max( SwTwips(0), SwTwips(rBound.Left() - nOfst) ); if ( nL < nMinLeft ) nMinLeft = nL; if( rBound.Right() + nOfst > nMaxRight ) nMaxRight = rBound.Right() + nOfst; } } } } } pPage = static_cast(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 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 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, 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::sbLstAct = true; GetLayout()->PaintSwFrame( *GetOut(), aRect ); SwViewShell::sbLstAct = 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? // ?????????????????????? 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(pPage)->GetFormatPage(); aPageRect.SSize( rFormatPage.getFrameArea().SSize() ); } const bool bSidebarRight = static_cast(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( 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(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::unique_ptr pRegion = Imp()->TakePaintRegion(); 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.Overlaps( VisArea() ); if ( !bStop ) break; } if ( bStop ) pRegion.reset(); } if ( pRegion ) { pRegion->LimitToOrigin(); pRegion->Compress( SwRegionRects::CompressFuzzy ); bRet = false; if ( !pRegion->empty() ) { SwRegionRects aRegion( rRect ); for ( size_t i = 0; i < pRegion->size(); ++i ) { const SwRect &rTmp = (*pRegion)[i]; 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 m_TemporaryPaintWindow; SdrPageWindow* m_pPatchedPageWindow; SdrPaintWindow* m_pPreviousPaintWindow = nullptr; public: RenderContextGuard(VclPtr& 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(); StartAction(); SwPageFrame *pPg = static_cast(GetLayout()->Lower()); do { pPg->InvalidateSize(); pPg->InvalidatePrt_(); pPg->InvaPercentLowers(); if ( bSizeChanged ) { pPg->PrepareHeader(); pPg->PrepareFooter(); } pPg = static_cast(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(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.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(); } 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( dynamic_cast(**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(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))-> SetHiddenFlag( !rOpt.IsShowHiddenField() ); bReformat = true; } if ( mpOpt->IsShowHiddenPara() != rOpt.IsShowHiddenPara() ) { SwHiddenParaFieldType* pFieldType = static_cast(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()) { GetLayout()->SetFieldmarkMode( rOpt.IsFieldName() ? sw::FieldmarkMode::ShowCommand : sw::FieldmarkMode::ShowResult ); 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 ); } 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(&rSh)) { if ((mpOpt->IsFieldName() && pSh->CursorInsideInputField()) || IsCursorInFieldmarkHidden(*pSh->GetCursor(), pSh->GetLayout()->GetFieldmarkMode())) { // move cursor out of field pSh->Left(1, CRSR_SKIP_CHARS); } } } } if( !bOnlineSpellChgd ) return; 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() ) GetWin()->Invalidate(); EndAction(); } else if ( GetWin() ) 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(SvtAccessibilityOptions const & rAccessibilityOptions) { if (utl::ConfigManager::IsFuzzing()) return; if (mpOpt->IsPagePreview() && !officecfg::Office::Common::Accessibility::IsForPagePreviews::get()) { 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()); } } #endif // ENABLE_WASM_STRIP_ACCESSIBILITY ShellResource* SwViewShell::GetShellRes() { return spShellRes; } void SwViewShell::SetCareDialog(const std::shared_ptr& rNew) { (*spCareDialog.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 (pTmpRoot->Lower()); while( --nPageNum && pPage->GetNext() ) pPage = static_cast( pPage->GetNext() ); if( !bSkipEmptyPages && pPage->IsEmptyPage() && pPage->GetNext() ) pPage = static_cast( 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; } /* * 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: */