diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/view | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/view')
-rw-r--r-- | sw/source/core/view/dialoghelp.cxx | 47 | ||||
-rw-r--r-- | sw/source/core/view/pagepreviewlayout.cxx | 1465 | ||||
-rw-r--r-- | sw/source/core/view/printdata.cxx | 447 | ||||
-rw-r--r-- | sw/source/core/view/vdraw.cxx | 280 | ||||
-rw-r--r-- | sw/source/core/view/viewimp.cxx | 528 | ||||
-rw-r--r-- | sw/source/core/view/viewpg.cxx | 216 | ||||
-rw-r--r-- | sw/source/core/view/viewsh.cxx | 2849 | ||||
-rw-r--r-- | sw/source/core/view/vnew.cxx | 383 | ||||
-rw-r--r-- | sw/source/core/view/vprint.cxx | 676 |
9 files changed, 6891 insertions, 0 deletions
diff --git a/sw/source/core/view/dialoghelp.cxx b/sw/source/core/view/dialoghelp.cxx new file mode 100644 index 0000000000..88d12484d2 --- /dev/null +++ b/sw/source/core/view/dialoghelp.cxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <sfx2/docfile.hxx> +#include <sfx2/frame.hxx> +#include <vcl/weld.hxx> + +#include <dialoghelp.hxx> +#include <doc.hxx> +#include <docsh.hxx> +#include <view.hxx> + +weld::Window* GetFrameWeld(const SfxFrame* pFrame) +{ + return pFrame ? pFrame->GetWindow().GetFrameWeld() : nullptr; +} + +weld::Window* GetFrameWeld(const SfxMedium* pMedium) +{ + return GetFrameWeld(pMedium ? pMedium->GetLoadTargetFrame() : nullptr); +} + +weld::Window* GetFrameWeld(SwDocShell* pDocShell) +{ + if (!pDocShell) + return nullptr; + weld::Window* pRet = GetFrameWeld(pDocShell->GetMedium()); + if (!pRet) + { + if (SwView* pView = pDocShell->GetView()) + pRet = pView->GetFrameWeld(); + } + return pRet; +} + +weld::Window* GetFrameWeld(SwDoc* pDoc) +{ + return GetFrameWeld(pDoc ? pDoc->GetDocShell() : nullptr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/core/view/pagepreviewlayout.cxx b/sw/source/core/view/pagepreviewlayout.cxx new file mode 100644 index 0000000000..5fc740ebfd --- /dev/null +++ b/sw/source/core/view/pagepreviewlayout.cxx @@ -0,0 +1,1465 @@ +/* -*- 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 <config_wasm_strip.h> + +#include <pagepreviewlayout.hxx> +#include <prevwpage.hxx> + +#include <algorithm> +#include <osl/diagnose.h> +#include <tools/fract.hxx> +#include <vcl/settings.hxx> + +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <viewsh.hxx> +#include <viewimp.hxx> +#include <viewopt.hxx> +#include <swregion.hxx> +#include <strings.hrc> +#include <frmtool.hxx> +#include <sfx2/zoomitem.hxx> +#include <printdata.hxx> +#include <paintfrm.hxx> + +#include <IDocumentDeviceAccess.hxx> + +// methods to initialize page preview layout + +SwPagePreviewLayout::SwPagePreviewLayout( SwViewShell& _rParentViewShell, + const SwRootFrame& _rLayoutRootFrame ) + : mrParentViewShell( _rParentViewShell ), + mrLayoutRootFrame ( _rLayoutRootFrame ) +{ + Clear_(); + + mbBookPreview = false; + mbBookPreviewModeToggled = false; + + mbPrintEmptyPages = mrParentViewShell.getIDocumentDeviceAccess().getPrintData().IsPrintEmptyPages(); +} + +void SwPagePreviewLayout::Clear_() +{ + mbLayoutInfoValid = mbLayoutSizesValid = mbPaintInfoValid = false; + + maWinSize.setWidth( 0 ); + maWinSize.setHeight( 0 ); + mnCols = mnRows = 0; + + ClearPreviewLayoutSizes(); + + mbDoesLayoutRowsFitIntoWindow = false; + mbDoesLayoutColsFitIntoWindow = false; + + mnPaintPhyStartPageNum = 0; + mnPaintStartCol = mnPaintStartRow = 0; + mbNoPageVisible = false; + maPaintStartPageOffset.setX( 0 ); + maPaintStartPageOffset.setY( 0 ); + maPaintPreviewDocOffset.setX( 0 ); + maPaintPreviewDocOffset.setY( 0 ); + maAdditionalPaintOffset.setX( 0 ); + maAdditionalPaintOffset.setY( 0 ); + maPaintedPreviewDocRect.SetLeft( 0 ); + maPaintedPreviewDocRect.SetTop( 0 ); + maPaintedPreviewDocRect.SetRight( 0 ); + maPaintedPreviewDocRect.SetBottom( 0 ); + mnSelectedPageNum = 0; + ClearPreviewPageData(); + + mbInPaint = false; + mbNewLayoutDuringPaint = false; +} + +void SwPagePreviewLayout::ClearPreviewLayoutSizes() +{ + mnPages = 0; + + maMaxPageSize.setWidth( 0 ); + maMaxPageSize.setHeight( 0 ); + maPreviewDocRect.SetLeft( 0 ); + maPreviewDocRect.SetTop( 0 ); + maPreviewDocRect.SetRight( 0 ); + maPreviewDocRect.SetBottom( 0 ); + mnColWidth = mnRowHeight = 0; + mnPreviewLayoutWidth = mnPreviewLayoutHeight = 0; +} + +void SwPagePreviewLayout::ClearPreviewPageData() +{ + maPreviewPages.clear(); +} + +/** calculate page preview layout sizes + +*/ +void SwPagePreviewLayout::CalcPreviewLayoutSizes() +{ + vcl::RenderContext* pRenderContext = mrParentViewShell.GetOut(); + // calculate maximal page size; calculate also number of pages + + const SwPageFrame* pPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower()); + while ( pPage ) + { + if ( !mbBookPreview && !mbPrintEmptyPages && pPage->IsEmptyPage() ) + { + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + continue; + } + + ++mnPages; + pPage->Calc(pRenderContext); + const Size& rPageSize = pPage->getFrameArea().SSize(); + if ( rPageSize.Width() > maMaxPageSize.Width() ) + maMaxPageSize.setWidth( rPageSize.Width() ); + if ( rPageSize.Height() > maMaxPageSize.Height() ) + maMaxPageSize.setHeight( rPageSize.Height() ); + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + } + // calculate and set column width and row height + mnColWidth = maMaxPageSize.Width() + gnXFree; + mnRowHeight = maMaxPageSize.Height() + gnYFree; + + // calculate and set preview layout width and height + mnPreviewLayoutWidth = mnCols * mnColWidth + gnXFree; + mnPreviewLayoutHeight = mnRows * mnRowHeight + gnYFree; + + // calculate document rectangle in preview layout + { + Size aDocSize; + // document width + aDocSize.setWidth( mnPreviewLayoutWidth ); + + // document height + // determine number of rows needed for <nPages> in preview layout + // use method <GetRowOfPage(..)>. + const sal_uInt16 nDocRows = GetRowOfPage( mnPages ); + aDocSize.setHeight( nDocRows * maMaxPageSize.Height() + + (nDocRows+1) * gnYFree ); + maPreviewDocRect.SetPos( Point( 0, 0 ) ); + maPreviewDocRect.SetSize( aDocSize ); + } +} + +/** init page preview layout + + initialize the page preview settings for a given layout. + + side effects: + (1) If parameter <_bCalcScale> is true, mapping mode with calculated + scaling is set at the output device and the zoom at the view options of + the given view shell is set with the calculated scaling. +*/ +void SwPagePreviewLayout::Init( const sal_uInt16 _nCols, + const sal_uInt16 _nRows, + const Size& _rPxWinSize + ) +{ + // check environment and parameters + { + bool bColsRowsValid = (_nCols != 0) && (_nRows != 0); + OSL_ENSURE( bColsRowsValid, "preview layout parameters not correct - preview layout can *not* be initialized" ); + if ( !bColsRowsValid ) + return; + + bool bPxWinSizeValid = (_rPxWinSize.Width() >= 0) && + (_rPxWinSize.Height() >= 0); + OSL_ENSURE( bPxWinSizeValid, "no window size - preview layout can *not* be initialized" ); + if ( !bPxWinSizeValid ) + return; + } + + // environment and parameters ok + + // clear existing preview settings + Clear_(); + + // set layout information columns and rows + mnCols = _nCols; + mnRows = _nRows; + + CalcPreviewLayoutSizes(); + + // validate layout information + mbLayoutInfoValid = true; + + // calculate scaling + MapMode aMapMode( MapUnit::MapTwip ); + Size aWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize, aMapMode ); + Fraction aXScale( aWinSize.Width(), mnPreviewLayoutWidth ); + Fraction aYScale( aWinSize.Height(), mnPreviewLayoutHeight ); + if( aXScale < aYScale ) + aYScale = aXScale; + { + // adjust scaling for Drawing layer. + aYScale *= Fraction( 1000, 1 ); + tools::Long nNewNuminator = aYScale.operator long(); + if( nNewNuminator < 1 ) + nNewNuminator = 1; + aYScale = Fraction( nNewNuminator, 1000 ); + // propagate scaling as zoom percentage to view options for font cache + ApplyNewZoomAtViewShell( static_cast<sal_uInt8>(nNewNuminator/10) ); + + aMapMode.SetScaleY( aYScale ); + aMapMode.SetScaleX( aYScale ); + // set created mapping mode with calculated scaling at output device. + mrParentViewShell.GetOut()->SetMapMode( aMapMode ); + // update statics for paint. + ::SwCalcPixStatics( mrParentViewShell.GetOut() ); + } + + // set window size in twips + maWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize ); + // validate layout sizes + mbLayoutSizesValid = true; +} + +/** apply new zoom at given view shell */ +void SwPagePreviewLayout::ApplyNewZoomAtViewShell( sal_uInt8 _aNewZoom ) +{ + SwViewOption aNewViewOptions = *(mrParentViewShell.GetViewOptions()); + if ( aNewViewOptions.GetZoom() != _aNewZoom ) + { + aNewViewOptions.SetZoom( _aNewZoom ); + //#i19975# - consider zoom type. + aNewViewOptions.SetZoomType( SvxZoomType::PERCENT ); + mrParentViewShell.ApplyViewOptions( aNewViewOptions ); + } +} + +/** method to adjust page preview layout to document changes + +*/ +void SwPagePreviewLayout::ReInit() +{ + // check environment and parameters + { + bool bLayoutSettingsValid = mbLayoutInfoValid && mbLayoutSizesValid; + OSL_ENSURE( bLayoutSettingsValid, + "no valid preview layout info/sizes - no re-init of page preview layout"); + if ( !bLayoutSettingsValid ) + return; + } + + ClearPreviewLayoutSizes(); + CalcPreviewLayoutSizes(); +} + +// methods to prepare paint of page preview + +/** prepare paint of page preview + + delete parameter _onStartPageVirtNum + + @note _nProposedStartPageNum, _onStartPageNum are absolute +*/ +bool SwPagePreviewLayout::Prepare( const sal_uInt16 _nProposedStartPageNum, + const Point& rProposedStartPos, + const Size& _rPxWinSize, + sal_uInt16& _onStartPageNum, + tools::Rectangle& _orDocPreviewPaintRect, + const bool _bStartWithPageAtFirstCol + ) +{ + sal_uInt16 nProposedStartPageNum = ConvertAbsoluteToRelativePageNum( _nProposedStartPageNum ); + // check environment and parameters + { + bool bLayoutSettingsValid = mbLayoutInfoValid && mbLayoutSizesValid; + OSL_ENSURE( bLayoutSettingsValid, + "no valid preview layout info/sizes - no prepare of preview paint"); + if ( !bLayoutSettingsValid ) + return false; + + bool bStartPageRangeValid = nProposedStartPageNum <= mnPages; + OSL_ENSURE( bStartPageRangeValid, + "proposed start page not existing - no prepare of preview paint"); + if ( !bStartPageRangeValid ) + return false; + + bool bStartPosRangeValid = + rProposedStartPos.X() >= 0 && rProposedStartPos.Y() >= 0 && + rProposedStartPos.X() <= maPreviewDocRect.Right() && + rProposedStartPos.Y() <= maPreviewDocRect.Bottom(); + OSL_ENSURE( bStartPosRangeValid, + "proposed start position out of range - no prepare of preview paint"); + if ( !bStartPosRangeValid ) + return false; + + bool bWinSizeValid = !_rPxWinSize.IsEmpty(); + OSL_ENSURE( bWinSizeValid, "no window size - no prepare of preview paint"); + if ( !bWinSizeValid ) + return false; + + bool bStartInfoValid = _nProposedStartPageNum > 0 || + rProposedStartPos != Point(0,0); + if ( !bStartInfoValid ) + nProposedStartPageNum = 1; + } + + // environment and parameter ok + + // update window size at preview setting data + maWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize ); + + mbNoPageVisible = false; + if ( nProposedStartPageNum > 0 ) + { + // determine column and row of proposed start page in virtual preview layout + const sal_uInt16 nColOfProposed = GetColOfPage( nProposedStartPageNum ); + const sal_uInt16 nRowOfProposed = GetRowOfPage( nProposedStartPageNum ); + // determine start page + if ( _bStartWithPageAtFirstCol ) + { + // leaving left-top-corner blank is + // controlled by <mbBookPreview>. + if ( mbBookPreview && + ( nProposedStartPageNum == 1 || nRowOfProposed == 1 ) + ) + mnPaintPhyStartPageNum = 1; + else + mnPaintPhyStartPageNum = nProposedStartPageNum - (nColOfProposed-1); + } + else + mnPaintPhyStartPageNum = nProposedStartPageNum; + + mnPaintPhyStartPageNum = ConvertRelativeToAbsolutePageNum( mnPaintPhyStartPageNum ); + + // set starting column + if ( _bStartWithPageAtFirstCol ) + mnPaintStartCol = 1; + else + mnPaintStartCol = nColOfProposed; + // set starting row + mnPaintStartRow = nRowOfProposed; + // page offset == (-1,-1), indicating no offset and paint of free space. + maPaintStartPageOffset.setX( -1 ); + maPaintStartPageOffset.setY( -1 ); + // virtual preview document offset. + if ( _bStartWithPageAtFirstCol ) + maPaintPreviewDocOffset.setX( 0 ); + else + maPaintPreviewDocOffset.setX( (nColOfProposed-1) * mnColWidth ); + maPaintPreviewDocOffset.setY( (nRowOfProposed-1) * mnRowHeight ); + } + else + { + // determine column and row of proposed start position. + // Note: paint starts at point (0,0) + const sal_uInt16 nColOfProposed = + o3tl::narrowing<sal_uInt16>(rProposedStartPos.X() / mnColWidth) + 1; + const sal_uInt16 nRowOfProposed = + o3tl::narrowing<sal_uInt16>(rProposedStartPos.Y() / mnRowHeight) + 1; + // determine start page == page at proposed start position + // leaving left-top-corner blank is + // controlled by <mbBookPreview>. + if ( mbBookPreview && + ( nRowOfProposed == 1 && nColOfProposed == 1 ) + ) + mnPaintPhyStartPageNum = 1; + else + { + // leaving left-top-corner blank is + // controlled by <mbBookPreview>. + mnPaintPhyStartPageNum = (nRowOfProposed-1) * mnCols + nColOfProposed; + if ( mbBookPreview ) + --mnPaintPhyStartPageNum; + if ( mnPaintPhyStartPageNum > mnPages ) + { + // no page will be visible, because shown part of document + // preview is the last row to the right of the last page + mnPaintPhyStartPageNum = mnPages; + mbNoPageVisible = true; + } + } + // set starting column and starting row + mnPaintStartCol = nColOfProposed; + mnPaintStartRow = nRowOfProposed; + // page offset + maPaintStartPageOffset.setX( + (rProposedStartPos.X() % mnColWidth) - gnXFree ); + maPaintStartPageOffset.setY( + (rProposedStartPos.Y() % mnRowHeight) - gnYFree ); + // virtual preview document offset. + maPaintPreviewDocOffset = rProposedStartPos; + } + + // determine additional paint offset, if preview layout fits into window. + CalcAdditionalPaintOffset(); + + // determine rectangle to be painted from document preview + CalcDocPreviewPaintRect(); + _orDocPreviewPaintRect = maPaintedPreviewDocRect; + + // shift visible preview document area to the left, + // if on the right is an area left blank. + if ( !mbDoesLayoutColsFitIntoWindow && + maPaintedPreviewDocRect.GetWidth() < maWinSize.Width() ) + { + maPaintedPreviewDocRect.Move( + -(maWinSize.Width() - maPaintedPreviewDocRect.GetWidth()), 0 ); + Prepare( 0, maPaintedPreviewDocRect.TopLeft(), + _rPxWinSize, _onStartPageNum, + _orDocPreviewPaintRect, _bStartWithPageAtFirstCol ); + } + + // shift visible preview document area to the top, + // if on the bottom is an area left blank. + if ( mbBookPreviewModeToggled && + maPaintedPreviewDocRect.Bottom() == maPreviewDocRect.Bottom() && + maPaintedPreviewDocRect.GetHeight() < maWinSize.Height() ) + { + if ( mbDoesLayoutRowsFitIntoWindow ) + { + if ( maPaintedPreviewDocRect.GetHeight() < mnPreviewLayoutHeight) + { + maPaintedPreviewDocRect.Move( + 0, -(mnPreviewLayoutHeight - maPaintedPreviewDocRect.GetHeight()) ); + Prepare( 0, maPaintedPreviewDocRect.TopLeft(), + _rPxWinSize, _onStartPageNum, + _orDocPreviewPaintRect, _bStartWithPageAtFirstCol ); + } + } + else + { + maPaintedPreviewDocRect.Move( + 0, -(maWinSize.Height() - maPaintedPreviewDocRect.GetHeight()) ); + Prepare( 0, maPaintedPreviewDocRect.TopLeft(), + _rPxWinSize, _onStartPageNum, + _orDocPreviewPaintRect, _bStartWithPageAtFirstCol ); + } + } + + // determine preview pages - visible pages with needed data for paint and + // accessible pages with needed data. + CalcPreviewPages(); + + // OD 07.11.2003 #i22014# - indicate new layout, if print preview is in paint + if ( mbInPaint ) + { + mbNewLayoutDuringPaint = true; + } + + // validate paint data + mbPaintInfoValid = true; + + // return start page + _onStartPageNum = mnPaintPhyStartPageNum; + + return true; +} + +/** calculate additional paint offset + +*/ +void SwPagePreviewLayout::CalcAdditionalPaintOffset() +{ + if ( mnPreviewLayoutWidth <= maWinSize.Width() && + maPaintStartPageOffset.X() <= 0 ) + { + mbDoesLayoutColsFitIntoWindow = true; + maAdditionalPaintOffset.setX( (maWinSize.Width() - mnPreviewLayoutWidth) / 2 ); + } + else + { + mbDoesLayoutColsFitIntoWindow = false; + maAdditionalPaintOffset.setX( 0 ); + } + + if ( mnPreviewLayoutHeight <= maWinSize.Height() && + maPaintStartPageOffset.Y() <= 0 ) + { + mbDoesLayoutRowsFitIntoWindow = true; + maAdditionalPaintOffset.setY( (maWinSize.Height() - mnPreviewLayoutHeight) / 2 ); + } + else + { + mbDoesLayoutRowsFitIntoWindow = false; + maAdditionalPaintOffset.setY( 0 ); + } +} + +/** calculate painted preview document rectangle + +*/ +void SwPagePreviewLayout::CalcDocPreviewPaintRect() +{ + Point aTopLeftPos = maPaintPreviewDocOffset; + maPaintedPreviewDocRect.SetPos( aTopLeftPos ); + + Size aSize; + if ( mbDoesLayoutColsFitIntoWindow ) + aSize.setWidth( std::min( tools::Long(mnPreviewLayoutWidth), + maPreviewDocRect.GetWidth() - aTopLeftPos.X() ) ); + else + aSize.setWidth( std::min( maPreviewDocRect.GetWidth() - aTopLeftPos.X(), + maWinSize.Width() - maAdditionalPaintOffset.X() ) ); + if ( mbDoesLayoutRowsFitIntoWindow ) + aSize.setHeight( std::min( tools::Long(mnPreviewLayoutHeight), + maPreviewDocRect.GetHeight() - aTopLeftPos.Y() ) ); + else + aSize.setHeight( std::min( maPreviewDocRect.GetHeight() - aTopLeftPos.Y(), + maWinSize.Height() - maAdditionalPaintOffset.Y() ) ); + maPaintedPreviewDocRect.SetSize( aSize ); +} + +/** calculate preview pages + +*/ +void SwPagePreviewLayout::CalcPreviewPages() +{ + vcl::RenderContext* pRenderContext = mrParentViewShell.GetOut(); + ClearPreviewPageData(); + + if ( mbNoPageVisible ) + return; + + // determine start page frame + const SwPageFrame* pStartPage = mrLayoutRootFrame.GetPageByPageNum( mnPaintPhyStartPageNum ); + + // calculate initial paint offset + Point aInitialPaintOffset; + /// check whether RTL interface or not + if(!AllSettings::GetLayoutRTL()){ + if ( maPaintStartPageOffset != Point( -1, -1 ) ) + aInitialPaintOffset = Point(0,0) - maPaintStartPageOffset; + else + aInitialPaintOffset = Point( gnXFree, gnYFree ); + } + else { + if ( maPaintStartPageOffset != Point( -1, -1 ) ) + aInitialPaintOffset = Point(0 + ((SwPagePreviewLayout::mnCols-1)*mnColWidth),0) - maPaintStartPageOffset; + else + aInitialPaintOffset = Point( gnXFree + ((SwPagePreviewLayout::mnCols-1)*mnColWidth), gnYFree ); + } + aInitialPaintOffset += maAdditionalPaintOffset; + + // prepare loop data + const SwPageFrame* pPage = pStartPage; + sal_uInt16 nCurrCol = mnPaintStartCol; + sal_uInt16 nConsideredRows = 0; + Point aCurrPaintOffset = aInitialPaintOffset; + // loop on pages to determine preview background rectangles + while ( pPage && + (!mbDoesLayoutRowsFitIntoWindow || nConsideredRows < mnRows) && + aCurrPaintOffset.Y() < maWinSize.Height() + ) + { + if ( !mbBookPreview && !mbPrintEmptyPages && pPage->IsEmptyPage() ) + { + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + continue; + } + + pPage->Calc(pRenderContext); + + // consider only pages, which have to be painted. + if ( nCurrCol < mnPaintStartCol ) + { + // calculate data of unvisible page needed for accessibility + std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage); + Point aCurrAccOffset = aCurrPaintOffset - + Point( (mnPaintStartCol-nCurrCol) * mnColWidth, 0 ); + CalcPreviewDataForPage( *pPage, aCurrAccOffset, pPreviewPage.get() ); + pPreviewPage->bVisible = false; + maPreviewPages.push_back( std::move(pPreviewPage) ); + // continue with next page and next column + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + ++nCurrCol; + continue; + } + if ( aCurrPaintOffset.X() < maWinSize.Width() ) + { + // leaving left-top-corner blank is + // controlled by <mbBookPreview>. + if ( mbBookPreview && pPage->GetPhyPageNum() == 1 && mnCols != 1 && nCurrCol == 1 + ) + { + // first page in 2nd column + // --> continue with increased paint offset and next column + /// check whether RTL interface or not + if(!AllSettings::GetLayoutRTL()) + aCurrPaintOffset.AdjustX(mnColWidth ); + else aCurrPaintOffset.AdjustX( -mnColWidth ); + ++nCurrCol; + continue; + } + + // calculate data of visible page + std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage); + CalcPreviewDataForPage( *pPage, aCurrPaintOffset, pPreviewPage.get() ); + pPreviewPage->bVisible = true; + maPreviewPages.push_back( std::move(pPreviewPage) ); + } + else + { + // calculate data of unvisible page needed for accessibility + std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage); + CalcPreviewDataForPage( *pPage, aCurrPaintOffset, pPreviewPage.get() ); + pPreviewPage->bVisible = false; + maPreviewPages.push_back( std::move(pPreviewPage) ); + } + + // prepare data for next loop + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + + /// check whether RTL interface or not + if(!AllSettings::GetLayoutRTL()) + aCurrPaintOffset.AdjustX(mnColWidth ); + else aCurrPaintOffset.AdjustX( -mnColWidth ); + ++nCurrCol; + if ( nCurrCol > mnCols ) + { + ++nConsideredRows; + aCurrPaintOffset.setX( aInitialPaintOffset.X() ); + nCurrCol = 1; + aCurrPaintOffset.AdjustY(mnRowHeight ); + } + } +} + +/** determines preview data for a given page and a given preview offset + + OD 13.12.2002 #103492# +*/ +void SwPagePreviewLayout::CalcPreviewDataForPage( const SwPageFrame& _rPage, + const Point& _rPreviewOffset, + PreviewPage* _opPreviewPage ) +{ + // page frame + _opPreviewPage->pPage = &_rPage; + // size of page frame + if ( _rPage.IsEmptyPage() ) + { + if ( _rPage.GetPhyPageNum() % 2 == 0 ) + _opPreviewPage->aPageSize = _rPage.GetPrev()->getFrameArea().SSize(); + else + _opPreviewPage->aPageSize = _rPage.GetNext()->getFrameArea().SSize(); + } + else + _opPreviewPage->aPageSize = _rPage.getFrameArea().SSize(); + // position of page in preview window + Point aPreviewWinOffset( _rPreviewOffset ); + if ( _opPreviewPage->aPageSize.Width() < maMaxPageSize.Width() ) + aPreviewWinOffset.AdjustX(( maMaxPageSize.Width() - _opPreviewPage->aPageSize.Width() ) / 2 ); + if ( _opPreviewPage->aPageSize.Height() < maMaxPageSize.Height() ) + aPreviewWinOffset.AdjustY(( maMaxPageSize.Height() - _opPreviewPage->aPageSize.Height() ) / 2 ); + _opPreviewPage->aPreviewWinPos = aPreviewWinOffset; + // logic position of page and mapping offset for paint + if ( _rPage.IsEmptyPage() ) + { + _opPreviewPage->aLogicPos = _opPreviewPage->aPreviewWinPos; + _opPreviewPage->aMapOffset = Point( 0, 0 ); + } + else + { + _opPreviewPage->aLogicPos = _rPage.getFrameArea().Pos(); + _opPreviewPage->aMapOffset = _opPreviewPage->aPreviewWinPos - _opPreviewPage->aLogicPos; + } +} + +/** enable/disable book preview + + OD 2004-03-04 #i18143# +*/ +bool SwPagePreviewLayout::SetBookPreviewMode( const bool _bEnableBookPreview, + sal_uInt16& _onStartPageNum, + tools::Rectangle& _orDocPreviewPaintRect ) +{ + if ( mbBookPreview != _bEnableBookPreview) + { + mbBookPreview = _bEnableBookPreview; + // re-initialize page preview layout + ReInit(); + // re-prepare page preview layout + { + mbBookPreviewModeToggled = true; + Point aProposedStartPos( maPaintPreviewDocOffset ); + // if proposed start position is below virtual preview document + // bottom, adjust it to the virtual preview document bottom + if ( aProposedStartPos.Y() > maPreviewDocRect.Bottom() ) + { + aProposedStartPos.setY( maPreviewDocRect.Bottom() ); + } + Prepare( 0, aProposedStartPos, + mrParentViewShell.GetOut()->LogicToPixel( maWinSize ), + _onStartPageNum, _orDocPreviewPaintRect ); + mbBookPreviewModeToggled = false; + } + + return true; + } + + return false; +} + +// methods to determine new data for changing the current shown part of the +// document preview. + +/** calculate start position for new scale + +*/ +Point SwPagePreviewLayout::GetPreviewStartPosForNewScale( + const Fraction& _aNewScale, + const Fraction& _aOldScale, + const Size& _aNewWinSize ) const +{ + Point aNewPaintStartPos = maPaintedPreviewDocRect.TopLeft(); + if ( _aNewScale < _aOldScale ) + { + // increase paint width by moving start point to left. + if ( mnPreviewLayoutWidth < _aNewWinSize.Width() ) + aNewPaintStartPos.setX( 0 ); + else if ( maPaintedPreviewDocRect.GetWidth() < _aNewWinSize.Width() ) + { + aNewPaintStartPos.AdjustX( -( + (_aNewWinSize.Width() - maPaintedPreviewDocRect.GetWidth()) / 2) ); + if ( aNewPaintStartPos.X() < 0) + aNewPaintStartPos.setX( 0 ); + } + + if ( !mbDoesLayoutRowsFitIntoWindow ) + { + // increase paint height by moving start point to top. + if ( mnPreviewLayoutHeight < _aNewWinSize.Height() ) + { + aNewPaintStartPos.setY( + (mnPaintStartRow - 1) * mnRowHeight ); + } + else if ( maPaintedPreviewDocRect.GetHeight() < _aNewWinSize.Height() ) + { + aNewPaintStartPos.AdjustY( -( + (_aNewWinSize.Height() - maPaintedPreviewDocRect.GetHeight()) / 2) ); + if ( aNewPaintStartPos.Y() < 0) + aNewPaintStartPos.setY( 0 ); + } + } + } + else + { + // decrease paint width by moving start point to right + if ( maPaintedPreviewDocRect.GetWidth() > _aNewWinSize.Width() ) + aNewPaintStartPos.AdjustX( + (maPaintedPreviewDocRect.GetWidth() - _aNewWinSize.Width()) / 2 ); + // decrease paint height by moving start point to bottom + if ( maPaintedPreviewDocRect.GetHeight() > _aNewWinSize.Height() ) + { + aNewPaintStartPos.AdjustY( + (maPaintedPreviewDocRect.GetHeight() - _aNewWinSize.Height()) / 2 ); + // check, if new y-position is outside document preview + if ( aNewPaintStartPos.Y() > maPreviewDocRect.Bottom() ) + aNewPaintStartPos.setY( + std::max( tools::Long(0), maPreviewDocRect.Bottom() - mnPreviewLayoutHeight ) ); + } + } + + return aNewPaintStartPos; +} + +/** determines, if page with given page number is visible in preview + + @note _nPageNum is absolute +*/ +bool SwPagePreviewLayout::IsPageVisible( const sal_uInt16 _nPageNum ) const +{ + const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum ); + return pPreviewPage && pPreviewPage->bVisible; +} + +/** calculate data to bring new selected page into view. + + @note IN/OUT parameters are absolute page numbers!!! +*/ +void SwPagePreviewLayout::CalcStartValuesForSelectedPageMove( + const sal_Int16 _nHoriMove, + const sal_Int16 _nVertMove, + sal_uInt16& _orNewSelectedPage, + sal_uInt16& _orNewStartPage, + Point& _orNewStartPos ) const +{ + // determine position of current selected page + sal_uInt16 nTmpRelSelPageNum = ConvertAbsoluteToRelativePageNum( mnSelectedPageNum ); + sal_uInt16 nNewRelSelectedPageNum = nTmpRelSelPageNum; + + const sal_uInt16 nCurrRow = GetRowOfPage(nTmpRelSelPageNum); + + // determine new selected page number + { + if ( _nHoriMove != 0 ) + { + if ( (nNewRelSelectedPageNum + _nHoriMove) < 1 ) + nNewRelSelectedPageNum = 1; + else if ( (nNewRelSelectedPageNum + _nHoriMove) > mnPages ) + nNewRelSelectedPageNum = mnPages; + else + nNewRelSelectedPageNum = nNewRelSelectedPageNum + _nHoriMove; + } + if ( _nVertMove != 0 ) + { + if ( (nNewRelSelectedPageNum + (_nVertMove * mnCols)) < 1 ) + nNewRelSelectedPageNum = 1; + else if ( (nNewRelSelectedPageNum + (_nVertMove * mnCols)) > mnPages ) + nNewRelSelectedPageNum = mnPages; + else + nNewRelSelectedPageNum += ( _nVertMove * mnCols ); + } + } + + sal_uInt16 nNewStartPage = mnPaintPhyStartPageNum; + Point aNewStartPos(0,0); + + const sal_uInt16 nNewAbsSelectedPageNum = ConvertRelativeToAbsolutePageNum( nNewRelSelectedPageNum ); + if ( !IsPageVisible( nNewAbsSelectedPageNum ) ) + { + if ( _nHoriMove != 0 && _nVertMove != 0 ) + { + OSL_FAIL( "missing implementation for moving preview selected page horizontal AND vertical"); + return; + } + + // new selected page has to be brought into view considering current + // visible preview. + const sal_uInt16 nTotalRows = GetRowOfPage( mnPages ); + if ( (_nHoriMove > 0 || _nVertMove > 0) && + mbDoesLayoutRowsFitIntoWindow && + mbDoesLayoutColsFitIntoWindow && + nCurrRow > nTotalRows - mnRows ) + { + // new proposed start page = left-top-corner of last possible + // preview page. + nNewStartPage = (nTotalRows - mnRows) * mnCols + 1; + // leaving left-top-corner blank is controlled + // by <mbBookPreview>. + if ( mbBookPreview ) + { + // Note: decrease new proposed start page number by one, + // because of blank left-top-corner + --nNewStartPage; + } + nNewStartPage = ConvertRelativeToAbsolutePageNum( nNewStartPage ); + } + else + { + // new proposed start page = new selected page. + nNewStartPage = ConvertRelativeToAbsolutePageNum( nNewRelSelectedPageNum ); + } + } + + _orNewSelectedPage = nNewAbsSelectedPageNum; + _orNewStartPage = nNewStartPage; + _orNewStartPos = aNewStartPos; +} + +namespace { + +/** checks, if given position is inside a shown document page */ +struct PreviewPosInsidePagePred +{ + const Point mnPreviewPos; + explicit PreviewPosInsidePagePred(const Point& rPreviewPos) + : mnPreviewPos( rPreviewPos ) + {} + bool operator() ( const std::unique_ptr<PreviewPage> & _pPreviewPage ) + { + if ( _pPreviewPage->bVisible ) + { + tools::Rectangle aPreviewPageRect( _pPreviewPage->aPreviewWinPos, _pPreviewPage->aPageSize ); + return aPreviewPageRect.Contains( mnPreviewPos ); + } + return false; + } +}; + +} + +bool SwPagePreviewLayout::IsPreviewPosInDocPreviewPage( const Point& rPreviewPos, + Point& _orDocPos, + bool& _obPosInEmptyPage, + sal_uInt16& _onPageNum ) const +{ + // initialize variable parameter values. + _orDocPos.setX( 0 ); + _orDocPos.setY( 0 ); + _obPosInEmptyPage = false; + _onPageNum = 0; + + auto aFoundPreviewPageIter = + std::find_if( maPreviewPages.begin(), maPreviewPages.end(), + PreviewPosInsidePagePred( rPreviewPos ) ); + + if ( aFoundPreviewPageIter != maPreviewPages.end() ) + { + // given preview position is inside a document page. + _onPageNum = (*aFoundPreviewPageIter)->pPage->GetPhyPageNum(); + _obPosInEmptyPage = (*aFoundPreviewPageIter)->pPage->IsEmptyPage(); + if ( !_obPosInEmptyPage ) + { + // given preview position inside a normal page + _orDocPos = rPreviewPos - + (*aFoundPreviewPageIter)->aPreviewWinPos + + (*aFoundPreviewPageIter)->aLogicPos; + return true; + } + } + + return false; +} + +/** determine window page scroll amount */ +SwTwips SwPagePreviewLayout::GetWinPagesScrollAmount( + const sal_Int16 _nWinPagesToScroll ) const +{ + SwTwips nScrollAmount; + if ( mbDoesLayoutRowsFitIntoWindow ) + { + nScrollAmount = (mnPreviewLayoutHeight - gnYFree) * _nWinPagesToScroll; + } + else + nScrollAmount = _nWinPagesToScroll * maPaintedPreviewDocRect.GetHeight(); + + // check, if preview layout size values are valid. + // If not, the checks for an adjustment of the scroll amount aren't useful. + if ( mbLayoutSizesValid ) + { + if ( (maPaintedPreviewDocRect.Top() + nScrollAmount) <= 0 ) + nScrollAmount = -maPaintedPreviewDocRect.Top(); + + // correct scroll amount + if ( nScrollAmount > 0 && + maPaintedPreviewDocRect.Bottom() == maPreviewDocRect.Bottom() + ) + { + nScrollAmount = 0; + } + else + { + while ( (maPaintedPreviewDocRect.Top() + nScrollAmount + gnYFree) >= maPreviewDocRect.GetHeight() ) + { + nScrollAmount -= mnRowHeight; + } + } + } + + return nScrollAmount; +} + +// methods to paint page preview layout + +namespace +{ +/// Similar to RenderContextGuard, but does not touch the draw view. +class PreviewRenderContextGuard +{ + VclPtr<vcl::RenderContext> m_pOriginalValue; + SwViewShell& m_rShell; + +public: + PreviewRenderContextGuard(SwViewShell& rShell, vcl::RenderContext* pValue) + : m_pOriginalValue(rShell.GetOut()), + m_rShell(rShell) + { + m_rShell.SetOut(pValue); + } + + ~PreviewRenderContextGuard() + { + m_rShell.SetOut(m_pOriginalValue); + } +}; +} + +/** paint prepared preview + +*/ +bool SwPagePreviewLayout::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rOutRect) const +{ + PreviewRenderContextGuard aGuard(mrParentViewShell, &rRenderContext); + // check environment and parameters + { + if (!mrParentViewShell.GetWin() && !mrParentViewShell.GetOut()->GetConnectMetaFile()) + { + return false; + } + + OSL_ENSURE(mbPaintInfoValid, "invalid preview settings - no paint of preview"); + if (!mbPaintInfoValid) + return false; + } + + // OD 17.11.2003 #i22014# - no paint, if <superfluous> flag is set at layout + if (mrLayoutRootFrame.IsSuperfluous()) + { + return true; + } + + // environment and parameter ok + + if (mbInPaint) + { + return false; + } + mbInPaint = true; + + OutputDevice* pOutputDev = &rRenderContext; + + // prepare paint + if ( !maPreviewPages.empty() ) + { + mrParentViewShell.Imp()->m_bFirstPageInvalid = false; + mrParentViewShell.Imp()->m_pFirstVisiblePage = + const_cast<SwPageFrame*>(maPreviewPages[0]->pPage); + } + + // paint preview background + { + SwRegionRects aPreviewBackgrdRegion((SwRect(rOutRect))); + // calculate preview background rectangles + for ( auto & rpPreviewPage : maPreviewPages ) + { + if ( rpPreviewPage->bVisible ) + { + aPreviewBackgrdRegion -= + SwRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize ); + } + } + // paint preview background rectangles + mrParentViewShell.PaintDesktop_(aPreviewBackgrdRegion); + } + + // prepare data for paint of pages + const tools::Rectangle aPxOutRect( pOutputDev->LogicToPixel(rOutRect) ); + + MapMode aMapMode( pOutputDev->GetMapMode() ); + MapMode aSavedMapMode = aMapMode; + + const vcl::Font& rEmptyPgFont = SwPageFrame::GetEmptyPageFont(); + + for ( auto & rpPreviewPage : maPreviewPages ) + { + if ( !rpPreviewPage->bVisible ) + continue; + + tools::Rectangle aPageRect( rpPreviewPage->aLogicPos, rpPreviewPage->aPageSize ); + aMapMode.SetOrigin( rpPreviewPage->aMapOffset ); + pOutputDev->SetMapMode( aMapMode ); + tools::Rectangle aPxPaintRect = pOutputDev->LogicToPixel( aPageRect ); + if ( aPxOutRect.Overlaps( aPxPaintRect) ) + { + const SwPageFrame* pPage = rpPreviewPage->pPage; + + if (pPage->IsEmptyPage()) + { + const Color aRetouche( mrParentViewShell.Imp()->GetRetoucheColor() ); + if( pOutputDev->GetFillColor() != aRetouche ) + pOutputDev->SetFillColor( aRetouche ); + pOutputDev->SetLineColor(); // no line color + // use aligned page rectangle + { + SwRect aTmpPageRect( aPageRect ); + ::SwAlignRect( aTmpPageRect, &mrParentViewShell, &rRenderContext ); + aPageRect = aTmpPageRect.SVRect(); + } + pOutputDev->DrawRect( aPageRect ); + + // paint empty page text + vcl::Font aOldFont( pOutputDev->GetFont() ); + pOutputDev->SetFont( rEmptyPgFont ); + pOutputDev->DrawText( aPageRect, SwResId( STR_EMPTYPAGE ), + DrawTextFlags::VCenter | + DrawTextFlags::Center | + DrawTextFlags::Clip ); + pOutputDev->SetFont( aOldFont ); + // paint shadow and border for empty page + // use new method to paint page border and shadow + SwPageFrame::PaintBorderAndShadow( SwRect(aPageRect), &mrParentViewShell, true, false, true ); + } + else + { + const bool bIsLeftShadowed = pPage->IsLeftShadowNeeded(); + const bool bIsRightShadowed = pPage->IsRightShadowNeeded(); + + mrParentViewShell.maVisArea = SwRect(aPageRect); + aPxPaintRect.Intersection( aPxOutRect ); + tools::Rectangle aPaintRect = pOutputDev->PixelToLogic( aPxPaintRect ); + mrParentViewShell.Paint(rRenderContext, aPaintRect); + + // --> OD 2007-08-15 #i80691# + // paint page border and shadow + { + SwRect aPageBorderRect; + SwPageFrame::GetBorderAndShadowBoundRect( SwRect( aPageRect ), &mrParentViewShell, &rRenderContext, aPageBorderRect, + bIsLeftShadowed, bIsRightShadowed, true ); + const vcl::Region aDLRegion(aPageBorderRect.SVRect()); + mrParentViewShell.DLPrePaint2(aDLRegion); + SwPageFrame::PaintBorderAndShadow( SwRect(aPageRect), &mrParentViewShell, true, false, true ); + mrParentViewShell.DLPostPaint2(true); + } + // <-- + } + // OD 07.11.2003 #i22014# - stop painting, because new print + // preview layout is created during paint. + if ( mbNewLayoutDuringPaint ) + { + break; + } + + if (pPage->GetPhyPageNum() == mnSelectedPageNum) + { + PaintSelectMarkAtPage(rRenderContext, rpPreviewPage.get()); + } + } + } + + // OD 17.11.2003 #i22014# - no update of accessible preview, if a new + // print preview layout is created during paint. +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if ( !mbNewLayoutDuringPaint ) + { + // update at accessibility interface + mrParentViewShell.Imp()->UpdateAccessiblePreview( + maPreviewPages, + aMapMode.GetScaleX(), + mrLayoutRootFrame.GetPageByPageNum( mnSelectedPageNum ), + maWinSize ); + } +#endif + + pOutputDev->SetMapMode( aSavedMapMode ); + mrParentViewShell.maVisArea.Clear(); + + // OD 07.11.2003 #i22014# + mbInPaint = false; + mbNewLayoutDuringPaint = false; + + return true; +} + +/** repaint pages on page preview + + OD 18.12.2002 #103492# +*/ +void SwPagePreviewLayout::Repaint( const tools::Rectangle& rInvalidCoreRect ) const +{ + // check environment and parameters + { + if ( !mrParentViewShell.GetWin() && + !mrParentViewShell.GetOut()->GetConnectMetaFile() ) + return; + + OSL_ENSURE( mbPaintInfoValid, + "invalid preview settings - no paint of preview" ); + if ( !mbPaintInfoValid ) + return; + } + + // environment and parameter ok + + // prepare paint + if ( !maPreviewPages.empty() ) + { + mrParentViewShell.Imp()->m_bFirstPageInvalid = false; + mrParentViewShell.Imp()->m_pFirstVisiblePage = + const_cast<SwPageFrame*>(maPreviewPages[0]->pPage); + } + + // invalidate visible pages, which overlap the invalid core rectangle + for ( auto & rpPreviewPage : maPreviewPages ) + { + if ( !rpPreviewPage->bVisible ) + continue; + + tools::Rectangle aPageRect( rpPreviewPage->aLogicPos, rpPreviewPage->aPageSize ); + if ( rInvalidCoreRect.Overlaps( aPageRect ) ) + { + aPageRect.Intersection(rInvalidCoreRect); + tools::Rectangle aInvalidPreviewRect = aPageRect; + aInvalidPreviewRect.SetPos( aInvalidPreviewRect.TopLeft() - + rpPreviewPage->aLogicPos + + rpPreviewPage->aPreviewWinPos ); + mrParentViewShell.GetWin()->Invalidate( aInvalidPreviewRect ); + } + } +} + +/** paint selection mark at page + + OD 17.12.2002 #103492# +*/ +void SwPagePreviewLayout::PaintSelectMarkAtPage(vcl::RenderContext& rRenderContext, + const PreviewPage* _aSelectedPreviewPage ) const +{ + OutputDevice* pOutputDev = &rRenderContext; + MapMode aMapMode( pOutputDev->GetMapMode() ); + // save mapping mode of output device + MapMode aSavedMapMode = aMapMode; + // save fill and line color of output device + Color aFill( pOutputDev->GetFillColor() ); + Color aLine( pOutputDev->GetLineColor() ); + + // determine selection mark color + Color aSelPgLineColor(117, 114, 106); + const StyleSettings& rSettings = + mrParentViewShell.GetWin()->GetSettings().GetStyleSettings(); + if ( rSettings.GetHighContrastMode() ) + aSelPgLineColor = rSettings.GetHighlightTextColor(); + + // set needed mapping mode at output device + aMapMode.SetOrigin( _aSelectedPreviewPage->aMapOffset ); + pOutputDev->SetMapMode( aMapMode ); + + // calculate page rectangle in pixel coordinates + SwRect aPageRect( _aSelectedPreviewPage->aLogicPos, + _aSelectedPreviewPage->aPageSize ); + // OD 19.02.2003 #107369# - use aligned page rectangle, as it is used for + // page border and shadow paint - see <SwPageFrame::PaintBorderAndShadow(..)> + ::SwAlignRect( aPageRect, &mrParentViewShell, pOutputDev ); + tools::Rectangle aPxPageRect = pOutputDev->LogicToPixel( aPageRect.SVRect() ); + + // draw two rectangle + // OD 19.02.2003 #107369# - adjust position of select mark rectangle + tools::Rectangle aRect( aPxPageRect.Left(), aPxPageRect.Top(), + aPxPageRect.Right(), aPxPageRect.Bottom() ); + aRect = pOutputDev->PixelToLogic( aRect ); + pOutputDev->SetFillColor(); // OD 20.02.2003 #107369# - no fill color + pOutputDev->SetLineColor( aSelPgLineColor ); + pOutputDev->DrawRect( aRect ); + // OD 19.02.2003 #107369# - adjust position of select mark rectangle + aRect = tools::Rectangle( aPxPageRect.Left()+1, aPxPageRect.Top()+1, + aPxPageRect.Right()-1, aPxPageRect.Bottom()-1 ); + aRect = pOutputDev->PixelToLogic( aRect ); + pOutputDev->DrawRect( aRect ); + + // reset fill and line color of output device + pOutputDev->SetFillColor( aFill ); + pOutputDev->SetLineColor( aLine ); + + // reset mapping mode of output device + pOutputDev->SetMapMode( aSavedMapMode ); +} + +/** paint to mark new selected page + + OD 17.12.2002 #103492# + Perform paint for current selected page in order to unmark it. + Set new selected page and perform paint to mark this page. + + @note _nSelectedPage, mnSelectedPage are absolute +*/ +void SwPagePreviewLayout::MarkNewSelectedPage( const sal_uInt16 _nSelectedPage ) +{ + const sal_uInt16 nOldSelectedPageNum = mnSelectedPageNum; + mnSelectedPageNum = _nSelectedPage; + + // re-paint for current selected page in order to unmark it. + const PreviewPage* pOldSelectedPreviewPage = GetPreviewPageByPageNum( nOldSelectedPageNum ); + OutputDevice* pOutputDev = mrParentViewShell.GetOut(); + if ( pOldSelectedPreviewPage && pOldSelectedPreviewPage->bVisible ) + { + // OD 20.02.2003 #107369# - invalidate only areas of selection mark. + SwRect aPageRect( pOldSelectedPreviewPage->aPreviewWinPos, + pOldSelectedPreviewPage->aPageSize ); + ::SwAlignRect( aPageRect, &mrParentViewShell, pOutputDev ); + tools::Rectangle aPxPageRect = pOutputDev->LogicToPixel( aPageRect.SVRect() ); + // invalidate top mark line + tools::Rectangle aInvalPxRect( aPxPageRect.Left(), aPxPageRect.Top(), + aPxPageRect.Right(), aPxPageRect.Top()+1 ); + mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) ); + // invalidate right mark line + aInvalPxRect = tools::Rectangle( aPxPageRect.Right()-1, aPxPageRect.Top(), + aPxPageRect.Right(), aPxPageRect.Bottom() ); + mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) ); + // invalidate bottom mark line + aInvalPxRect = tools::Rectangle( aPxPageRect.Left(), aPxPageRect.Bottom()-1, + aPxPageRect.Right(), aPxPageRect.Bottom() ); + mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) ); + // invalidate left mark line + aInvalPxRect = tools::Rectangle( aPxPageRect.Left(), aPxPageRect.Top(), + aPxPageRect.Left()+1, aPxPageRect.Bottom() ); + mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) ); + } + + // re-paint for new selected page in order to mark it. + const PreviewPage* pNewSelectedPreviewPage = GetPreviewPageByPageNum( _nSelectedPage ); + if ( pNewSelectedPreviewPage && pNewSelectedPreviewPage->bVisible ) + { + const PreviewPage* pSelectedPreviewPage = GetPreviewPageByPageNum(mnSelectedPageNum); + SwRect aPageRect(pSelectedPreviewPage->aPreviewWinPos, pSelectedPreviewPage->aPageSize); + ::SwAlignRect(aPageRect, &mrParentViewShell, pOutputDev); + mrParentViewShell.GetWin()->Invalidate(aPageRect.SVRect()); + } +} + +// helper methods + +namespace { + +/** get preview page by physical page number + + OD 17.12.2002 #103492# +*/ +struct EqualsPageNumPred +{ + const sal_uInt16 mnPageNum; + explicit EqualsPageNumPred(const sal_uInt16 _nPageNum) + : mnPageNum( _nPageNum ) + {} + bool operator() ( const std::unique_ptr<PreviewPage> & _pPreviewPage ) + { + return _pPreviewPage->pPage->GetPhyPageNum() == mnPageNum; + } +}; + +} + +const PreviewPage* SwPagePreviewLayout::GetPreviewPageByPageNum( const sal_uInt16 _nPageNum ) const +{ + auto aFoundPreviewPageIter = + std::find_if( maPreviewPages.begin(), maPreviewPages.end(), + EqualsPageNumPred( _nPageNum ) ); + + if ( aFoundPreviewPageIter == maPreviewPages.end() ) + return nullptr; + + return aFoundPreviewPageIter->get(); +} + +/** determine row the page with the given number is in + + OD 17.01.2003 #103492# + + @note _nPageNum is relative +*/ +sal_uInt16 SwPagePreviewLayout::GetRowOfPage( sal_uInt16 _nPageNum ) const +{ + // OD 19.02.2003 #107369# - leaving left-top-corner blank is controlled + // by <mbBookPreview>. + if ( mbBookPreview ) + { + // Note: increase given physical page number by one, because left-top-corner + // in the preview layout is left blank. + ++_nPageNum; + } + + return _nPageNum / mnCols + ((_nPageNum % mnCols)>0 ? 1 : 0); +} + +/** determine column the page with the given number is in + + OD 17.01.2003 #103492# + + @note _nPageNum is relative +*/ +sal_uInt16 SwPagePreviewLayout::GetColOfPage( sal_uInt16 _nPageNum ) const +{ + // OD 19.02.2003 #107369# - leaving left-top-corner blank is controlled + // by <mbBookPreview>. + if ( mbBookPreview ) + { + // Note: increase given physical page number by one, because left-top-corner + // in the preview layout is left blank. + ++_nPageNum; + } + + const sal_uInt16 nCol = _nPageNum % mnCols; + return nCol ? nCol : mnCols; +} + +Size SwPagePreviewLayout::GetPreviewDocSize() const +{ + OSL_ENSURE( PreviewLayoutValid(), "PagePreviewLayout not valid" ); + return maPreviewDocRect.GetSize(); +} + +/** get size of a preview page by its physical page number + + OD 15.01.2003 #103492# +*/ +Size SwPagePreviewLayout::GetPreviewPageSizeByPageNum( sal_uInt16 _nPageNum ) const +{ + const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum ); + if ( pPreviewPage ) + { + return pPreviewPage->aPageSize; + } + return Size( 0, 0 ); +} + +/** get virtual page number by its physical page number + + OD 21.03.2003 #108282# +*/ +sal_uInt16 SwPagePreviewLayout::GetVirtPageNumByPageNum( sal_uInt16 _nPageNum ) const +{ + const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum ); + if ( pPreviewPage ) + { + return pPreviewPage->pPage->GetVirtPageNum(); + } + return 0; +} + +/** Convert absolute to relative page numbers (see PrintEmptyPages) */ +sal_uInt16 SwPagePreviewLayout::ConvertAbsoluteToRelativePageNum( sal_uInt16 _nAbsPageNum ) const +{ + if ( mbBookPreview || mbPrintEmptyPages || !_nAbsPageNum ) + { + return _nAbsPageNum; + } + + const SwPageFrame* pTmpPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower()); + + sal_uInt16 nRet = 1; + + while ( pTmpPage && pTmpPage->GetPhyPageNum() != _nAbsPageNum ) + { + if ( !pTmpPage->IsEmptyPage() ) + ++nRet; + + pTmpPage = static_cast<const SwPageFrame*>( pTmpPage->GetNext() ); + } + + return nRet; +} + +/** Convert relative to absolute page numbers (see PrintEmptyPages) */ +sal_uInt16 SwPagePreviewLayout::ConvertRelativeToAbsolutePageNum( sal_uInt16 _nRelPageNum ) const +{ + if ( mbBookPreview || mbPrintEmptyPages || !_nRelPageNum ) + { + return _nRelPageNum; + } + + const SwPageFrame* pTmpPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower()); + const SwPageFrame* pRet = nullptr; + + sal_uInt16 i = 0; + while( pTmpPage && i != _nRelPageNum ) + { + if ( !pTmpPage->IsEmptyPage() ) + ++i; + + pRet = pTmpPage; + pTmpPage = static_cast<const SwPageFrame*>( pTmpPage->GetNext() ); + } + + return pRet->GetPhyPageNum(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/printdata.cxx b/sw/source/core/view/printdata.cxx new file mode 100644 index 0000000000..478e553aaf --- /dev/null +++ b/sw/source/core/view/printdata.cxx @@ -0,0 +1,447 @@ +/* -*- 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 <printdata.hxx> + +#include <strings.hrc> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <unotxdoc.hxx> +#include <wdocsh.hxx> +#include <viewsh.hxx> +#include <docfld.hxx> + +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/moduleoptions.hxx> +#include <vcl/outdev.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; + +SwRenderData::SwRenderData() +{ +} + +SwRenderData::~SwRenderData() +{ + OSL_ENSURE( !m_pPostItShell, "m_pPostItShell should already have been deleted" ); + OSL_ENSURE( !m_pPostItFields, " should already have been deleted" ); +} + +void SwRenderData::CreatePostItData( SwDoc& rDoc, const SwViewOption *pViewOpt, OutputDevice *pOutDev ) +{ + DeletePostItData(); + m_pPostItFields.reset(new SetGetExpFields); + sw_GetPostIts( rDoc.getIDocumentFieldsAccess(), m_pPostItFields.get() ); + + //!! Disable spell and grammar checking in the temporary document. + //!! Otherwise the grammar checker might process it and crash if we later on + //!! simply delete this document while he is still at it. + SwViewOption aViewOpt( *pViewOpt ); + aViewOpt.SetOnlineSpell( false ); + + m_pPostItShell.reset(new SwViewShell(*new SwDoc, nullptr, &aViewOpt, pOutDev)); +} + +void SwRenderData::DeletePostItData() +{ + if (HasPostItData()) + { + // printer needs to remain at the real document + m_pPostItShell->GetDoc()->getIDocumentDeviceAccess().setPrinter( nullptr, false, false ); + { // avoid destroying layout from SwDoc dtor + rtl::Reference<SwDoc> const xKeepAlive(m_pPostItShell->GetDoc()); + m_pPostItShell.reset(); + } + m_pPostItFields.reset(); + } +} + +void SwRenderData::SetTempDocShell(SfxObjectShellLock const& xShell) +{ + m_xTempDocShell = xShell; +} + +bool SwRenderData::NeedNewViewOptionAdjust( const SwViewShell& rCompare ) const +{ + return !(m_pViewOptionAdjust && m_pViewOptionAdjust->checkShell( rCompare )); +} + +void SwRenderData::ViewOptionAdjustStart( + SwViewShell &rSh, const SwViewOption &rViewOptions) +{ + if (m_pViewOptionAdjust) + { + OSL_FAIL("error: there should be no ViewOptionAdjust active when calling this function" ); + } + m_pViewOptionAdjust.reset( + new SwViewOptionAdjust_Impl( rSh, rViewOptions )); +} + +void SwRenderData::ViewOptionAdjust(SwPrintData const*const pPrtOptions, bool setShowPlaceHoldersInPDF) +{ + m_pViewOptionAdjust->AdjustViewOptions( pPrtOptions, setShowPlaceHoldersInPDF ); +} + +void SwRenderData::ViewOptionAdjustStop() +{ + m_pViewOptionAdjust.reset(); +} + +void SwRenderData::ViewOptionAdjustCrashPreventionKludge() +{ + m_pViewOptionAdjust->DontTouchThatViewShellItSmellsFunny(); +} + +void SwRenderData::MakeSwPrtOptions( + SwDocShell const*const pDocShell, + SwPrintUIOptions const*const pOpt, + bool const bIsPDFExport) +{ + if (!pDocShell || !pOpt) + return; + + m_pPrtOptions.reset(new SwPrintData); + SwPrintData & rOptions(*m_pPrtOptions); + + // get default print options + bool bWeb = dynamic_cast<const SwWebDocShell*>( pDocShell) != nullptr; + ::sw::InitPrintOptionsFromApplication(rOptions, bWeb); + + // get print options to use from provided properties + rOptions.m_bPrintGraphic = pOpt->IsPrintGraphics(); + rOptions.m_bPrintTable = true; // for now it was decided that tables should always be printed + rOptions.m_bPrintDraw = pOpt->IsPrintDrawings(); + rOptions.m_bPrintControl = pOpt->IsPrintFormControls(); + rOptions.m_bPrintLeftPages = pOpt->IsPrintLeftPages(); + rOptions.m_bPrintRightPages = pOpt->IsPrintRightPages(); + rOptions.m_bPrintPageBackground = pOpt->IsPrintPageBackground(); + rOptions.m_bPrintEmptyPages = pOpt->IsPrintEmptyPages( bIsPDFExport ); + // bUpdateFieldsInPrinting <-- not set here; mail merge only + rOptions.m_bPaperFromSetup = pOpt->IsPaperFromSetup(); + rOptions.m_bPrintReverse = false; /*handled by print dialog now*/ + rOptions.m_bPrintProspect = pOpt->IsPrintProspect(); + rOptions.m_bPrintProspectRTL = pOpt->IsPrintProspectRTL(); + // bPrintSingleJobs <-- not set here; mail merge and or configuration + // bModified <-- not set here; mail merge only + rOptions.m_bPrintBlackFont = pOpt->IsPrintWithBlackTextColor(); + rOptions.m_bPrintHiddenText = pOpt->IsPrintHiddenText(); + rOptions.m_bPrintTextPlaceholder = pOpt->IsPrintTextPlaceholders(); + rOptions.m_nPrintPostIts = pOpt->GetPrintPostItsType(); + + //! needs to be set after MakeOptions since the assignment operation in that + //! function will destroy the pointers + rOptions.SetRenderData( this ); +} + +SwPrintUIOptions::SwPrintUIOptions( + sal_uInt16 nCurrentPage, + bool bWeb, + bool bSwSrcView, + bool bHasSelection, + bool bHasPostIts, + const SwPrintData &rDefaultPrintData ) : + m_rDefaultPrintData( rDefaultPrintData ) +{ + // printing HTML sources does not have any valid UI options. + // It's just the source code that gets printed... + if (bSwSrcView || utl::ConfigManager::IsFuzzing()) + { + m_aUIProperties.clear(); + return; + } + + // check if either CJK or CTL is enabled + bool bRTL = SvtCJKOptions::IsCJKFontEnabled() || SvtCTLOptions::IsCTLFontEnabled(); + + // create sequence of print UI options + // (5 options are not available for Writer-Web) + const int nRTLOpts = bRTL ? 1 : 0; + const int nNumProps = nRTLOpts + (bWeb ? 15 : 19); + m_aUIProperties.resize( nNumProps); + int nIdx = 0; + + // load the writer PrinterOptions into the custom tab + m_aUIProperties[nIdx].Name = "OptionsUIFile"; + m_aUIProperties[nIdx++].Value <<= OUString("modules/swriter/ui/printeroptions.ui"); + + // create "writer" section (new tab page in dialog) + SvtModuleOptions aModOpt; + OUString aAppGroupname( SwResId( STR_PRINTOPTUI_PRODUCTNAME) ); + aAppGroupname = aAppGroupname.replaceFirst( "%s", aModOpt.GetModuleName( SvtModuleOptions::EModule::WRITER ) ); + m_aUIProperties[ nIdx++ ].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage"); + + // create sub section for Contents + m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("contents", SwResId( STR_PRINTOPTUI_CONTENTS), OUString()); + + // create a bool option for background + bool bDefaultVal = rDefaultPrintData.IsPrintPageBackground(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("pagebackground", SwResId( STR_PRINTOPTUI_PAGE_BACKGROUND), + ".HelpID:vcl:PrintDialog:PrintPageBackground:CheckBox", + "PrintPageBackground", + bDefaultVal); + + // create a bool option for pictures/graphics AND OLE and drawing objects as well + bDefaultVal = rDefaultPrintData.IsPrintGraphic() || rDefaultPrintData.IsPrintDraw(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("pictures", SwResId( STR_PRINTOPTUI_PICTURES), + ".HelpID:vcl:PrintDialog:PrintPicturesAndObjects:CheckBox", + "PrintPicturesAndObjects", + bDefaultVal); + if (!bWeb) + { + // create a bool option for hidden text + bDefaultVal = rDefaultPrintData.IsPrintHiddenText(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("hiddentext", SwResId( STR_PRINTOPTUI_HIDDEN), + ".HelpID:vcl:PrintDialog:PrintHiddenText:CheckBox", + "PrintHiddenText", + bDefaultVal); + + // create a bool option for place holder + bDefaultVal = rDefaultPrintData.IsPrintTextPlaceholder(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("placeholders", SwResId( STR_PRINTOPTUI_TEXT_PLACEHOLDERS), + ".HelpID:vcl:PrintDialog:PrintTextPlaceholder:CheckBox", + "PrintTextPlaceholder", + bDefaultVal); + } + + // create a bool option for controls + bDefaultVal = rDefaultPrintData.IsPrintControl(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("formcontrols", SwResId( STR_PRINTOPTUI_FORM_CONTROLS), + ".HelpID:vcl:PrintDialog:PrintControls:CheckBox", + "PrintControls", + bDefaultVal); + + // create sub section for Color + m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("color", SwResId( STR_PRINTOPTUI_COLOR), OUString()); + + // create a bool option for printing text with black font color + bDefaultVal = rDefaultPrintData.IsPrintBlackFont(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("textinblack", SwResId( STR_PRINTOPTUI_PRINT_BLACK), + ".HelpID:vcl:PrintDialog:PrintBlackFonts:CheckBox", + "PrintBlackFonts", + bDefaultVal); + + if (!bWeb) + { + // create subgroup for misc options + m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("pages", SwResId( STR_PRINTOPTUI_PAGES_TEXT), OUString()); + + // create a bool option for printing automatically inserted blank pages + bDefaultVal = rDefaultPrintData.IsPrintEmptyPages(); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("autoblankpages", SwResId( STR_PRINTOPTUI_PRINT_BLANK), + ".HelpID:vcl:PrintDialog:PrintEmptyPages:CheckBox", + "PrintEmptyPages", + bDefaultVal); + } + + // create a bool option for paper tray + bDefaultVal = rDefaultPrintData.IsPaperFromSetup(); + vcl::PrinterOptionsHelper::UIControlOptions aPaperTrayOpt; + aPaperTrayOpt.maGroupHint = "OptionsPageOptGroup"; + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("printpaperfromsetup", SwResId( STR_PRINTOPTUI_ONLY_PAPER), + ".HelpID:vcl:PrintDialog:PrintPaperFromSetup:CheckBox", + "PrintPaperFromSetup", + bDefaultVal, + aPaperTrayOpt); + + // print range selection + vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt; + aPrintRangeOpt.maGroupHint = "PrintRange"; + aPrintRangeOpt.mbInternalOnly = true; + m_aUIProperties[nIdx++].Value = setSubgroupControlOpt( "printrange", + SwResId( STR_PRINTOPTUI_PAGES_TEXT ), + OUString(), + aPrintRangeOpt ); + + // create a choice for the content to create + static constexpr OUString aPrintRangeName( u"PrintContent"_ustr ); + uno::Sequence< OUString > aChoices{ SwResId( STR_PRINTOPTUI_PRINTALLPAGES ), + SwResId( STR_PRINTOPTUI_PRINTPAGES ), + SwResId( STR_PRINTOPTUI_PRINTSELECTION ) }; + uno::Sequence< sal_Bool > aChoicesDisabled{ false, false, !bHasSelection }; + uno::Sequence< OUString > aHelpIds{ ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:2" }; + uno::Sequence< OUString > aWidgetIds{ "rbAllPages", "rbRangePages", "rbRangeSelection" }; + m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(), + aHelpIds, aPrintRangeName, + aChoices, 0 /* always default to 'All pages' */, + aChoicesDisabled); + + // show an Edit dependent on "Pages" selected + vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true ); + m_aUIProperties[nIdx++].Value = setEditControlOpt("pagerange", OUString(), + ".HelpID:vcl:PrintDialog:PageRange:Edit", + "PageRange", + OUString::number( nCurrentPage ) /* set text box to current page number */, + aPageRangeOpt); + + vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, -1, true); + m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("evenoddbox", + OUString(), + uno::Sequence<OUString>(), + "EvenOdd", + uno::Sequence<OUString>(), + 0, + uno::Sequence< sal_Bool >(), + aEvenOddOpt); + + // create a list box for notes content + const SwPostItMode nPrintPostIts = rDefaultPrintData.GetPrintPostIts(); + aChoices = { SwResId( STR_PRINTOPTUI_NONE), + SwResId( STR_PRINTOPTUI_COMMENTS_ONLY), + SwResId( STR_PRINTOPTUI_PLACE_END), + SwResId( STR_PRINTOPTUI_PLACE_PAGE), + SwResId( STR_PRINTOPTUI_PLACE_MARGINS) }; + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintAnnotationMode:FixedText", + ".HelpID:vcl:PrintDialog:PrintAnnotationMode:ListBox" }; + vcl::PrinterOptionsHelper::UIControlOptions aAnnotOpt( "PrintProspect", 0, false ); + aAnnotOpt.mbEnabled = bHasPostIts; + m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("writercomments", + SwResId( STR_PRINTOPTUI_COMMENTS), + aHelpIds, + "PrintAnnotationMode", + aChoices, + bHasPostIts ? static_cast<sal_uInt16>(nPrintPostIts) : 0, + uno::Sequence< sal_Bool >(), + aAnnotOpt); + + // create subsection for Page settings + vcl::PrinterOptionsHelper::UIControlOptions aPageSetOpt; + aPageSetOpt.maGroupHint = "LayoutPage"; + + // create a bool option for brochure + bDefaultVal = rDefaultPrintData.IsPrintProspect(); + static constexpr OUString aBrochurePropertyName( u"PrintProspect"_ustr ); + m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("brochure", SwResId( STR_PRINTOPTUI_BROCHURE), + ".HelpID:vcl:PrintDialog:PrintProspect:CheckBox", + aBrochurePropertyName, + bDefaultVal, + aPageSetOpt); + + if (bRTL) + { + // create a bool option for brochure RTL dependent on brochure + uno::Sequence< OUString > aBRTLChoices{ SwResId( STR_PRINTOPTUI_LEFT_SCRIPT), + SwResId( STR_PRINTOPTUI_RIGHT_SCRIPT) }; + vcl::PrinterOptionsHelper::UIControlOptions aBrochureRTLOpt( aBrochurePropertyName, -1, true ); + uno::Sequence<OUString> aBRTLHelpIds { ".HelpID:vcl:PrintDialog:PrintProspectRTL:ListBox" }; + aBrochureRTLOpt.maGroupHint = "LayoutPage"; + // RTL brochure choices + // 0 : left-to-right + // 1 : right-to-left + const sal_Int16 nBRTLChoice = rDefaultPrintData.IsPrintProspectRTL() ? 1 : 0; + m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("scriptdirection", + OUString(), + aBRTLHelpIds, + "PrintProspectRTL", + aBRTLChoices, + nBRTLChoice, + uno::Sequence< sal_Bool >(), + aBrochureRTLOpt); + } + + assert(nIdx == nNumProps); +} + +SwPrintUIOptions::~SwPrintUIOptions() +{ +} + +bool SwPrintUIOptions::IsPrintLeftPages() const +{ + // take care of different property names for the option. + // for compatibility the old name should win (may still be used for PDF export or via Uno API) + + // 0: left and right pages + // 1: left pages only + // 2: right pages only + sal_Int64 nEOPages = getIntValue( "EvenOdd", 0 /* default: all */ ); + bool bRes = nEOPages != 1; + bRes = getBoolValue( "EvenOdd", bRes /* <- default value if property is not found */ ); + return bRes; +} + +bool SwPrintUIOptions::IsPrintRightPages() const +{ + // take care of different property names for the option. + // for compatibility the old name should win (may still be used for PDF export or via Uno API) + + sal_Int64 nEOPages = getIntValue( "EvenOdd", 0 /* default: all */ ); + bool bRes = nEOPages != 2; + bRes = getBoolValue( "EvenOdd", bRes /* <- default value if property is not found */ ); + return bRes; +} + +bool SwPrintUIOptions::IsPrintEmptyPages( bool bIsPDFExport ) const +{ + // take care of different property names for the option. + + bool bRes = bIsPDFExport ? + !getBoolValue( "IsSkipEmptyPages", true ) : + getBoolValue( "PrintEmptyPages", true ); + return bRes; +} + +bool SwPrintUIOptions::IsPrintGraphics() const +{ + // take care of different property names for the option. + // for compatibility the old name should win (may still be used for PDF export or via Uno API) + + bool bRes = getBoolValue( "PrintPicturesAndObjects", true ); + bRes = getBoolValue( "PrintGraphics", bRes ); + return bRes; +} + +bool SwPrintUIOptions::IsPrintDrawings() const +{ + // take care of different property names for the option. + // for compatibility the old name should win (may still be used for PDF export or via Uno API) + + bool bRes = getBoolValue( "PrintPicturesAndObjects", true ); + bRes = getBoolValue( "PrintDrawings", bRes ); + return bRes; +} + +bool SwPrintUIOptions::processPropertiesAndCheckFormat( const uno::Sequence< beans::PropertyValue >& i_rNewProp ) +{ + bool bChanged = processProperties( i_rNewProp ); + + uno::Reference< awt::XDevice > xRenderDevice; + uno::Any aVal( getValue( "RenderDevice" ) ); + aVal >>= xRenderDevice; + + VclPtr< OutputDevice > pOut; + if (xRenderDevice.is()) + { + VCLXDevice* pDevice = dynamic_cast<VCLXDevice*>( xRenderDevice.get() ); + if (pDevice) + pOut = pDevice->GetOutputDevice(); + } + bChanged = bChanged || (pOut.get() != m_pLast.get()); + if( pOut ) + m_pLast = pOut; + + return bChanged; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/vdraw.cxx b/sw/source/core/view/vdraw.cxx new file mode 100644 index 0000000000..5a95053cd7 --- /dev/null +++ b/sw/source/core/view/vdraw.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <officecfg/Office/Common.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdpagv.hxx> +#include <fmtanchr.hxx> +#include <frmfmt.hxx> + +#include <svx/svdoutl.hxx> + +#include <drawdoc.hxx> +#include <fesh.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <viewimp.hxx> +#include <dflyobj.hxx> +#include <printdata.hxx> +#include <dcontact.hxx> +#include <dview.hxx> +#include <flyfrm.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/canvastools.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <basegfx/range/b2irectangle.hxx> + +#include <IDocumentDrawModelAccess.hxx> + +void SwViewShellImp::StartAction() +{ + if ( HasDrawView() ) + { + CurrShell aCurr( GetShell() ); + if ( auto pFEShell = dynamic_cast<SwFEShell*>( m_pShell) ) + pFEShell->HideChainMarker(); // might have changed + } +} + +void SwViewShellImp::EndAction() +{ + if ( HasDrawView() ) + { + CurrShell aCurr( GetShell() ); + if ( auto pFEShell = dynamic_cast<SwFEShell*>(m_pShell) ) + pFEShell->SetChainMarker(); // might have changed + } +} + +void SwViewShellImp::LockPaint() +{ + if ( HasDrawView() ) + { + m_bResetHdlHiddenPaint = !GetDrawView()->areMarkHandlesHidden(); + GetDrawView()->hideMarkHandles(); + } + else + { + m_bResetHdlHiddenPaint = false; + } +} + +void SwViewShellImp::UnlockPaint() +{ + if ( m_bResetHdlHiddenPaint ) + GetDrawView()->showMarkHandles(); +} + +void SwViewShellImp::PaintLayer( const SdrLayerID _nLayerID, + SwPrintData const*const pPrintData, + SwPageFrame const& rPageFrame, + const SwRect& aPaintRect, + const Color* _pPageBackgrdColor, + const bool _bIsPageRightToLeft, + sdr::contact::ViewObjectContactRedirector* pRedirector ) +{ + if ( !HasDrawView() ) + return; + + //change the draw mode in high contrast mode + OutputDevice* pOutDev = GetShell()->GetOut(); + DrawModeFlags nOldDrawMode = pOutDev->GetDrawMode(); + if( GetShell()->GetWin() && + Application::GetSettings().GetStyleSettings().GetHighContrastMode() && + (!GetShell()->IsPreview() || officecfg::Office::Common::Accessibility::IsForPagePreviews::get())) + { + pOutDev->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | + DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ); + } + + // For correct handling of accessibility, high contrast, the + // page background color is set as the background color at the + // outliner of the draw view. Only necessary for the layers + // hell and heaven + Color aOldOutlinerBackgroundColor; + // set default horizontal text direction on painting <hell> or + // <heaven>. + EEHorizontalTextDirection aOldEEHoriTextDir = EEHorizontalTextDirection::L2R; + const IDocumentDrawModelAccess& rIDDMA = GetShell()->getIDocumentDrawModelAccess(); + if ( (_nLayerID == rIDDMA.GetHellId()) || + (_nLayerID == rIDDMA.GetHeavenId()) ) + { + OSL_ENSURE( _pPageBackgrdColor, + "incorrect usage of SwViewShellImp::PaintLayer: pPageBackgrdColor have to be set for painting layer <hell> or <heaven>"); + if ( _pPageBackgrdColor ) + { + aOldOutlinerBackgroundColor = + GetDrawView()->GetModel().GetDrawOutliner().GetBackgroundColor(); + GetDrawView()->GetModel().GetDrawOutliner().SetBackgroundColor( *_pPageBackgrdColor ); + } + + aOldEEHoriTextDir = + GetDrawView()->GetModel().GetDrawOutliner().GetDefaultHorizontalTextDirection(); + EEHorizontalTextDirection aEEHoriTextDirOfPage = + _bIsPageRightToLeft ? EEHorizontalTextDirection::R2L : EEHorizontalTextDirection::L2R; + GetDrawView()->GetModel().GetDrawOutliner().SetDefaultHorizontalTextDirection( aEEHoriTextDirOfPage ); + } + + pOutDev->Push( vcl::PushFlags::LINECOLOR ); + if (pPrintData) + { + // hide drawings but not form controls (form controls are handled elsewhere) + SdrView &rSdrView = GetPageView()->GetView(); + rSdrView.setHideDraw( !pPrintData->IsPrintDraw() ); + } + basegfx::B2IRectangle const pageFrame = vcl::unotools::b2IRectangleFromRectangle(rPageFrame.getFrameArea().SVRect()); + GetPageView()->DrawLayer(_nLayerID, pOutDev, pRedirector, aPaintRect.SVRect(), &pageFrame); + pOutDev->Pop(); + + // reset background color of the outliner & default horiz. text dir. + if ( (_nLayerID == rIDDMA.GetHellId()) || + (_nLayerID == rIDDMA.GetHeavenId()) ) + { + GetDrawView()->GetModel().GetDrawOutliner().SetBackgroundColor( aOldOutlinerBackgroundColor ); + GetDrawView()->GetModel().GetDrawOutliner().SetDefaultHorizontalTextDirection( aOldEEHoriTextDir ); + } + + pOutDev->SetDrawMode( nOldDrawMode ); + +} + +#define FUZZY_EDGE 400 + +bool SwViewShellImp::IsDragPossible( const Point &rPoint ) +{ + if ( !HasDrawView() ) + return false; + + const SdrMarkList &rMrkList = GetDrawView()->GetMarkedObjectList(); + + if( !rMrkList.GetMarkCount() ) + return false; + + SdrObject *pO = rMrkList.GetMark(rMrkList.GetMarkCount()-1)->GetMarkedSdrObj(); + + SwRect aRect; + if( pO && ::CalcClipRect( pO, aRect, false ) ) + { + SwRect aTmp; + ::CalcClipRect( pO, aTmp ); + aRect.Union( aTmp ); + } + else + aRect = GetShell()->GetLayout()->getFrameArea(); + + aRect.AddTop (- FUZZY_EDGE ); + aRect.AddBottom( FUZZY_EDGE ); + aRect.AddLeft (- FUZZY_EDGE ); + aRect.AddRight ( FUZZY_EDGE ); + return aRect.Contains( rPoint ); +} + +void SwViewShellImp::NotifySizeChg( const Size &rNewSz ) +{ + if ( !HasDrawView() ) + return; + + if ( GetPageView() ) + GetPageView()->GetPage()->SetSize( rNewSz ); + + // Limitation of the work area + const tools::Rectangle aDocRect( Point( DOCUMENTBORDER, DOCUMENTBORDER ), rNewSz ); + const tools::Rectangle &rOldWork = GetDrawView()->GetWorkArea(); + bool bCheckDrawObjs = false; + if ( aDocRect != rOldWork ) + { + if ( rOldWork.Bottom() > aDocRect.Bottom() || rOldWork.Right() > aDocRect.Right()) + bCheckDrawObjs = true; + GetDrawView()->SetWorkArea( aDocRect ); + } + if ( !bCheckDrawObjs ) + return; + + OSL_ENSURE( m_pShell->getIDocumentDrawModelAccess().GetDrawModel(), "NotifySizeChg without DrawModel" ); + SdrPage* pPage = m_pShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ); + for (const rtl::Reference<SdrObject>& pObj : *pPage) + { + if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj.get()) == nullptr ) + { + // Objects not anchored to the frame, do not need to be adjusted + const SwContact *pCont = GetUserCall(pObj.get()); + // this function might be called by the InsertDocument, when + // a PageDesc-Attribute is set on a node. Then the SdrObject + // must not have an UserCall. + if( !pCont ) + continue; + auto pDrawContact = dynamic_cast<const SwDrawContact*>( pCont); + if( !pDrawContact ) + continue; + + const SwFrame *pAnchor = pDrawContact->GetAnchorFrame(); + if ( !pAnchor || pAnchor->IsInFly() || !pAnchor->isFrameAreaDefinitionValid() || + !pAnchor->GetUpper() || !pAnchor->FindPageFrame() || + (RndStdIds::FLY_AS_CHAR == pCont->GetFormat()->GetAnchor().GetAnchorId()) ) + { + continue; + } + else + { + // Actually this should never happen but currently layouting + // is broken. So don't move anchors, if the page is invalid. + // This should be turned into a DBG_ASSERT, once layouting is fixed! + const SwPageFrame *pPageFrame = pAnchor->FindPageFrame(); + if (!pPageFrame || pPageFrame->IsInvalid() ) { + SAL_WARN( "sw.core", "Trying to move anchor from invalid page - fix layouting!" ); + continue; + } + } + + // no move for drawing objects in header/footer + if ( pAnchor->FindFooterOrHeader() ) + { + continue; + } + + const tools::Rectangle aObjBound( pObj->GetCurrentBoundRect() ); + if ( !aDocRect.Contains( aObjBound ) ) + { + Size aSz; + if ( aObjBound.Left() > aDocRect.Right() ) + aSz.setWidth( (aDocRect.Right() - aObjBound.Left()) - MINFLY ); + if ( aObjBound.Top() > aDocRect.Bottom() ) + aSz.setHeight( (aDocRect.Bottom() - aObjBound.Top()) - MINFLY ); + if ( aSz.Width() || aSz.Height() ) + pObj->Move( aSz ); + + // Don't let large objects disappear to the top + aSz.setWidth(0); + aSz.setHeight(0); + if ( aObjBound.Right() < aDocRect.Left() ) + aSz.setWidth( (aDocRect.Left() - aObjBound.Right()) + MINFLY ); + if ( aObjBound.Bottom() < aDocRect.Top() ) + aSz.setHeight( (aDocRect.Top() - aObjBound.Bottom()) + MINFLY ); + if ( aSz.Width() || aSz.Height() ) + pObj->Move( aSz ); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/viewimp.cxx b/sw/source/core/view/viewimp.cxx new file mode 100644 index 0000000000..3f427ed773 --- /dev/null +++ b/sw/source/core/view/viewimp.cxx @@ -0,0 +1,528 @@ +/* -*- 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 <config_wasm_strip.h> + +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <viewimp.hxx> +#include <viewopt.hxx> +#include <flyfrm.hxx> +#include <layact.hxx> +#include <dview.hxx> +#include <svx/svdpage.hxx> +#include <accmap.hxx> + +#include <officecfg/Office/Common.hxx> +#include <pagepreviewlayout.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <drawdoc.hxx> +#include <prevwpage.hxx> +#include <sfx2/viewsh.hxx> + +void SwViewShellImp::Init( const SwViewOption *pNewOpt ) +{ + OSL_ENSURE( m_pDrawView, "SwViewShellImp::Init without DrawView" ); + //Create PageView if it doesn't exist + SwRootFrame *pRoot = m_pShell->GetLayout(); + if ( !m_pSdrPageView ) + { + IDocumentDrawModelAccess& rIDDMA = m_pShell->getIDocumentDrawModelAccess(); + if ( !pRoot->GetDrawPage() ) + pRoot->SetDrawPage( rIDDMA.GetDrawModel()->GetPage( 0 ) ); + + if ( pRoot->GetDrawPage()->GetSize() != pRoot->getFrameArea().SSize() ) + pRoot->GetDrawPage()->SetSize( pRoot->getFrameArea().SSize() ); + + m_pSdrPageView = m_pDrawView->ShowSdrPage( pRoot->GetDrawPage()); + // Notify drawing page view about invisible layers + rIDDMA.NotifyInvisibleLayers( *m_pSdrPageView ); + } + m_pDrawView->SetDragStripes( pNewOpt->IsCrossHair() ); + m_pDrawView->SetGridSnap( pNewOpt->IsSnap() ); + m_pDrawView->SetGridVisible( pNewOpt->IsGridVisible() ); + const Size &rSz = pNewOpt->GetSnapSize(); + m_pDrawView->SetGridCoarse( rSz ); + const Size aFSize + ( rSz.Width() ? rSz.Width() /std::max(short(1),pNewOpt->GetDivisionX()):0, + rSz.Height()? rSz.Height()/std::max(short(1),pNewOpt->GetDivisionY()):0); + m_pDrawView->SetGridFine( aFSize ); + Fraction aSnGrWdtX(rSz.Width(), pNewOpt->GetDivisionX() + 1); + Fraction aSnGrWdtY(rSz.Height(), pNewOpt->GetDivisionY() + 1); + m_pDrawView->SetSnapGridWidth( aSnGrWdtX, aSnGrWdtY ); + + if ( pRoot->getFrameArea().HasArea() ) + m_pDrawView->SetWorkArea( pRoot->getFrameArea().SVRect() ); + + if ( GetShell()->IsPreview() ) + m_pDrawView->SetAnimationEnabled( false ); + + m_pDrawView->SetUseIncompatiblePathCreateInterface( false ); + + // set handle size to 9 pixels, always + m_pDrawView->SetMarkHdlSizePixel(9); +} + +/// CTor for the core internals +SwViewShellImp::SwViewShellImp( SwViewShell *pParent ) : + m_pShell( pParent ), + m_pSdrPageView( nullptr ), + m_pFirstVisiblePage( nullptr ), + m_pLayAction( nullptr ), + m_pIdleAct( nullptr ), + m_bFirstPageInvalid( true ), + m_bResetHdlHiddenPaint( false ), + m_bSmoothUpdate( false ), + m_bStopSmooth( false ), + m_nRestoreActions( 0 ) +{ +} + +SwViewShellImp::~SwViewShellImp() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + m_pAccessibleMap.reset(); +#endif + + m_pPagePreviewLayout.reset(); + + // Make sure HideSdrPage is also executed after ShowSdrPage. + if( m_pDrawView ) + m_pDrawView->HideSdrPage(); + + m_pDrawView.reset(); + + DeletePaintRegion(); + + OSL_ENSURE( !m_pLayAction, "Have action for the rest of your life." ); + OSL_ENSURE( !m_pIdleAct,"Be idle for the rest of your life." ); +} + +bool SwViewShellImp::AddPaintRect( const SwRect &rRect ) +{ + // In case of tiled rendering the visual area is the last painted tile -> not interesting. + if ( rRect.Overlaps( m_pShell->VisArea() ) || comphelper::LibreOfficeKit::isActive() ) + { + if ( !m_oPaintRegion ) + { + // In case of normal rendering, this makes sure only visible rectangles are painted. + // Otherwise get the rectangle of the full document, so all paint rectangles are invalidated. + const SwRect& rArea = comphelper::LibreOfficeKit::isActive() ? m_pShell->GetLayout()->getFrameArea() : m_pShell->VisArea(); + m_oPaintRegion.emplace(); + m_oPaintRegion->ChangeOrigin(rArea); + } + if(!m_oPaintRegion->empty()) + { + // This function often gets called with rectangles that line up vertically. + // Try to extend the last one downwards to include the new one (use Union() + // in case the new one is actually already contained in the last one). + SwRect& last = m_oPaintRegion->back(); + if(last.Left() == rRect.Left() && last.Width() == rRect.Width() + && last.Bottom() + 1 >= rRect.Top() && last.Bottom() <= rRect.Bottom()) + { + last.Union(rRect); + // And these rectangles lined up vertically often come up in groups + // that line up horizontally. Try to extend the previous rectangle + // to the right to include the last one. + if(m_oPaintRegion->size() > 1) + { + SwRect& last2 = (*m_oPaintRegion)[m_oPaintRegion->size() - 2]; + if(last2.Top() == last.Top() && last2.Height() == last.Height() + && last2.Right() + 1 >= last.Left() && last2.Right() <= last2.Right()) + { + last2.Union(last); + m_oPaintRegion->pop_back(); + return true; + } + } + return true; + } + } + (*m_oPaintRegion) += rRect; + return true; + } + return false; +} + +void SwViewShellImp::AddPendingLOKInvalidation( const SwRect& rRect ) +{ + std::vector<SwRect>& l = m_pendingLOKInvalidations; + if(l.empty() && m_pShell && m_pShell->GetSfxViewShell()) // Announce that these invalidations will need flushing. + m_pShell->GetSfxViewShell()->libreOfficeKitViewAddPendingInvalidateTiles(); + // These are often repeated, so check first for duplicates. + if( std::find( l.begin(), l.end(), rRect ) == l.end()) + l.push_back( rRect ); +} + +std::vector<SwRect> SwViewShellImp::TakePendingLOKInvalidations() +{ + std::vector<SwRect> ret; + std::swap(ret, m_pendingLOKInvalidations); + return ret; +} + +void SwViewShellImp::CheckWaitCursor() +{ + if ( m_pLayAction ) + m_pLayAction->CheckWaitCursor(); +} + +bool SwViewShellImp::IsCalcLayoutProgress() const +{ + return m_pLayAction && m_pLayAction->IsCalcLayout(); +} + +bool SwViewShellImp::IsUpdateExpFields() +{ + if ( m_pLayAction && m_pLayAction->IsCalcLayout() ) + { + m_pLayAction->SetUpdateExpFields(); + return true; + } + return false; +} + +void SwViewShellImp::SetFirstVisPage(OutputDevice const * pRenderContext) +{ + if ( m_pShell->mbDocSizeChgd && m_pShell->VisArea().Top() > m_pShell->GetLayout()->getFrameArea().Height() ) + { + //We are in an action and because of erase actions the VisArea is + //after the first visible page. + //To avoid excessive formatting, hand back the last page. + m_pFirstVisiblePage = static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower()); + while ( m_pFirstVisiblePage && m_pFirstVisiblePage->GetNext() ) + m_pFirstVisiblePage = static_cast<SwPageFrame*>(m_pFirstVisiblePage->GetNext()); + } + else + { + const SwViewOption* pSwViewOption = GetShell()->GetViewOptions(); + const bool bBookMode = pSwViewOption->IsViewLayoutBookMode(); + + SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower()); + SwRect aPageRect = pPage->GetBoundRect(pRenderContext); + while ( pPage && !aPageRect.Overlaps( m_pShell->VisArea() ) ) + { + pPage = static_cast<SwPageFrame*>(pPage->GetNext()); + if ( pPage ) + { + aPageRect = pPage->GetBoundRect(pRenderContext); + if ( bBookMode && pPage->IsEmptyPage() ) + { + const SwPageFrame& rFormatPage = pPage->GetFormatPage(); + aPageRect.SSize( rFormatPage.GetBoundRect(pRenderContext).SSize() ); + } + } + } + m_pFirstVisiblePage = pPage ? pPage : static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower()); + } + m_bFirstPageInvalid = false; +} + +void SwViewShellImp::MakeDrawView() +{ + IDocumentDrawModelAccess& rIDDMA = GetShell()->getIDocumentDrawModelAccess(); + + // the else here is not an error, MakeDrawModel_() calls this method again + // after the DrawModel is created to create DrawViews for all shells... + if( !rIDDMA.GetDrawModel() ) + { + rIDDMA.MakeDrawModel_(); + } + else + { + if ( !m_pDrawView ) + { + // #i72809# + // Discussed with FME, he also thinks that the getPrinter is old and not correct. When i got + // him right, it anyways returns GetOut() when it's a printer, but NULL when not. He suggested + // to use GetOut() and check the existing cases. + // Check worked well. Took a look at viewing, printing, PDF export and print preview with a test + // document which has an empty 2nd page (right page, see bug) + auto pWin = GetShell()->GetWin(); + OutputDevice* pOutDevForDrawView = pWin ? pWin->GetOutDev() : nullptr; + + if(!pOutDevForDrawView) + { + pOutDevForDrawView = GetShell()->GetOut(); + } + + m_pDrawView.reset( new SwDrawView( + *this, + *rIDDMA.GetOrCreateDrawModel(), + pOutDevForDrawView) ); + } + + GetDrawView()->SetActiveLayer("Heaven"); + const SwViewOption* pSwViewOption = GetShell()->GetViewOptions(); + Init(pSwViewOption); + + // #i68597# If document is read-only, we will not profit from overlay, + // so switch it off. + if (m_pDrawView->IsBufferedOverlayAllowed()) + { + if(pSwViewOption->IsReadonly()) + { + m_pDrawView->SetBufferedOverlayAllowed(false); + } + } + } +} + +Color SwViewShellImp::GetRetoucheColor() const +{ + Color aRet( COL_TRANSPARENT ); + const SwViewShell &rSh = *GetShell(); + if (rSh.GetWin() || rSh.isOutputToWindow()) + { + if ( rSh.GetViewOptions()->getBrowseMode() && + COL_TRANSPARENT != rSh.GetViewOptions()->GetRetoucheColor() ) + aRet = rSh.GetViewOptions()->GetRetoucheColor(); + else if(rSh.GetViewOptions()->IsPagePreview() && + !officecfg::Office::Common::Accessibility::IsForPagePreviews::get()) + aRet = COL_WHITE; + else + aRet = rSh.GetViewOptions()->GetDocColor(); + } + return aRet; +} + +SwPageFrame *SwViewShellImp::GetFirstVisPage(OutputDevice const * pRenderContext) +{ + if ( m_bFirstPageInvalid ) + SetFirstVisPage(pRenderContext); + return m_pFirstVisiblePage; +} + +const SwPageFrame *SwViewShellImp::GetFirstVisPage(OutputDevice const * pRenderContext) const +{ + if ( m_bFirstPageInvalid ) + const_cast<SwViewShellImp*>(this)->SetFirstVisPage(pRenderContext); + return m_pFirstVisiblePage; +} + +const SwPageFrame* SwViewShellImp::GetLastVisPage(const OutputDevice* pRenderContext) const +{ + const SwViewOption* pSwViewOption = m_pShell->GetViewOptions(); + const bool bBookMode = pSwViewOption->IsViewLayoutBookMode(); + const SwPageFrame* pPage = GetFirstVisPage(pRenderContext); + const SwPageFrame* pLastVisPage = pPage; + SwRect aPageRect = pPage->GetBoundRect(pRenderContext); + while (pPage && (pPage->IsEmptyPage() || aPageRect.Overlaps(m_pShell->VisArea()))) + { + pLastVisPage = pPage; + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + if (pPage) + { + aPageRect = pPage->GetBoundRect(pRenderContext); + if (bBookMode && pPage->IsEmptyPage()) + { + const SwPageFrame& rFormatPage = pPage->GetFormatPage(); + aPageRect.SSize(rFormatPage.GetBoundRect(pRenderContext).SSize()); + } + } + } + return pLastVisPage; +} + +// create page preview layout +void SwViewShellImp::InitPagePreviewLayout() +{ + OSL_ENSURE( m_pShell->GetLayout(), "no layout - page preview layout can not be created."); + if ( m_pShell->GetLayout() ) + m_pPagePreviewLayout.reset( new SwPagePreviewLayout( *m_pShell, *(m_pShell->GetLayout()) ) ); +} + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +void SwViewShellImp::UpdateAccessible() +{ + // We require a layout and an XModel to be accessible. + IDocumentLayoutAccess& rIDLA = GetShell()->getIDocumentLayoutAccess(); + vcl::Window *pWin = GetShell()->GetWin(); + OSL_ENSURE( GetShell()->GetLayout(), "no layout, no access" ); + OSL_ENSURE( pWin, "no window, no access" ); + + if( IsAccessible() && rIDLA.GetCurrentViewShell() && pWin ) + { + try + { + GetAccessibleMap().GetDocumentView(); + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("sw.a11y", ""); + assert(!"SwViewShellImp::UpdateAccessible: unhandled exception"); + } + } +} + +void SwViewShellImp::DisposeAccessible(const SwFrame *pFrame, + const SdrObject *pObj, + bool bRecursive, + bool bCanSkipInvisible) +{ + OSL_ENSURE( !pFrame || pFrame->IsAccessibleFrame(), "frame is not accessible" ); + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if( rTmp.Imp()->IsAccessible() ) + rTmp.Imp()->GetAccessibleMap().A11yDispose( pFrame, pObj, nullptr, bRecursive, bCanSkipInvisible ); + } +} + +void SwViewShellImp::MoveAccessible( const SwFrame *pFrame, const SdrObject *pObj, + const SwRect& rOldFrame ) +{ + OSL_ENSURE( !pFrame || pFrame->IsAccessibleFrame(), "frame is not accessible" ); + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if( rTmp.Imp()->IsAccessible() ) + rTmp.Imp()->GetAccessibleMap().InvalidatePosOrSize( pFrame, pObj, nullptr, + rOldFrame ); + } +} + +void SwViewShellImp::InvalidateAccessibleFrameContent( const SwFrame *pFrame ) +{ + OSL_ENSURE( pFrame->IsAccessibleFrame(), "frame is not accessible" ); + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if( rTmp.Imp()->IsAccessible() ) + rTmp.Imp()->GetAccessibleMap().InvalidateContent( pFrame ); + } +} + +void SwViewShellImp::InvalidateAccessibleCursorPosition( const SwFrame *pFrame ) +{ + if( IsAccessible() ) + GetAccessibleMap().InvalidateCursorPosition( pFrame ); +} + +void SwViewShellImp::InvalidateAccessibleEditableState( bool bAllShells, + const SwFrame *pFrame ) +{ + if( bAllShells ) + { + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if( rTmp.Imp()->IsAccessible() ) + rTmp.Imp()->GetAccessibleMap().InvalidateEditableStates( pFrame ); + } + } + else if( IsAccessible() ) + { + GetAccessibleMap().InvalidateEditableStates( pFrame ); + } +} + +void SwViewShellImp::InvalidateAccessibleRelationSet( const SwFlyFrame *pMaster, + const SwFlyFrame *pFollow ) +{ + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if( rTmp.Imp()->IsAccessible() ) + rTmp.Imp()->GetAccessibleMap().InvalidateRelationSet( pMaster, + pFollow ); + } +} + +/// invalidate CONTENT_FLOWS_FROM/_TO relation for paragraphs +void SwViewShellImp::InvalidateAccessibleParaFlowRelation_( const SwTextFrame* _pFromTextFrame, + const SwTextFrame* _pToTextFrame ) +{ + if ( !_pFromTextFrame && !_pToTextFrame ) + { + // No text frame provided. Thus, nothing to do. + return; + } + + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if ( rTmp.Imp()->IsAccessible() ) + { + if ( _pFromTextFrame ) + { + rTmp.Imp()->GetAccessibleMap(). + InvalidateParaFlowRelation( *_pFromTextFrame, true ); + } + if ( _pToTextFrame ) + { + rTmp.Imp()->GetAccessibleMap(). + InvalidateParaFlowRelation( *_pToTextFrame, false ); + } + } + } +} + +/// invalidate text selection for paragraphs +void SwViewShellImp::InvalidateAccessibleParaTextSelection_() +{ + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if ( rTmp.Imp()->IsAccessible() ) + { + rTmp.Imp()->GetAccessibleMap().InvalidateTextSelectionOfAllParas(); + } + } +} + +/// invalidate attributes for paragraphs +void SwViewShellImp::InvalidateAccessibleParaAttrs_( const SwTextFrame& rTextFrame ) +{ + for(SwViewShell& rTmp : GetShell()->GetRingContainer()) + { + if ( rTmp.Imp()->IsAccessible() ) + { + rTmp.Imp()->GetAccessibleMap().InvalidateAttr( rTextFrame ); + } + } +} + +void SwViewShellImp::UpdateAccessiblePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages, + const Fraction& _rScale, + const SwPageFrame* _pSelectedPageFrame, + const Size& _rPreviewWinSize ) +{ + if( IsAccessible() ) + GetAccessibleMap().UpdatePreview( _rPreviewPages, _rScale, + _pSelectedPageFrame, _rPreviewWinSize ); +} + +void SwViewShellImp::InvalidateAccessiblePreviewSelection( sal_uInt16 nSelPage ) +{ + if( IsAccessible() ) + GetAccessibleMap().InvalidatePreviewSelection( nSelPage ); +} + +SwAccessibleMap *SwViewShellImp::CreateAccessibleMap() +{ + assert(!m_pAccessibleMap); + m_pAccessibleMap = std::make_shared<SwAccessibleMap>(GetShell()); + return m_pAccessibleMap.get(); +} + +void SwViewShellImp::FireAccessibleEvents() +{ + if( IsAccessible() ) + GetAccessibleMap().FireEvents(); +} +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/viewpg.cxx b/sw/source/core/view/viewpg.cxx new file mode 100644 index 0000000000..02ebd1a69e --- /dev/null +++ b/sw/source/core/view/viewpg.cxx @@ -0,0 +1,216 @@ +/* -*- 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 <config_wasm_strip.h> + +#include <tools/fract.hxx> +#include <osl/diagnose.h> +#include <viewsh.hxx> +#include <pagefrm.hxx> +#include <viewimp.hxx> +#include <printdata.hxx> +#include <ptqueue.hxx> +#include <fntcache.hxx> + +#include <vprint.hxx> + +using namespace ::com::sun::star; + +SwPagePreviewLayout* SwViewShell::PagePreviewLayout() +{ + return Imp()->PagePreviewLayout(); +} + +void SwViewShell::ShowPreviewSelection( sal_uInt16 nSelPage ) +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + Imp()->InvalidateAccessiblePreviewSelection( nSelPage ); +#else + (void)nSelPage; +#endif +} + +// adjust view options for page preview +void SwViewShell::AdjustOptionsForPagePreview(SwPrintData const& rPrintOptions) +{ + if ( !IsPreview() ) + { + OSL_FAIL( "view shell doesn't belongs to a page preview - no adjustment of its view options"); + return; + } + + PrepareForPrint( rPrintOptions ); +} + +/// print brochure +// consider empty pages on calculation of the scaling +// for a page to be printed. +void SwViewShell::PrintProspect( + OutputDevice *pOutDev, + const SwPrintData &rPrintData, + sal_Int32 nRenderer // the index in the vector of prospect pages to be printed + ) +{ + const sal_Int32 nMaxRenderer = rPrintData.GetRenderData().GetPagePairsForProspectPrinting().size() - 1; + OSL_ENSURE( 0 <= nRenderer && nRenderer <= nMaxRenderer, "nRenderer out of bounds"); + Printer *pPrinter = dynamic_cast< Printer * >(pOutDev); + if (!pPrinter || nMaxRenderer < 0 || nRenderer < 0 || nRenderer > nMaxRenderer) + return; + + // save settings of OutputDevice (should be done always since the + // output device is now provided by a call from outside the Writer) + pPrinter->Push(); + + std::pair< sal_Int32, sal_Int32 > rPagesToPrint = + rPrintData.GetRenderData().GetPagePairsForProspectPrinting()[ nRenderer ]; + OSL_ENSURE( rPagesToPrint.first == -1 || rPrintData.GetRenderData().GetValidPagesSet().count( rPagesToPrint.first ) == 1, "first Page not valid" ); + OSL_ENSURE( rPagesToPrint.second == -1 || rPrintData.GetRenderData().GetValidPagesSet().count( rPagesToPrint.second ) == 1, "second Page not valid" ); + + // create a new shell for the printer + SwViewShell aShell( *this, nullptr, pPrinter ); + + CurrShell aCurr( &aShell ); + + aShell.PrepareForPrint( rPrintData ); + + //!! applying view options and formatting the document should now only be done in getRendererCount! + + MapMode aMapMode( MapUnit::MapTwip ); + Size aPrtSize( pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), aMapMode ) ); + + SwTwips nMaxRowSz, nMaxColSz; + + const SwPageFrame *pStPage = nullptr; + const SwPageFrame *pNxtPage = nullptr; + if (rPagesToPrint.first > 0) + { + pStPage = sw_getPage(*aShell.GetLayout(), rPagesToPrint.first); + } + if (rPagesToPrint.second > 0) + { + pNxtPage = sw_getPage(*aShell.GetLayout(), rPagesToPrint.second); + } + + // i#14016 consider empty pages on calculation + // of page size, used for calculation of scaling. + Size aSttPageSize; + if ( pStPage ) + { + if ( pStPage->IsEmptyPage() ) + { + if ( pStPage->GetPhyPageNum() % 2 == 0 ) + aSttPageSize = pStPage->GetPrev()->getFrameArea().SSize(); + else + aSttPageSize = pStPage->GetNext()->getFrameArea().SSize(); + } + else + { + aSttPageSize = pStPage->getFrameArea().SSize(); + } + } + Size aNxtPageSize; + if ( pNxtPage ) + { + if ( pNxtPage->IsEmptyPage() ) + { + if ( pNxtPage->GetPhyPageNum() % 2 == 0 ) + aNxtPageSize = pNxtPage->GetPrev()->getFrameArea().SSize(); + else + aNxtPageSize = pNxtPage->GetNext()->getFrameArea().SSize(); + } + else + { + aNxtPageSize = pNxtPage->getFrameArea().SSize(); + } + } + + if( !pStPage ) + { + nMaxColSz = 2 * aNxtPageSize.Width(); + nMaxRowSz = aNxtPageSize.Height(); + } + else if( !pNxtPage ) + { + nMaxColSz = 2 * aSttPageSize.Width(); + nMaxRowSz = aSttPageSize.Height(); + } + else + { + nMaxColSz = aNxtPageSize.Width() + aSttPageSize.Width(); + nMaxRowSz = std::max( aNxtPageSize.Height(), aSttPageSize.Height() ); + } + + // set the MapMode + aMapMode.SetOrigin( Point() ); + { + Fraction aScX( aPrtSize.Width(), nMaxColSz ); + Fraction aScY( aPrtSize.Height(), nMaxRowSz ); + if( aScX < aScY ) + aScY = aScX; + + { + // Round percentages for Drawings so that these can paint their objects properly + aScY *= Fraction( 1000, 1 ); + tools::Long nTmp = static_cast<tools::Long>(aScY); + if( 1 < nTmp ) + --nTmp; + else + nTmp = 1; + aScY = Fraction( nTmp, 1000 ); + } + + aMapMode.SetScaleY( aScY ); + aMapMode.SetScaleX( aScY ); + } + + Size aTmpPrtSize( pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), aMapMode ) ); + + // calculate start point for equal border on all sides + Point aSttPt( (aTmpPrtSize.Width() - nMaxColSz) / 2, + (aTmpPrtSize.Height() - nMaxRowSz) / 2 ); + for( int nC = 0; nC < 2; ++nC ) + { + if( pStPage ) + { + aShell.Imp()->SetFirstVisPageInvalid(); + aShell.maVisArea = pStPage->getFrameArea(); + + Point aPos( aSttPt ); + aPos -= aShell.maVisArea.Pos(); + aMapMode.SetOrigin( aPos ); + pPrinter->SetMapMode( aMapMode ); + pStPage->GetUpper()->PaintSwFrame( *pOutDev, pStPage->getFrameArea() ); + } + + pStPage = pNxtPage; + aSttPt.AdjustX(aTmpPrtSize.Width() / 2 ); + } + + SwPaintQueue::Repaint(); + + //!! applying/modifying view options and formatting the document should now only be done in getRendererCount! + + pFntCache->Flush(); + + // restore settings of OutputDevice (should be done always now since the + // output device is now provided by a call from outside the Writer) + pPrinter->Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx new file mode 100644 index 0000000000..7a322ed7a4 --- /dev/null +++ b/sw/source/core/view/viewsh.cxx @@ -0,0 +1,2849 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <officecfg/Office/Common.hxx> +#include <config_wasm_strip.h> + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <sfx2/viewfrm.hxx> +#include <sfx2/progress.hxx> +#include <svx/srchdlg.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/ipclient.hxx> +#include <sal/log.hxx> +#include <drawdoc.hxx> +#include <swwait.hxx> +#include <crsrsh.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentOutlineNodes.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentState.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <viewimp.hxx> +#include <frmtool.hxx> +#include <viewopt.hxx> +#include <dview.hxx> +#include <swregion.hxx> +#include <hints.hxx> +#include <docufld.hxx> +#include <txtfrm.hxx> +#include <layact.hxx> +#include <mdiexp.hxx> +#include <fntcache.hxx> +#include <ptqueue.hxx> +#include <docsh.hxx> +#include <bookmark.hxx> +#include <ndole.hxx> +#include <ndindex.hxx> +#include <accmap.hxx> +#include <vcl/bitmapex.hxx> +#include <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 <DocumentRedlineManager.hxx> +#include <DocumentLayoutManager.hxx> + +#include <unotxdoc.hxx> +#include <view.hxx> +#include <PostItMgr.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/svdpagv.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> +#include <tools/UnitConversion.hxx> + +#if !HAVE_FEATURE_DESKTOP +#include <vcl/sysdata.hxx> +#endif + +#include <frameformats.hxx> +#include <fmtcntnt.hxx> + +bool SwViewShell::sbLstAct = false; +ShellResource *SwViewShell::spShellRes = nullptr; +vcl::DeleteOnDeinit<std::shared_ptr<weld::Window>> 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; + } + + InvalidatePageAndHFSubsidiaryLines(); +} + +// Invalidate Subsidiary Lines around headers/footers and page frames to repaint +void SwViewShell::InvalidatePageAndHFSubsidiaryLines() +{ + RectangleVector aInvalidRects; + SwPageFrame *pPg = static_cast<SwPageFrame*>(GetLayout()->Lower()); + while (pPg) + { + pPg->AddSubsidiaryLinesBounds(*this, aInvalidRects); + pPg = static_cast<SwPageFrame*>(pPg->GetNext()); + } + + for (const auto &rRect : aInvalidRects) + GetWin()->Invalidate(rRect); +} + +void SwViewShell::setOutputToWindow(bool bOutputToWindow) +{ + mbOutputToWindow = bOutputToWindow; +} + +bool SwViewShell::isOutputToWindow() const +{ + return mbOutputToWindow; +} + +void SwViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwViewShell")); + (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::optional<SwRegionRects> oRegion = Imp()->TakePaintRegion(); + + //JP 27.11.97: what hid the selection, must also Show it, + // else we get Paint errors! + // e.g. additional mode, page half visible vertically, in the + // middle a selection and with another cursor jump to left + // right border. Without ShowCursor the selection disappears. + bool bShowCursor = oRegion && dynamic_cast<const SwCursorShell*>(this) != nullptr; + if( bShowCursor ) + static_cast<SwCursorShell*>(this)->HideCursors(); + + if ( oRegion ) + { + SwRootFrame* pCurrentLayout = GetLayout(); + + oRegion->LimitToOrigin(); + oRegion->Compress( SwRegionRects::CompressFuzzy ); + + while ( !oRegion->empty() ) + { + SwRect aRect( oRegion->back() ); + oRegion->pop_back(); + + if (GetWin()->SupportsDoubleBuffering()) + InvalidateWindows(aRect); + else + { + // #i75172# begin DrawingLayer paint + // need to do begin/end DrawingLayer preparation for each single rectangle of the + // repaint region. I already tried to prepare only once for the whole Region. This + // seems to work (and does technically) but fails with transparent objects. Since the + // region given to BeginDrawLayers() defines the clip region for DrawingLayer paint, + // transparent objects in the single rectangles will indeed be painted multiple times. + if (!comphelper::LibreOfficeKit::isActive()) + { + DLPrePaint2(vcl::Region(aRect.SVRect())); + } + + if ( bPaintsFromSystem ) + PaintDesktop(*GetOut(), aRect); + if (!comphelper::LibreOfficeKit::isActive()) + pCurrentLayout->PaintSwFrame( *mpOut, aRect ); + else + pCurrentLayout->GetCurrShell()->InvalidateWindows(aRect); + + // #i75172# end DrawingLayer paint + if (!comphelper::LibreOfficeKit::isActive()) + { + DLPostPaint2(true); + } + } + + lcl_PaintTransparentFormControls(*this, aRect); // i#107365 + } + } + if( bShowCursor ) + static_cast<SwCursorShell*>(this)->ShowCursors( true ); + } + else + { + Imp()->DeletePaintRegion(); + mbPaintWorks = true; + } + } + else + mbPaintWorks = true; + + mbInEndAction = false; + SwViewShell::sbLstAct = false; + Imp()->EndAction(); + + //We artificially end the action here to enable the automatic scrollbars + //to adjust themselves correctly + //EndAction sends a Notify, and that must call Start-/EndAction to + //adjust the scrollbars correctly + --mnStartAction; + UISizeNotify(); + ++mnStartAction; + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( Imp()->IsAccessible() ) + Imp()->FireAccessibleEvents(); +#endif +} + +void SwViewShell::ImplStartAction() +{ + mbPaintWorks = false; + Imp()->StartAction(); +} + +void SwViewShell::ImplLockPaint() +{ + if ( GetWin() && GetWin()->IsVisible() && !comphelper::LibreOfficeKit::isActive()) + GetWin()->EnablePaint( false ); //Also cut off the controls. + Imp()->LockPaint(); +} + +void SwViewShell::ImplUnlockPaint(std::vector<LockPaintReason>& rReasons, bool bVirDev) +{ + CurrShell aCurr( this ); + if ( GetWin() && GetWin()->IsVisible() ) + { + if ( (bInSizeNotify || bVirDev ) && VisArea().HasArea() && !comphelper::LibreOfficeKit::isActive()) + { + //Refresh with virtual device to avoid flickering. + VclPtrInstance<VirtualDevice> pVout( *mpOut ); + pVout->SetMapMode( mpOut->GetMapMode() ); + Size aSize( VisArea().SSize() ); + aSize.AdjustWidth(20 ); + aSize.AdjustHeight(20 ); + if( pVout->SetOutputSize( aSize ) ) + { + GetWin()->EnablePaint( true ); + GetWin()->Validate(); + + Imp()->UnlockPaint(); + pVout->SetLineColor( mpOut->GetLineColor() ); + pVout->SetFillColor( mpOut->GetFillColor() ); + + // #i72754# start Pre/PostPaint encapsulation before mpOut is changed to the buffering VDev + const vcl::Region aRepaintRegion(VisArea().SVRect()); + DLPrePaint2(aRepaintRegion); + + OutputDevice *pOld = mpOut; + mpOut = pVout.get(); + Paint(*mpOut, VisArea().SVRect()); + mpOut = pOld; + mpOut->DrawOutDev( VisArea().Pos(), aSize, + VisArea().Pos(), aSize, *pVout ); + + // #i72754# end Pre/PostPaint encapsulation when mpOut is back and content is painted + DLPostPaint2(true); + + lcl_PaintTransparentFormControls(*this, VisArea()); // fdo#63949 + } + else + { + Imp()->UnlockPaint(); + GetWin()->EnablePaint( true ); + InvalidateAll(rReasons); + } + pVout.disposeAndClear(); + } + else + { + Imp()->UnlockPaint(); + GetWin()->EnablePaint( true ); + InvalidateAll(rReasons); + } + } + else + Imp()->UnlockPaint(); +} + +namespace +{ + std::string_view to_string(LockPaintReason eReason) + { + switch(eReason) + { + case LockPaintReason::ViewLayout: + return "ViewLayout"; + case LockPaintReason::OuterResize: + return "OuterResize"; + case LockPaintReason::Undo: + return "Undo"; + case LockPaintReason::Redo: + return "Redo"; + case LockPaintReason::OutlineFolding: + return "OutlineFolding"; + case LockPaintReason::EndSdrCreate: + return "EndSdrCreate"; + case LockPaintReason::SwLayIdle: + return "SwLayIdle"; + case LockPaintReason::InvalidateLayout: + return "InvalidateLayout"; + case LockPaintReason::StartDrag: + return "StartDrag"; + case LockPaintReason::DataChanged: + return "DataChanged"; + case LockPaintReason::InsertFrame: + return "InsertFrame"; + case LockPaintReason::GotoPage: + return "GotoPage"; + case LockPaintReason::InsertGraphic: + return "InsertGraphic"; + case LockPaintReason::SetZoom: + return "SetZoom"; + case LockPaintReason::ExampleFrame: + return "ExampleFram"; + } + return ""; + }; +} + +void SwViewShell::InvalidateAll(std::vector<LockPaintReason>& rReasons) +{ + assert(!rReasons.empty() && "there must be a reason to InvalidateAll"); + + for (const auto& reason : rReasons) + SAL_INFO("sw.core", "InvalidateAll because of: " << to_string(reason)); + + if (comphelper::LibreOfficeKit::isActive()) + { + // https://github.com/CollaboraOnline/online/issues/6379 + // ditch OuterResize as a reason to invalidate all in the online case + std::erase(rReasons, LockPaintReason::OuterResize); + } + + if (!rReasons.empty()) + GetWin()->Invalidate(InvalidateFlags::Children); + rReasons.clear(); +} + +bool SwViewShell::AddPaintRect( const SwRect & rRect ) +{ + bool bRet = false; + for(SwViewShell& rSh : GetRingContainer()) + { + if( rSh.Imp() ) + { + if ( rSh.IsPreview() && rSh.GetWin() ) + ::RepaintPagePreview( &rSh, rRect ); + else + bRet |= rSh.Imp()->AddPaintRect( rRect ); + } + } + return bRet; +} + +void SwViewShell::InvalidateWindows( const SwRect &rRect ) +{ + if ( Imp()->IsCalcLayoutProgress() ) + return; + + if(comphelper::LibreOfficeKit::isActive()) + { + // If we are inside tiled painting, invalidations are ignored. + // Ignore them right now to save work, but also to avoid the problem + // that this state could be reset before FlushPendingLOKInvalidateTiles() + // gets called. + if(comphelper::LibreOfficeKit::isTiledPainting()) + return; + // First collect all invalidations and perform them only later, + // otherwise the number of Invalidate() calls would be at least + // O(n^2) if not worse. The problem is that if any change in a document + // is made, SwEditShell::EndAllAction() is called, which calls EndAction() + // for every view. And every view does it own handling of paint rectangles, + // and then calls InvalidateWindows() based on that. On then this code + // would call Invalidate() for all views for each rectangle. + // So collect the rectangles, avoid duplicates (which there usually will + // be many because of the repetitions), FlushPendingLOKInvalidateTiles() + // will collect all rectangles from all related views, compress them + // and only with those relatively few rectangle it'd call Invalidate() + // for all views. + Imp()->AddPendingLOKInvalidation(rRect); + return; + } + + for(SwViewShell& rSh : GetRingContainer()) + { + if ( rSh.GetWin() ) + { + if ( rSh.IsPreview() ) + ::RepaintPagePreview( &rSh, rRect ); + // In case of tiled rendering, invalidation is wanted even if + // the rectangle is outside the visual area. + else if ( rSh.VisArea().Overlaps( rRect ) || comphelper::LibreOfficeKit::isActive() ) + rSh.GetWin()->Invalidate( rRect.SVRect() ); + } + } +} + +void SwViewShell::FlushPendingLOKInvalidateTiles() +{ + assert(comphelper::LibreOfficeKit::isActive()); + SwRegionRects rects; + for(SwViewShell& rSh : GetRingContainer()) + { + std::vector<SwRect> tmpRects = rSh.Imp()->TakePendingLOKInvalidations(); + rects.insert( rects.end(), tmpRects.begin(), tmpRects.end()); + } + rects.Compress( SwRegionRects::CompressFuzzy ); + if(rects.empty()) + return; + // This is basically the loop from SwViewShell::InvalidateWindows(). + for(SwViewShell& rSh : GetRingContainer()) + { + if ( rSh.GetWin() ) + { + if ( rSh.IsPreview() ) + { + for( const SwRect& rect : rects ) + ::RepaintPagePreview( &rSh, rect ); + } + else + { + for( const SwRect& rect : rects ) + rSh.GetWin()->Invalidate( rect.SVRect() ); + } + } + } +} + +const SwRect& SwViewShell::VisArea() const +{ + // when using the tiled rendering, consider the entire document as our + // visible area + return comphelper::LibreOfficeKit::isActive()? GetLayout()->getFrameArea(): maVisArea; +} + +void SwViewShell::MakeVisible( const SwRect &rRect ) +{ + if ( !(!VisArea().Contains( rRect ) || IsScrollMDI( this, rRect ) || GetCareDialog(*this)) ) + return; + + if ( IsViewLocked() ) + return; + + if( mpWin ) + { + const SwFrame* pRoot = GetLayout(); + int nLoopCnt = 3; + tools::Long nOldH; + do{ + nOldH = pRoot->getFrameArea().Height(); + StartAction(); + ScrollMDI( this, rRect, USHRT_MAX, USHRT_MAX ); + EndAction(); + } while( nOldH != pRoot->getFrameArea().Height() && nLoopCnt-- ); + } +#if OSL_DEBUG_LEVEL > 0 + else + { + //MA: 04. Nov. 94, no one needs this, does one? + OSL_ENSURE( false, "Is MakeVisible still needed for printers?" ); + } + +#endif +} + +weld::Window* SwViewShell::CareChildWin(SwViewShell const & rVSh) +{ + if (!rVSh.mpSfxViewShell) + return nullptr; +#if HAVE_FEATURE_DESKTOP + const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId(); + SfxViewFrame& rVFrame = rVSh.mpSfxViewShell->GetViewFrame(); + SfxChildWindow* pChWin = rVFrame.GetChildWindow( nId ); + if (!pChWin) + return nullptr; + weld::DialogController* pController = pChWin->GetController().get(); + if (!pController) + return nullptr; + weld::Window* pWin = pController->getDialog(); + if (pWin && pWin->get_visible()) + return pWin; +#endif + return nullptr; +} + +Point SwViewShell::GetPagePos( sal_uInt16 nPageNum ) const +{ + return GetLayout()->GetPagePos( nPageNum ); +} + +sal_uInt16 SwViewShell::GetNumPages() const +{ + //It is possible that no layout exists when the method from + //root-Ctor is called. + return GetLayout() ? GetLayout()->GetPageNum() : 0; +} + +bool SwViewShell::IsDummyPage( sal_uInt16 nPageNum ) const +{ + return GetLayout() && GetLayout()->IsDummyPage( nPageNum ); +} + +/** + * Forces update of each field. + * It notifies all fields with pNewHt. If that is 0 (default), the field + * type is sent (???). + * @param[in] bCloseDB Passed in to GetDoc()->UpdateFields. [TODO] Purpose??? + */ +void SwViewShell::UpdateFields(bool bCloseDB) +{ + CurrShell aCurr( this ); + + auto pCursorShell = dynamic_cast<SwCursorShell*>( this ); + if ( pCursorShell ) + pCursorShell->StartAction(); + else + StartAction(); + + GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB); + + if ( pCursorShell ) + pCursorShell->EndAction(); + else + EndAction(); +} + +void SwViewShell::UpdateOleObjectPreviews() +{ + SwDoc* pDoc = GetDoc(); + for(sw::SpzFrameFormat* pFormat: *pDoc->GetSpzFrameFormats()) + { + if (pFormat->Which() != RES_FLYFRMFMT) + { + continue; + } + + const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx(); + if (!pNodeIndex || !pNodeIndex->GetNodes().IsDocNodes()) + { + continue; + } + + SwNode* pNode = pDoc->GetNodes()[pNodeIndex->GetIndex() + 1]; + SwOLENode* pOleNode = pNode->GetOLENode(); + if (!pOleNode) + { + continue; + } + + SwOLEObj& rOleObj = pOleNode->GetOLEObj(); + svt::EmbeddedObjectRef& rObject = rOleObj.GetObject(); + rObject.UpdateReplacement( true ); + // Trigger the repaint. + pOleNode->SetChanged(); + } +} + +/** update all charts for which any table exists */ +void SwViewShell::UpdateAllCharts() +{ + CurrShell aCurr( this ); + // Start-/EndAction handled in the SwDoc-Method! + GetDoc()->UpdateAllCharts(); +} + +bool SwViewShell::HasCharts() const +{ + bool bRet = false; + SwNodeIndex aIdx( *GetDoc()->GetNodes().GetEndOfAutotext(). + StartOfSectionNode(), 1 ); + while (aIdx.GetNode().GetStartNode()) + { + ++aIdx; + const SwOLENode *pNd = aIdx.GetNode().GetOLENode(); + if( pNd && !pNd->GetChartTableName().isEmpty() ) + { + bRet = true; + break; + } + } + return bRet; +} + +void SwViewShell::LayoutIdle() +{ + if( !mpOpt->IsIdle() || !GetWin() || HasDrawViewDrag() ) + return; + + //No idle when printing is going on. + for(const SwViewShell& rSh : GetRingContainer()) + { + if ( !rSh.GetWin() ) + return; + } + + CurrShell aCurr( this ); + +#ifdef DBG_UTIL + // If Test5 has been set, the IdleFormatter is disabled. + if( mpOpt->IsTest5() ) + return; +#endif + + { + // Preserve top of the text frame cache. + SwSaveSetLRUOfst aSaveLRU; + // #125243# there are lots of stacktraces indicating that Imp() returns NULL + // this SwViewShell seems to be invalid - but it's not clear why + // this return is only a workaround! + OSL_ENSURE(Imp(), "SwViewShell already deleted?"); + if(!Imp()) + return; + SwLayIdle aIdle( GetLayout(), Imp() ); + } +} + +static void lcl_InvalidateAllContent( SwViewShell& rSh, SwInvalidateFlags nInv ) +{ + auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh); + if ( pCursorShell ) + pCursorShell->StartAction(); + else + rSh.StartAction(); + rSh.GetLayout()->InvalidateAllContent( nInv ); + if ( pCursorShell ) + pCursorShell->EndAction(); + else + rSh.EndAction(); + + rSh.GetDoc()->getIDocumentState().SetModified(); +} + +/** local method to invalidate/re-calculate positions of floating screen + * objects (Writer fly frame and drawing objects), which are anchored + * to paragraph or to character. #i11860# + */ +static void lcl_InvalidateAllObjPos( SwViewShell &_rSh ) +{ + auto pCursorShell = dynamic_cast<SwCursorShell*>( &_rSh); + if ( pCursorShell ) + pCursorShell->StartAction(); + else + _rSh.StartAction(); + + _rSh.GetLayout()->InvalidateAllObjPos(); + + if ( pCursorShell ) + pCursorShell->EndAction(); + else + _rSh.EndAction(); + + _rSh.GetDoc()->getIDocumentState().SetModified(); +} + +void SwViewShell::SetParaSpaceMax( bool bNew ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) != bNew ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::PARA_SPACE_MAX, bNew ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +void SwViewShell::SetParaSpaceMaxAtPages( bool bNew ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) != bNew ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, bNew ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +void SwViewShell::SetTabCompat( bool bNew ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if( rIDSA.get(DocumentSettingId::TAB_COMPAT) != bNew ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::TAB_COMPAT, bNew ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +void SwViewShell::SetAddExtLeading( bool bNew ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) != bNew ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::ADD_EXT_LEADING, bNew ); + SwDrawModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel(); + if ( pTmpDrawModel ) + pTmpDrawModel->SetAddExtLeading( bNew ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +/** Sets if paragraph and table spacing is added at bottom of table cells. + * #106629# + * @param[in] (bool) setting of the new value + */ +void SwViewShell::SetAddParaSpacingToTableCells( bool _bAddParaSpacingToTableCells ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells + || rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells ); + // note: the dialog can't change the value to indeterminate, so only false/false and true/true + rIDSA.set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +/** + * Sets if former formatting of text lines with proportional line spacing should used. + * #i11859# + * @param[in] (bool) setting of the new value + */ +void SwViewShell::SetUseFormerLineSpacing( bool _bUseFormerLineSpacing ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) != _bUseFormerLineSpacing ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::OLD_LINE_SPACING, _bUseFormerLineSpacing ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +/** + * Sets IDocumentSettingAccess if former object positioning should be used. + * #i11860# + * @param[in] (bool) setting the new value + */ +void SwViewShell::SetUseFormerObjectPositioning( bool _bUseFormerObjPos ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::USE_FORMER_OBJECT_POS) != _bUseFormerObjPos ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::USE_FORMER_OBJECT_POS, _bUseFormerObjPos ); + lcl_InvalidateAllObjPos( *this ); + } +} + +// #i28701# +void SwViewShell::SetConsiderWrapOnObjPos( bool _bConsiderWrapOnObjPos ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) != _bConsiderWrapOnObjPos ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, _bConsiderWrapOnObjPos ); + lcl_InvalidateAllObjPos( *this ); + } +} + +void SwViewShell::SetUseFormerTextWrapping( bool _bUseFormerTextWrapping ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) != _bUseFormerTextWrapping ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, _bUseFormerTextWrapping ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +// #i45491# +void SwViewShell::SetDoNotJustifyLinesWithManualBreak( bool _bDoNotJustifyLinesWithManualBreak ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if ( rIDSA.get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK) != _bDoNotJustifyLinesWithManualBreak ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + rIDSA.set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, _bDoNotJustifyLinesWithManualBreak ); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent( *this, nInv ); + } +} + +void SwViewShell::SetProtectForm( bool _bProtectForm ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + rIDSA.set(DocumentSettingId::PROTECT_FORM, _bProtectForm ); +} + +void SwViewShell::SetMsWordCompTrailingBlanks( bool _bMsWordCompTrailingBlanks ) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if (rIDSA.get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS) != _bMsWordCompTrailingBlanks) + { + SwWait aWait(*GetDoc()->GetDocShell(), true); + rIDSA.set(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, _bMsWordCompTrailingBlanks); + const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section; + lcl_InvalidateAllContent(*this, nInv); + } +} + +void SwViewShell::SetSubtractFlysAnchoredAtFlys(bool bSubtractFlysAnchoredAtFlys) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + rIDSA.set(DocumentSettingId::SUBTRACT_FLYS, bSubtractFlysAnchoredAtFlys); +} + +void SwViewShell::SetEmptyDbFieldHidesPara(bool bEmptyDbFieldHidesPara) +{ + IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess(); + if (rIDSA.get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA) == bEmptyDbFieldHidesPara) + return; + + SwWait aWait(*GetDoc()->GetDocShell(), true); + rIDSA.set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, bEmptyDbFieldHidesPara); + StartAction(); + GetDoc()->getIDocumentState().SetModified(); + for (auto const & pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes()) + { + if(pFieldType->Which() == SwFieldIds::Database) + pFieldType->UpdateFields(); + } + EndAction(); +} + +void SwViewShell::Reformat() +{ + SwWait aWait( *GetDoc()->GetDocShell(), true ); + + // we go for safe: get rid of the old font information, + // when the printer resolution or zoom factor changes. + // Init() and Reformat() are the safest locations. + pFntCache->Flush( ); + + if( GetLayout()->IsCallbackActionEnabled() ) + { + StartAction(); + GetLayout()->InvalidateAllContent( SwInvalidateFlags::Size | SwInvalidateFlags::Pos | SwInvalidateFlags::PrtArea ); + EndAction(); + } +} + +void SwViewShell::ChgNumberDigits() +{ + SdrModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel(); + if ( pTmpDrawModel ) + pTmpDrawModel->ReformatAllTextObjects(); + Reformat(); +} + +void SwViewShell::CalcLayout() +{ + // extremely likely to be a Bad Idea to call this without StartAction + // (except the Page Preview apparently only has a non-subclassed ViewShell) + assert((typeid(*this) == typeid(SwViewShell)) || mnStartAction); + + CurrShell aCurr( this ); + SwWait aWait( *GetDoc()->GetDocShell(), true ); + + // Preserve top of the text frame cache. + SwSaveSetLRUOfst aSaveLRU; + + //switch on Progress when none is running yet. + const bool bEndProgress = SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) == nullptr; + if ( bEndProgress ) + { + tools::Long nEndPage = GetLayout()->GetPageNum(); + nEndPage += nEndPage * 10 / 100; + ::StartProgress( STR_STATSTR_REFORMAT, 0, nEndPage, GetDoc()->GetDocShell() ); + } + + SwLayAction aAction( GetLayout(), Imp() ); + aAction.SetPaint( false ); + aAction.SetStatBar( true ); + aAction.SetCalcLayout( true ); + aAction.SetReschedule( true ); + GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + aAction.Action(GetOut()); + GetDoc()->getIDocumentFieldsAccess().UnlockExpFields(); + + //the SetNewFieldLst() on the Doc was cut off and must be fetched again + //(see flowfrm.cxx, txtfld.cxx) + if ( aAction.IsExpFields() ) + { + aAction.Reset(); + aAction.SetPaint( false ); + aAction.SetStatBar( true ); + aAction.SetReschedule( true ); + + GetDoc()->getIDocumentFieldsAccess().UpdatePageFields(0); + GetDoc()->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); + + aAction.Action(GetOut()); + } + + if ( VisArea().HasArea() ) + InvalidateWindows( VisArea() ); + if ( bEndProgress ) + ::EndProgress( GetDoc()->GetDocShell() ); +} + +void SwViewShell::SetFirstVisPageInvalid() +{ + for(SwViewShell& rSh : GetRingContainer()) + { + if ( rSh.Imp() ) + rSh.Imp()->SetFirstVisPageInvalid(); + } +} + +void SwViewShell::SizeChgNotify() +{ + if ( !mpWin ) + mbDocSizeChgd = true; + else if( ActionPend() || Imp()->IsCalcLayoutProgress() || mbPaintInProgress ) + { + mbDocSizeChgd = true; + + if ( !Imp()->IsCalcLayoutProgress() && dynamic_cast<const SwCursorShell*>( this ) != nullptr ) + { + PageNumNotify(this); + + if (comphelper::LibreOfficeKit::isActive()) + { + Size aDocSize = GetDocSize(); + OString sPayload = OString::number(aDocSize.Width() + 2 * DOCUMENTBORDER) + + ", " + OString::number(aDocSize.Height() + 2 * DOCUMENTBORDER); + + SwXTextDocument* pModel = comphelper::getFromUnoTunnel<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument()); + SfxLokHelper::notifyDocumentSizeChanged(GetSfxViewShell(), sPayload, pModel); + } + } + } + else + { + mbDocSizeChgd = false; + ::SizeNotify( this, GetDocSize() ); + } +} + +void SwViewShell::VisPortChgd( const SwRect &rRect) +{ + OSL_ENSURE( GetWin(), "VisPortChgd without Window." ); + + if ( rRect == VisArea() ) + return; + + // Is someone spuriously rescheduling again? + SAL_WARN_IF(mbInEndAction, "sw.core", "Scrolling during EndAction"); + + //First get the old visible page, so we don't have to look + //for it afterwards. + const SwFrame *pOldPage = Imp()->GetFirstVisPage(GetWin()->GetOutDev()); + + const SwRect aPrevArea( VisArea() ); + const bool bFull = aPrevArea.IsEmpty(); + maVisArea = rRect; + SetFirstVisPageInvalid(); + + //When there a PaintRegion still exists and the VisArea has changed, + //the PaintRegion is at least by now obsolete. The PaintRegion can + //have been created by RootFrame::PaintSwFrame. + if ( !mbInEndAction && + Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea() ) + Imp()->DeletePaintRegion(); + + CurrShell aCurr( this ); + + bool bScrolled = false; + + SwPostItMgr* pPostItMgr = GetPostItMgr(); + + if ( bFull ) + GetWin()->Invalidate(); + else + { + //Calculate amount to be scrolled. + const tools::Long nXDiff = aPrevArea.Left() - VisArea().Left(); + const tools::Long nYDiff = aPrevArea.Top() - VisArea().Top(); + + if( !nXDiff && !GetViewOptions()->getBrowseMode() && + (!Imp()->HasDrawView() || !Imp()->GetDrawView()->IsGridVisible() ) ) + { + // If possible, don't scroll the application background + // (PaintDesktop). Also limit the left and right side of + // the scroll range to the pages. + const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower()); + if ( pPage->getFrameArea().Top() > pOldPage->getFrameArea().Top() ) + pPage = static_cast<const SwPageFrame*>(pOldPage); + SwRect aBoth( VisArea() ); + aBoth.Union( aPrevArea ); + const SwTwips nBottom = aBoth.Bottom(); + SwTwips nMinLeft = SAL_MAX_INT32; + SwTwips nMaxRight= 0; + + const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode(); + + while ( pPage && pPage->getFrameArea().Top() <= nBottom ) + { + SwRect aPageRect( pPage->GetBoundRect(GetWin()->GetOutDev()) ); + if ( bBookMode ) + { + const SwPageFrame& rFormatPage = pPage->GetFormatPage(); + aPageRect.SSize( rFormatPage.GetBoundRect(GetWin()->GetOutDev()).SSize() ); + } + + // #i9719# - consider new border and shadow width + if ( aPageRect.Overlaps( aBoth ) ) + { + SwTwips nPageLeft = 0; + SwTwips nPageRight = 0; + const sw::sidebarwindows::SidebarPosition aSidebarPos = pPage->SidebarPosition(); + + if( aSidebarPos != sw::sidebarwindows::SidebarPosition::NONE ) + { + nPageLeft = aPageRect.Left(); + nPageRight = aPageRect.Right(); + } + + if( nPageLeft < nMinLeft ) + nMinLeft = nPageLeft; + if( nPageRight > nMaxRight ) + nMaxRight = nPageRight; + //match with the draw objects + //take nOfst into account as the objects have been + //selected and have handles attached. + if ( pPage->GetSortedObjs() ) + { + const tools::Long nOfst = GetOut()->PixelToLogic( + Size(Imp()->GetDrawView()->GetMarkHdlSizePixel()/2,0)).Width(); + for (SwAnchoredObject* pObj : *pPage->GetSortedObjs()) + { + // ignore objects that are not actually placed on the page + if (pObj->IsFormatPossible()) + { + const tools::Rectangle &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<const SwPageFrame*>(pPage->GetNext()); + } + tools::Rectangle aRect( aPrevArea.SVRect() ); + aRect.SetLeft( nMinLeft ); + aRect.SetRight( nMaxRight ); + if( VisArea().Overlaps( aPrevArea ) && !mnLockPaint ) + { + bScrolled = true; + maVisArea.Pos() = aPrevArea.Pos(); + if ( SmoothScroll( nXDiff, nYDiff, &aRect ) ) + return; + maVisArea.Pos() = rRect.Pos(); + } + else if (!comphelper::LibreOfficeKit::isActive()) + GetWin()->Invalidate( aRect ); + } + else if ( !mnLockPaint ) //will be released in Unlock + { + if( VisArea().Overlaps( aPrevArea ) ) + { + bScrolled = true; + maVisArea.Pos() = aPrevArea.Pos(); + if ( SmoothScroll( nXDiff, nYDiff, nullptr ) ) + return; + maVisArea.Pos() = rRect.Pos(); + } + else + GetWin()->Invalidate(); + } + } + + // When tiled rendering, the map mode of the window is disabled, avoid + // enabling it here. + if (!comphelper::LibreOfficeKit::isActive()) + { + Point aPt( VisArea().Pos() ); + aPt.setX( -aPt.X() ); aPt.setY( -aPt.Y() ); + MapMode aMapMode( GetWin()->GetMapMode() ); + aMapMode.SetOrigin( aPt ); + GetWin()->SetMapMode( aMapMode ); + } + + if ( HasDrawView() ) + { + Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() ); + Imp()->GetDrawView()->SetActualWin( GetWin()->GetOutDev() ); + } + GetWin()->PaintImmediately(); + + if ( pPostItMgr ) // #i88070# + { + pPostItMgr->Rescale(); + pPostItMgr->CalcRects(); + pPostItMgr->LayoutPostIts(); + } + + if ( !bScrolled && pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ) + pPostItMgr->CorrectPositions(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( Imp()->IsAccessible() ) + Imp()->UpdateAccessible(); +#endif +} + +bool SwViewShell::SmoothScroll( tools::Long lXDiff, tools::Long lYDiff, const tools::Rectangle *pRect ) +{ +#if !defined(MACOSX) && !defined(ANDROID) && !defined(IOS) + // #i98766# - disable smooth scrolling for Mac + + const sal_uLong nBitCnt = mpOut->GetBitCount(); + tools::Long lMult = 1, lMax = LONG_MAX; + if ( nBitCnt == 16 ) + { + lMax = 7000; + lMult = 2; + } + if ( nBitCnt == 24 ) + { + lMax = 5000; + lMult = 6; + } + else if ( nBitCnt == 1 ) + { + lMax = 3000; + lMult = 12; + } + + // #i75172# isolated static conditions + const bool bOnlyYScroll(!lXDiff && std::abs(lYDiff) != 0 && std::abs(lYDiff) < lMax); + const bool bAllowedWithChildWindows(GetWin()->GetWindowClipRegionPixel().IsNull()); + const bool bSmoothScrollAllowed(bOnlyYScroll && mbEnableSmooth && GetViewOptions()->IsSmoothScroll() && bAllowedWithChildWindows); + + if(bSmoothScrollAllowed) + { + Imp()->m_bStopSmooth = false; + + const SwRect aOldVis( VisArea() ); + + //create virtual device and set. + const Size aPixSz = GetWin()->PixelToLogic(Size(1,1)); + VclPtrInstance<VirtualDevice> pVout( *GetWin()->GetOutDev() ); + pVout->SetLineColor( GetWin()->GetOutDev()->GetLineColor() ); + pVout->SetFillColor( GetWin()->GetOutDev()->GetFillColor() ); + MapMode aMapMode( GetWin()->GetMapMode() ); + pVout->SetMapMode( aMapMode ); + Size aSize( maVisArea.Width()+2*aPixSz.Width(), std::abs(lYDiff)+(2*aPixSz.Height()) ); + if ( pRect ) + aSize.setWidth( std::min(aSize.Width(), pRect->GetWidth()+2*aPixSz.Width()) ); + if ( pVout->SetOutputSize( aSize ) ) + { + mnLockPaint++; + + //First Paint everything in the virtual device. + SwRect aRect( VisArea() ); + aRect.Height( aSize.Height() ); + if ( pRect ) + { + aRect.Pos().setX( std::max(aRect.Left(),pRect->Left()-aPixSz.Width()) ); + aRect.Right( std::min(aRect.Right()+2*aPixSz.Width(), pRect->Right()+aPixSz.Width())); + } + else + aRect.AddWidth(2*aPixSz.Width() ); + aRect.Pos().setY( lYDiff < 0 ? aOldVis.Bottom() - aPixSz.Height() + : aRect.Top() - aSize.Height() + aPixSz.Height() ); + aRect.Pos().setX( std::max( tools::Long(0), aRect.Left()-aPixSz.Width() ) ); + aRect.Pos() = GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.Pos())); + aRect.SSize( GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.SSize())) ); + maVisArea = aRect; + const Point aPt( -aRect.Left(), -aRect.Top() ); + aMapMode.SetOrigin( aPt ); + pVout->SetMapMode( aMapMode ); + OutputDevice *pOld = mpOut; + mpOut = pVout.get(); + + { + // #i75172# To get a clean repaint, a new ObjectContact is needed here. Without, the + // repaint would not be correct since it would use the wrong DrawPage visible region. + // This repaint IS about painting something currently outside the visible part (!). + // For that purpose, AddDeviceToPaintView is used which creates a new SdrPageViewWindow + // and all the necessary stuff. It's not cheap, but necessary here. Alone because repaint + // target really is NOT the current window. + // Also will automatically NOT use PreRendering and overlay (since target is VirtualDevice) + if(!HasDrawView()) + MakeDrawView(); + SdrView* pDrawView = GetDrawView(); + pDrawView->AddDeviceToPaintView(*pVout, nullptr); + + // clear mpWin during DLPrePaint2 to get paint preparation for mpOut, but set it again + // immediately afterwards. There are many decisions in SW which imply that Printing + // is used when mpWin == 0 (wrong but widely used). + vcl::Window* pOldWin = mpWin; + mpWin = nullptr; + DLPrePaint2(vcl::Region(aRect.SVRect())); + mpWin = pOldWin; + + // SW paint stuff + PaintDesktop(*GetOut(), aRect); + SwViewShell::sbLstAct = true; + GetLayout()->PaintSwFrame( *GetOut(), aRect ); + SwViewShell::sbLstAct = false; + + // end paint and destroy ObjectContact again + DLPostPaint2(true); + pDrawView->DeleteDeviceFromPaintView(*pVout); + } + + mpOut = pOld; + maVisArea = aOldVis; + + //Now shift in parts and copy the new Pixel from the virtual device. + + // ?????????????????????? + // or is it better to get the scrollfactor from the User + // as option? + // ?????????????????????? + tools::Long lMaDelta = aPixSz.Height(); + if ( std::abs(lYDiff) > ( maVisArea.Height() / 3 ) ) + lMaDelta *= 6; + else + lMaDelta *= 2; + + lMaDelta *= lMult; + + if ( lYDiff < 0 ) + lMaDelta = -lMaDelta; + + tools::Long lDiff = lYDiff; + while ( lDiff ) + { + tools::Long lScroll; + if ( Imp()->m_bStopSmooth || std::abs(lDiff) <= std::abs(lMaDelta) ) + { + lScroll = lDiff; + lDiff = 0; + } + else + { + lScroll = lMaDelta; + lDiff -= lMaDelta; + } + + const SwRect aTmpOldVis = VisArea(); + maVisArea.Pos().AdjustY( -lScroll ); + maVisArea.Pos() = GetWin()->PixelToLogic( GetWin()->LogicToPixel( VisArea().Pos())); + lScroll = aTmpOldVis.Top() - VisArea().Top(); + if ( pRect ) + { + tools::Rectangle aTmp( aTmpOldVis.SVRect() ); + aTmp.SetLeft( pRect->Left() ); + aTmp.SetRight( pRect->Right() ); + GetWin()->Scroll( 0, lScroll, aTmp, ScrollFlags::Children); + } + else + GetWin()->Scroll( 0, lScroll, ScrollFlags::Children ); + + const Point aTmpPt( -VisArea().Left(), -VisArea().Top() ); + MapMode aTmpMapMode( GetWin()->GetMapMode() ); + aTmpMapMode.SetOrigin( aTmpPt ); + GetWin()->SetMapMode( aTmpMapMode ); + + if ( Imp()->HasDrawView() ) + Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() ); + + SetFirstVisPageInvalid(); + if ( !Imp()->m_bStopSmooth ) + { + const bool bScrollDirectionIsUp(lScroll > 0); + Imp()->m_aSmoothRect = VisArea(); + + if(bScrollDirectionIsUp) + { + Imp()->m_aSmoothRect.Bottom( VisArea().Top() + lScroll + aPixSz.Height()); + } + else + { + Imp()->m_aSmoothRect.Top( VisArea().Bottom() + lScroll - aPixSz.Height()); + } + + Imp()->m_bSmoothUpdate = true; + GetWin()->PaintImmediately(); + Imp()->m_bSmoothUpdate = false; + + if(!Imp()->m_bStopSmooth) + { + // start paint on logic base + const tools::Rectangle aTargetLogic(Imp()->m_aSmoothRect.SVRect()); + DLPrePaint2(vcl::Region(aTargetLogic)); + + // get target rectangle in discrete pixels + OutputDevice& rTargetDevice = mpTargetPaintWindow->GetTargetOutputDevice(); + const tools::Rectangle aTargetPixel(rTargetDevice.LogicToPixel(aTargetLogic)); + + // get source top-left in discrete pixels + const Point aSourceTopLeft(pVout->LogicToPixel(aTargetLogic.TopLeft())); + + // switch off MapModes + const bool bMapModeWasEnabledDest(rTargetDevice.IsMapModeEnabled()); + const bool bMapModeWasEnabledSource(pVout->IsMapModeEnabled()); + rTargetDevice.EnableMapMode(false); + pVout->EnableMapMode(false); + + rTargetDevice.DrawOutDev( + aTargetPixel.TopLeft(), aTargetPixel.GetSize(), // dest + aSourceTopLeft, aTargetPixel.GetSize(), // source + *pVout); + + // restore MapModes + rTargetDevice.EnableMapMode(bMapModeWasEnabledDest); + pVout->EnableMapMode(bMapModeWasEnabledSource); + + // end paint on logoc base + DLPostPaint2(true); + } + else + --mnLockPaint; + } + } + pVout.disposeAndClear(); + GetWin()->PaintImmediately(); + if ( !Imp()->m_bStopSmooth ) + --mnLockPaint; + SetFirstVisPageInvalid(); + return true; + } + pVout.disposeAndClear(); + } +#endif + + maVisArea.Pos().AdjustX( -lXDiff ); + maVisArea.Pos().AdjustY( -lYDiff ); + if ( pRect ) + GetWin()->Scroll( lXDiff, lYDiff, *pRect, ScrollFlags::Children); + else + GetWin()->Scroll( lXDiff, lYDiff, ScrollFlags::Children); + return false; +} + +void SwViewShell::PaintDesktop(const vcl::RenderContext& rRenderContext, const SwRect &rRect) +{ + if ( !GetWin() && !GetOut()->GetConnectMetaFile() ) + return; //for the printer we don't do anything here. + + if(comphelper::LibreOfficeKit::isActive()) + return; + + //Catch exceptions, so that it doesn't look so surprising. + //Can e.g. happen during Idle. + //Unfortunately we must at any rate Paint the rectangles next to the pages, + //as these are not painted at VisPortChgd. + bool bBorderOnly = false; + const SwRootFrame *pRoot = GetLayout(); + if ( rRect.Top() > pRoot->getFrameArea().Bottom() ) + { + const SwFrame *pPg = pRoot->Lower(); + while ( pPg && pPg->GetNext() ) + pPg = pPg->GetNext(); + if ( !pPg || !pPg->getFrameArea().Overlaps( VisArea() ) ) + bBorderOnly = true; + } + + const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode(); + + SwRegionRects aRegion( rRect ); + + //mod #i6193: remove sidebar area to avoid flickering + const SwPostItMgr* pPostItMgr = GetPostItMgr(); + const SwTwips nSidebarWidth = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? + pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : + 0; + + if ( bBorderOnly ) + { + const SwFrame *pPage =pRoot->Lower(); + SwRect aLeft( rRect ), aRight( rRect ); + while ( pPage ) + { + tools::Long nTmp = pPage->getFrameArea().Left(); + if ( nTmp < aLeft.Right() ) + aLeft.Right( nTmp ); + nTmp = pPage->getFrameArea().Right(); + if ( nTmp > aRight.Left() ) + { + aRight.Left( nTmp + nSidebarWidth ); + } + pPage = pPage->GetNext(); + } + aRegion.clear(); + if ( aLeft.HasArea() ) + aRegion.push_back( aLeft ); + if ( aRight.HasArea() ) + aRegion.push_back( aRight ); + } + else + { + const SwFrame *pPage = Imp()->GetFirstVisPage(&rRenderContext); + const SwTwips nBottom = rRect.Bottom(); + while ( pPage && !aRegion.empty() && + (pPage->getFrameArea().Top() <= nBottom) ) + { + SwRect aPageRect( pPage->getFrameArea() ); + if ( bBookMode ) + { + const SwPageFrame& rFormatPage = static_cast<const SwPageFrame*>(pPage)->GetFormatPage(); + aPageRect.SSize( rFormatPage.getFrameArea().SSize() ); + } + + const bool bSidebarRight = + static_cast<const SwPageFrame*>(pPage)->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT; + aPageRect.Pos().AdjustX( -(bSidebarRight ? 0 : nSidebarWidth) ); + aPageRect.AddWidth(nSidebarWidth ); + + if ( aPageRect.Overlaps( rRect ) ) + aRegion -= aPageRect; + + pPage = pPage->GetNext(); + } + } + if ( !aRegion.empty() ) + PaintDesktop_(aRegion); +} + +// PaintDesktop is split in two, this part is also used by PreviewPage +void SwViewShell::PaintDesktop_(const SwRegionRects &rRegion) +{ + // OD 2004-04-23 #116347# + GetOut()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR ); + GetOut()->SetLineColor(); + + for ( auto &rRgn : rRegion ) + { + const tools::Rectangle aRectangle(rRgn.SVRect()); + + // #i93170# + // Here we have a real Problem. On the one hand we have the buffering for paint + // and overlay which needs an embracing pair of DLPrePaint2/DLPostPaint2 calls, + // on the other hand the MapMode is not set correctly when this code is executed. + // This is done in the users of this method, for each SWpage before painting it. + // Since the MapMode is not correct here, the call to DLPostPaint2 will paint + // existing FormControls due to the current MapMode. + + // There are basically three solutions for this: + + // (1) Set the MapMode correct, move the background painting to the users of + // this code + + // (2) Do no DLPrePaint2/DLPostPaint2 here; no SdrObjects are allowed to lie in + // the desktop region. Disadvantage: the desktop will not be part of the + // buffers, e.g. overlay. Thus, as soon as overlay will be used over the + // desktop, it will not work. + + // (3) expand DLPostPaint2 with a flag to signal if FormControl paints shall + // be done or not + + // Currently, (3) will be the best possible solution. It will keep overlay and + // buffering intact and work without MapMode for single pages. In the medium + // to long run, (1) will need to be used and the bool bPaintFormLayer needs + // to be removed again + + // #i68597# inform Drawinglayer about display change + DLPrePaint2(vcl::Region(aRectangle)); + + // #i75172# needed to move line/Fill color setters into loop since DLPrePaint2 + // may exchange GetOut(), that's it's purpose. This happens e.g. at print preview. + GetOut()->SetFillColor( GetViewOptions()->GetAppBackgroundColor()); + GetOut()->SetLineColor(); + GetOut()->DrawRect(aRectangle); + + DLPostPaint2(false); + } + + GetOut()->Pop(); +} + +bool SwViewShell::CheckInvalidForPaint( const SwRect &rRect ) +{ + if ( !GetWin() ) + return false; + + const SwPageFrame *pPage = Imp()->GetFirstVisPage(GetOut()); + const SwTwips nBottom = VisArea().Bottom(); + const SwTwips nRight = VisArea().Right(); + bool bRet = false; + while ( !bRet && pPage && ((pPage->getFrameArea().Top() <= nBottom) && + (pPage->getFrameArea().Left() <= nRight))) + { + if ( pPage->IsInvalid() || pPage->IsInvalidFly() ) + bRet = true; + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + } + + if ( bRet ) + { + //Unfortunately Start/EndAction won't help here, as the Paint originated + //from GUI and so Clipping has been set against getting through. + //Ergo: do it all yourself (see ImplEndAction()) + if ( Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea()) + Imp()->DeletePaintRegion(); + + SwLayAction aAction( GetLayout(), Imp() ); + aAction.SetComplete( false ); + // We increment the action counter to avoid a recursive call of actions + // e.g. from a SwFEShell::RequestObjectResize(..) in bug 95829. + // A recursive call of actions is no good idea because the inner action + // can't format frames which are locked by the outer action. This may + // cause and endless loop. + ++mnStartAction; + aAction.Action(GetWin()->GetOutDev()); + --mnStartAction; + + std::optional<SwRegionRects> oRegion = Imp()->TakePaintRegion(); + if ( oRegion && aAction.IsBrowseActionStop() ) + { + //only of interest when something has changed in the visible range + bool bStop = true; + for ( size_t i = 0; i < oRegion->size(); ++i ) + { + const SwRect &rTmp = (*oRegion)[i]; + bStop = rTmp.Overlaps( VisArea() ); + if ( !bStop ) + break; + } + if ( bStop ) + oRegion.reset(); + } + + if ( oRegion ) + { + oRegion->LimitToOrigin(); + oRegion->Compress( SwRegionRects::CompressFuzzy ); + bRet = false; + if ( !oRegion->empty() ) + { + SwRegionRects aRegion( rRect ); + for ( size_t i = 0; i < oRegion->size(); ++i ) + { const SwRect &rTmp = (*oRegion)[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<SdrPaintWindow> m_TemporaryPaintWindow; + SdrPageWindow* m_pPatchedPageWindow; + SdrPaintWindow* m_pPreviousPaintWindow = nullptr; + +public: + RenderContextGuard(VclPtr<vcl::RenderContext>& pRef, vcl::RenderContext* pValue, SwViewShell* pShell) + : m_pPatchedPageWindow(nullptr) + { + pRef = pValue; + + if (pValue == pShell->GetWin()->GetOutDev()) + return; + + SdrView* pDrawView(pShell->Imp()->GetDrawView()); + + if (nullptr == pDrawView) + return; + + SdrPageView* pSdrPageView(pDrawView->GetSdrPageView()); + + if (nullptr != pSdrPageView) + { + m_pPatchedPageWindow = pSdrPageView->FindPageWindow(*pShell->GetWin()->GetOutDev()); + + if (nullptr != m_pPatchedPageWindow) + { + m_TemporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, *pValue)); + m_pPreviousPaintWindow = m_pPatchedPageWindow->patchPaintWindow(*m_TemporaryPaintWindow); + } + } + } + + ~RenderContextGuard() + { + if(nullptr != m_pPatchedPageWindow) + { + m_pPatchedPageWindow->unpatchPaintWindow(m_pPreviousPaintWindow); + } + } +}; +} + +void SwViewShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect) +{ + RenderContextGuard aGuard(mpOut, &rRenderContext, this); + if ( mnLockPaint ) + { + if ( Imp()->m_bSmoothUpdate ) + { + SwRect aTmp( rRect ); + if ( !Imp()->m_aSmoothRect.Contains( aTmp ) ) + Imp()->m_bStopSmooth = true; + else + { + Imp()->m_aSmoothRect = aTmp; + return; + } + } + else + return; + } + + if ( SwRootFrame::IsInPaint() ) + { + //During the publication of a page at printing the Paint is buffered. + SwPaintQueue::Add( this, SwRect( rRect ) ); + return; + } + + //With !nStartAction I try to protect me against erroneous code at other places. + //Hopefully it will not lead to problems!? + if ( mbPaintWorks && !mnStartAction ) + { + if( GetWin() && GetWin()->IsVisible() ) + { + SwRect aRect( rRect ); + if ( mbPaintInProgress ) //Guard against double Paints! + { + GetWin()->Invalidate( rRect ); + return; + } + + mbPaintInProgress = true; + CurrShell aCurr( this ); + SwRootFrame::SetNoVirDev( true ); + + //We don't want to Clip to and from, we trust that all are limited + //to the rectangle and only need to calculate the clipping once. + //The ClipRect is removed here once and not recovered, as externally + //no one needs it anymore anyway. + //Not when we paint a Metafile. + if( !GetOut()->GetConnectMetaFile() && GetOut()->IsClipRegion()) + GetOut()->SetClipRegion(); + + if ( IsPreview() ) + { + //When useful, process or destroy the old InvalidRect. + if ( aRect.Contains( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::sbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::sbLstAct = false; + } + else + { + //When one of the visible pages still has anything entered for + //Repaint, Repaint must be triggered. + if ( !CheckInvalidForPaint( aRect ) ) + { + // --> OD 2009-08-12 #i101192# + // start Pre/PostPaint encapsulation to avoid screen blinking + const vcl::Region aRepaintRegion(aRect.SVRect()); + DLPrePaint2(aRepaintRegion); + + // <-- + PaintDesktop(rRenderContext, aRect); + + //When useful, process or destroy the old InvalidRect. + if ( aRect.Contains( maInvalidRect ) ) + ResetInvalidRect(); + SwViewShell::sbLstAct = true; + GetLayout()->PaintSwFrame( rRenderContext, aRect ); + SwViewShell::sbLstAct = false; + // --> OD 2009-08-12 #i101192# + // end Pre/PostPaint encapsulation + DLPostPaint2(true); + // <-- + } + } + SwRootFrame::SetNoVirDev( false ); + mbPaintInProgress = false; + UISizeNotify(); + } + } + else + { + if ( maInvalidRect.IsEmpty() ) + maInvalidRect = SwRect( rRect ); + else + maInvalidRect.Union( SwRect( rRect ) ); + + if ( mbInEndAction && GetWin() ) + { + const vcl::Region aRegion(GetWin()->GetPaintRegion()); + RectangleVector aRectangles; + aRegion.GetRegionRectangles(aRectangles); + + for(const auto& rRectangle : aRectangles) + { + Imp()->AddPaintRect(SwRect(rRectangle)); + } + + //RegionHandle hHdl( aRegion.BeginEnumRects() ); + //Rectangle aRect; + //while ( aRegion.GetEnumRects( hHdl, aRect ) ) + // Imp()->AddPaintRect( aRect ); + //aRegion.EndEnumRects( hHdl ); + } + else if ( SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) && + GetOut() == GetWin()->GetOutDev() ) + { + // #i68597# + const vcl::Region aDLRegion(rRect); + DLPrePaint2(aDLRegion); + + rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR ); + rRenderContext.SetFillColor( Imp()->GetRetoucheColor() ); + rRenderContext.SetLineColor(); + rRenderContext.DrawRect( rRect ); + rRenderContext.Pop(); + // #i68597# + DLPostPaint2(true); + } + } +} + +void SwViewShell::PaintTile(VirtualDevice &rDevice, int contextWidth, int contextHeight, int tilePosX, int tilePosY, tools::Long tileWidth, tools::Long tileHeight) +{ + // SwViewShell's output device setup + // TODO clean up SwViewShell's approach to output devices (the many of + // them - mpBufferedOut, mpOut, mpWin, ...) + OutputDevice *pSaveOut = mpOut; + comphelper::LibreOfficeKit::setTiledPainting(true); + mpOut = &rDevice; + + // resizes the virtual device so to contain the entries context + rDevice.SetOutputSizePixel(Size(contextWidth, contextHeight)); + + // setup the output device to draw the tile + MapMode aMapMode(rDevice.GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapTwip); + aMapMode.SetOrigin(Point(-tilePosX, -tilePosY)); + + // Scaling. Must convert from pixels to twips. We know + // that VirtualDevices use a DPI of 96. + const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip); + Fraction scaleX = Fraction(contextWidth, tileWidth) * scale; + Fraction scaleY = Fraction(contextHeight, tileHeight) * scale; + aMapMode.SetScaleX(scaleX); + aMapMode.SetScaleY(scaleY); + rDevice.SetMapMode(aMapMode); + + // Update scaling of SwEditWin and its sub-widgets, needed for comments. + sal_uInt16 nOldZoomValue = 0; + if (GetWin() && GetWin()->GetMapMode().GetScaleX() != scaleX) + { + double fScale = double(scaleX); + SwViewOption aOption(*GetViewOptions()); + nOldZoomValue = aOption.GetZoom(); + aOption.SetZoom(fScale * 100); + ApplyViewOptions(aOption); + // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled. + GetWin()->EnableMapMode(false); + } + + tools::Rectangle aOutRect(Point(tilePosX, tilePosY), + rDevice.PixelToLogic(Size(contextWidth, contextHeight))); + + // Make the requested area visible -- we can't use MakeVisible as that will + // only scroll the contents, but won't zoom/resize if needed. + // Without this, items/text that are outside the visible area (in the SwView) + // won't be painted when rendering tiles (at least when using either the + // tiledrendering app, or the gtktiledviewer) -- although ultimately we + // probably want to fix things so that the SwView's area doesn't affect + // tiled rendering? + VisPortChgd(SwRect(aOutRect)); + + // Invoke SwLayAction if layout is not yet ready. + CheckInvalidForPaint(SwRect(aOutRect)); + + // draw - works in logic coordinates + Paint(rDevice, aOutRect); + + SwPostItMgr* pPostItMgr = GetPostItMgr(); + if (GetViewOptions()->IsPostIts() && pPostItMgr) + pPostItMgr->PaintTile(rDevice); + + // SwViewShell's output device tear down + + // A view shell can get a PaintTile call for a tile at a zoom level + // different from the one, the related client really is. + // In such a case it is better to reset the current scale value to + // the original one, since such a value should be in synchronous with + // the zoom level in the client (see setClientZoom). + // At present the zoom value returned by GetViewOptions()->GetZoom() is + // used in SwXTextDocument methods (postMouseEvent and setGraphicSelection) + // for passing the correct mouse position to an edited chart (if any). + if (nOldZoomValue !=0) + { + SwViewOption aOption(*GetViewOptions()); + aOption.SetZoom(nOldZoomValue); + ApplyViewOptions(aOption); + + // Changing the zoom value doesn't always trigger the updating of + // the client ole object area, so we call it directly. + SfxInPlaceClient* pIPClient = GetSfxViewShell()->GetIPClient(); + if (pIPClient) + { + pIPClient->VisAreaChanged(); + } + // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled. + GetWin()->EnableMapMode(false); + } + + mpOut = pSaveOut; + comphelper::LibreOfficeKit::setTiledPainting(false); +} + +void SwViewShell::SetBrowseBorder( const Size& rNew ) +{ + if( rNew != maBrowseBorder ) + { + maBrowseBorder = rNew; + if ( maVisArea.HasArea() ) + InvalidateLayout( false ); + } +} + +const Size& SwViewShell::GetBrowseBorder() const +{ + return maBrowseBorder; +} + +sal_Int32 SwViewShell::GetBrowseWidth() const +{ + const SwPostItMgr* pPostItMgr = GetPostItMgr(); + if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ) + { + Size aBorder( maBrowseBorder ); + aBorder.AdjustWidth(maBrowseBorder.Width() ); + aBorder.AdjustWidth(pPostItMgr->GetSidebarWidth(true) + pPostItMgr->GetSidebarBorderWidth(true) ); + return maVisArea.Width() - GetOut()->PixelToLogic(aBorder).Width(); + } + else + return maVisArea.Width() - 2 * GetOut()->PixelToLogic(maBrowseBorder).Width(); +} + +void SwViewShell::InvalidateLayout( bool bSizeChanged ) +{ + if ( !bSizeChanged && !GetViewOptions()->getBrowseMode() && + !GetViewOptions()->IsWhitespaceHidden() ) + return; + + CurrShell aCurr( this ); + + OSL_ENSURE( GetLayout(), "Layout not ready" ); + + // When the Layout doesn't have a height yet, nothing is formatted. + // That leads to problems with Invalidate, e.g. when setting up a new View + // the content is inserted and formatted (regardless of empty VisArea). + // Therefore the pages must be roused for formatting. + if( !GetLayout()->getFrameArea().Height() ) + { + SwFrame* pPage = GetLayout()->Lower(); + while( pPage ) + { + pPage->InvalidateSize_(); + pPage = pPage->GetNext(); + } + return; + } + + LockPaint(LockPaintReason::InvalidateLayout); + StartAction(); + + SwPageFrame *pPg = static_cast<SwPageFrame*>(GetLayout()->Lower()); + do + { pPg->InvalidateSize(); + pPg->InvalidatePrt_(); + pPg->InvaPercentLowers(); + if ( bSizeChanged ) + { + pPg->PrepareHeader(); + pPg->PrepareFooter(); + } + pPg = static_cast<SwPageFrame*>(pPg->GetNext()); + } while ( pPg ); + + // When the size ratios in browse mode change, + // the Position and PrtArea of the Content and Tab frames must be Invalidated. + SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Pos; + // In case of layout or mode change, the ContentFrames need a size-Invalidate + // because of printer/screen formatting. + if ( bSizeChanged ) + nInv |= SwInvalidateFlags::Size | SwInvalidateFlags::Direction; + + GetLayout()->InvalidateAllContent( nInv ); + + SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(GetLayout()->Lower()) ); + + EndAction(); + UnlockPaint(); +} + +SwRootFrame *SwViewShell::GetLayout() const +{ + return mpLayout.get(); +} + +vcl::RenderContext& SwViewShell::GetRefDev() const +{ + OutputDevice* pTmpOut = nullptr; + if ( GetWin() && + GetViewOptions()->getBrowseMode() && + !GetViewOptions()->IsPrtFormat() ) + pTmpOut = GetWin()->GetOutDev(); + else + pTmpOut = GetDoc()->getIDocumentDeviceAccess().getReferenceDevice( true ); + + return *pTmpOut; +} + +const SwNodes& SwViewShell::GetNodes() const +{ + return mxDoc->GetNodes(); +} + +void SwViewShell::DrawSelChanged() +{ +} + +Size SwViewShell::GetDocSize() const +{ + Size aSz; + const SwRootFrame* pRoot = GetLayout(); + if( pRoot ) + aSz = pRoot->getFrameArea().SSize(); + + return aSz; +} + +SfxItemPool& SwViewShell::GetAttrPool() +{ + return GetDoc()->GetAttrPool(); +} + +void SwViewShell::ApplyViewOptions( const SwViewOption &rOpt ) +{ + for(SwViewShell& rSh : GetRingContainer()) + rSh.StartAction(); + + ImplApplyViewOptions( rOpt ); + + // With one layout per view it is no longer necessary + // to sync these "layout related" view options + // But as long as we have to disable "multiple layout" + + for(SwViewShell& rSh : GetRingContainer()) + { + if(&rSh == this) + continue; + SwViewOption aOpt( *rSh.GetViewOptions() ); + aOpt.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<sw::mark::IFieldmark&>(**iter))); + if (eMode == sw::FieldmarkMode::ShowResult) + { + if (*rCursor.GetPoint() <= sepPos + && *rCursor.GetPoint() != (**iter).GetMarkStart()) + { + return true; + } + } + else + { + if (sepPos < *rCursor.GetPoint()) + { + return true; + } + } + } + } + return false; +} + +void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt ) +{ + if (*mpOpt == rOpt) + return; + + vcl::Window *pMyWin = GetWin(); + if( !pMyWin ) + { + OSL_ENSURE( pMyWin, "SwViewShell::ApplyViewOptions: no window" ); + return; + } + + CurrShell aCurr( this ); + + bool bReformat = false; + + if( mpOpt->IsShowHiddenField() != rOpt.IsShowHiddenField() ) + { + static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))-> + SetHiddenFlag( !rOpt.IsShowHiddenField() ); + bReformat = true; + } + if ( mpOpt->IsShowHiddenPara() != rOpt.IsShowHiddenPara() ) + { + SwHiddenParaFieldType* pFieldType = static_cast<SwHiddenParaFieldType*>(GetDoc()-> + getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenPara)); + if( pFieldType && pFieldType->HasWriterListeners() ) + pFieldType->PrintHiddenPara(); + bReformat = true; + } + if ( !bReformat && mpOpt->IsShowHiddenChar() != rOpt.IsShowHiddenChar() ) + { + bReformat = GetDoc()->ContainsHiddenChars(); + } + if ( mpOpt->IsShowChangesInMargin() != rOpt.IsShowChangesInMargin() || + mpOpt->IsShowChangesInMargin2() != rOpt.IsShowChangesInMargin2() ) + { + if (rOpt.IsShowChangesInMargin()) + GetDoc()->GetDocumentRedlineManager().HideAll( + /*bDeletion=*/!rOpt.IsShowChangesInMargin2() ); + else + GetDoc()->GetDocumentRedlineManager().ShowAll(); + GetLayout()->SetHideRedlines( false ); + } + + // bReformat becomes true, if ... + // - fieldnames apply or not ... + // ( - SwEndPortion must _no_ longer be generated. ) + // - Of course, the screen is something completely different than the printer ... + bool const isToggleFieldNames(mpOpt->IsFieldName() != rOpt.IsFieldName()); + + if (mpOpt->IsFieldName() != rOpt.IsFieldName() + || mpOpt->IsParagraph() != rOpt.IsParagraph()) + { + GetLayout()->SetFieldmarkMode( rOpt.IsFieldName() + ? sw::FieldmarkMode::ShowCommand + : sw::FieldmarkMode::ShowResult, + rOpt.IsParagraph() + ? sw::ParagraphBreakMode::Shown + : sw::ParagraphBreakMode::Hidden); + bReformat = true; + } + + // The map mode is changed, minima/maxima will be attended by UI + if( mpOpt->GetZoom() != rOpt.GetZoom() && !IsPreview() ) + { + MapMode aMode( pMyWin->GetMapMode() ); + Fraction aNewFactor( rOpt.GetZoom(), 100 ); + aMode.SetScaleX( aNewFactor ); + aMode.SetScaleY( aNewFactor ); + pMyWin->SetMapMode( aMode ); + // if not a reference device (printer) is used for formatting, + // but the screen, new formatting is needed for zoomfactor changes. + if (mpOpt->getBrowseMode() || mpOpt->IsWhitespaceHidden()) + bReformat = true; + } + + bool bBrowseModeChanged = false; + if( mpOpt->getBrowseMode() != rOpt.getBrowseMode() ) + { + bBrowseModeChanged = true; + bReformat = true; + } + else if( mpOpt->getBrowseMode() && mpOpt->IsPrtFormat() != rOpt.IsPrtFormat() ) + bReformat = true; + + bool bHideWhitespaceModeChanged = false; + if (mpOpt->IsWhitespaceHidden() != rOpt.IsWhitespaceHidden()) + { + // When whitespace is hidden, view change needs reformatting. + bHideWhitespaceModeChanged = true; + bReformat = true; + } + + if ( HasDrawView() || rOpt.IsGridVisible() ) + { + if ( !HasDrawView() ) + MakeDrawView(); + + SwDrawView *pDView = Imp()->GetDrawView(); + if ( pDView->IsDragStripes() != rOpt.IsCrossHair() ) + pDView->SetDragStripes( rOpt.IsCrossHair() ); + + if ( pDView->IsGridSnap() != rOpt.IsSnap() ) + pDView->SetGridSnap( rOpt.IsSnap() ); + + if ( pDView->IsGridVisible() != rOpt.IsGridVisible() ) + pDView->SetGridVisible( rOpt.IsGridVisible() ); + + const Size &rSz = rOpt.GetSnapSize(); + pDView->SetGridCoarse( rSz ); + + const Size aFSize + ( rSz.Width() ? rSz.Width() / (rOpt.GetDivisionX()+1) : 0, + rSz.Height()? rSz.Height()/ (rOpt.GetDivisionY()+1) : 0); + pDView->SetGridFine( aFSize ); + Fraction aSnGrWdtX(rSz.Width(), rOpt.GetDivisionX() + 1); + Fraction aSnGrWdtY(rSz.Height(), rOpt.GetDivisionY() + 1); + pDView->SetSnapGridWidth( aSnGrWdtX, aSnGrWdtY ); + + // set handle size to 9 pixels, always + pDView->SetMarkHdlSizePixel(9); + } + + bool bOnlineSpellChgd = mpOpt->IsOnlineSpell() != rOpt.IsOnlineSpell(); + + *mpOpt = rOpt; // First the options are taken. + mpOpt->SetUIOptions(rOpt); + + mxDoc->GetDocumentSettingManager().set(DocumentSettingId::HTML_MODE, 0 != ::GetHtmlMode(mxDoc->GetDocShell())); + + if( bBrowseModeChanged || bHideWhitespaceModeChanged ) + { + // #i44963# Good occasion to check if page sizes in + // page descriptions are still set to (LONG_MAX, LONG_MAX) (html import) + mxDoc->CheckDefaultPageFormat(); + InvalidateLayout( true ); + } + + SwXTextDocument* pModel = comphelper::getFromUnoTunnel<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument()); + SfxLokHelper::notifyViewRenderState(GetSfxViewShell(), pModel); + + pMyWin->Invalidate(); + if ( bReformat ) + { + // Nothing helps, we need to send all ContentFrames a + // Prepare, we format anew: + StartAction(); + Reformat(); + EndAction(); + } + + if (isToggleFieldNames) + { + for(SwViewShell& rSh : GetRingContainer()) + { + if (SwCursorShell *const pSh = dynamic_cast<SwCursorShell *>(&rSh)) + { + if ((mpOpt->IsFieldName() && pSh->CursorInsideInputField()) + || IsCursorInFieldmarkHidden(*pSh->GetCursor(), + pSh->GetLayout()->GetFieldmarkMode())) + { // move cursor out of field + pSh->Left(1, SwCursorSkipMode::Chars); + } + } + } + // the layout changes but SetModified() wasn't called so do it here: + mxDoc->GetDocumentLayoutManager().ClearSwLayouterEntries(); + } + + if( !bOnlineSpellChgd ) + return; + + if ( !comphelper::LibreOfficeKit::isActive() ) + { + bool bOnlineSpl = rOpt.IsOnlineSpell(); + for(SwViewShell& rSh : GetRingContainer()) + { + if(&rSh == this) + continue; + rSh.mpOpt->SetOnlineSpell( bOnlineSpl ); + vcl::Window *pTmpWin = rSh.GetWin(); + if( pTmpWin ) + pTmpWin->Invalidate(); + } + } +} + +void SwViewShell::SetUIOptions( const SwViewOption &rOpt ) +{ + mpOpt->SetUIOptions(rOpt); + //the API-Flag of the view options is set but never reset + //it is required to set scroll bars in readonly documents + if(rOpt.IsStarOneSetting()) + mpOpt->SetStarOneSetting(true); + + mpOpt->SetSymbolFont(rOpt.GetSymbolFont()); +} + +void SwViewShell::SetReadonlyOption(bool bSet) +{ + //JP 01.02.99: at readonly flag query properly + // and if need be format; Bug 61335 + + // Are we switching from readonly to edit? + if( bSet == mpOpt->IsReadonly() ) + return; + + // so that the flags can be queried properly. + mpOpt->SetReadonly( false ); + + bool bReformat = mpOpt->IsFieldName(); + + mpOpt->SetReadonly( bSet ); + + if( bReformat ) + { + StartAction(); + Reformat(); + if ( GetWin() ) + 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() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + if (mpOpt->IsPagePreview() && !officecfg::Office::Common::Accessibility::IsForPagePreviews::get()) + { + mpAccOptions->SetAlwaysAutoColor(false); + mpAccOptions->SetStopAnimatedGraphics(false); + } + else + { + mpAccOptions->SetAlwaysAutoColor(SvtAccessibilityOptions::GetIsAutomaticFontColor()); + mpAccOptions->SetStopAnimatedGraphics(! SvtAccessibilityOptions::GetIsAllowAnimatedGraphics()); + + // Form view + // Always set this option, not only if document is read-only: + mpOpt->SetSelectionInReadonly(SvtAccessibilityOptions::IsSelectionInReadonly()); + } +} +#endif // ENABLE_WASM_STRIP_ACCESSIBILITY + +ShellResource* SwViewShell::GetShellRes() +{ + return spShellRes; +} + +void SwViewShell::SetCareDialog(const std::shared_ptr<weld::Window>& 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<const SwPageFrame*> + (pTmpRoot->Lower()); + + while( --nPageNum && pPage->GetNext() ) + pPage = static_cast<const SwPageFrame*>( pPage->GetNext() ); + + if( !bSkipEmptyPages && pPage->IsEmptyPage() && pPage->GetNext() ) + pPage = static_cast<const SwPageFrame*>( pPage->GetNext() ); + + aSize = pPage->getFrameArea().SSize(); + } + return aSize; +} + +void SwViewShell::OnGraphicArrived(const SwRect& rRect) +{ + for(SwViewShell& rShell : GetRingContainer()) + { + CurrShell aCurr(&rShell); + if(rShell.IsPreview()) + { + if(rShell.GetWin()) + ::RepaintPagePreview(&rShell, rRect); + } + else if(rShell.VisArea().Overlaps(rRect) && OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType()) + { + // invalidate instead of painting + rShell.GetWin()->Invalidate(rRect.SVRect()); + } + } +} +// #i12836# enhanced pdf export +sal_Int32 SwViewShell::GetPageNumAndSetOffsetForPDF( OutputDevice& rOut, const SwRect& rRect ) const +{ + OSL_ENSURE( GetLayout(), "GetPageNumAndSetOffsetForPDF assumes presence of layout" ); + + sal_Int32 nRet = -1; + + // #i40059# Position out of bounds: + SwRect aRect( rRect ); + aRect.Pos().setX( std::max( aRect.Left(), GetLayout()->getFrameArea().Left() ) ); + + const SwPageFrame* pPage = GetLayout()->GetPageAtPos( aRect.Center() ); + if ( pPage ) + { + OSL_ENSURE( pPage, "GetPageNumAndSetOffsetForPDF: No page found" ); + + Point aOffset( pPage->getFrameArea().Pos() ); + aOffset.setX( -aOffset.X() ); + aOffset.setY( -aOffset.Y() ); + + MapMode aMapMode( rOut.GetMapMode() ); + aMapMode.SetOrigin( aOffset ); + rOut.SetMapMode( aMapMode ); + + nRet = pPage->GetPhyPageNum() - 1; + } + + return nRet; +} + +// --> PB 2007-05-30 #146850# +const BitmapEx& SwViewShell::GetReplacementBitmap( bool bIsErrorState ) +{ + if (bIsErrorState) + { + if (!m_xErrorBmp) + m_xErrorBmp.reset(new BitmapEx(RID_GRAPHIC_ERRORBMP)); + return *m_xErrorBmp; + } + + if (!m_xReplaceBmp) + m_xReplaceBmp.reset(new BitmapEx(RID_GRAPHIC_REPLACEBMP)); + return *m_xReplaceBmp; +} + +void SwViewShell::DeleteReplacementBitmaps() +{ + m_xErrorBmp.reset(); + m_xReplaceBmp.reset(); +} + +SwPostItMgr* SwViewShell::GetPostItMgr() +{ + SwView* pView = GetDoc()->GetDocShell() ? GetDoc()->GetDocShell()->GetView() : nullptr; + if ( pView ) + return pView->GetPostItMgr(); + + return nullptr; +} + +void SwViewShell::GetFirstLastVisPageNumbers(SwVisiblePageNumbers& rVisiblePageNumbers) +{ + SwView* pView = GetDoc()->GetDocShell() ? GetDoc()->GetDocShell()->GetView() : nullptr; + if (!pView) + return; + SwRect rViewVisArea(pView->GetVisArea()); + vcl::RenderContext* pRenderContext = GetOut(); + const SwPageFrame* pPageFrame = Imp()->GetFirstVisPage(pRenderContext); + SwRect rPageRect = pPageFrame->getFrameArea(); + rPageRect.AddBottom(-pPageFrame->GetBottomMargin()); + while (!rPageRect.Overlaps(rViewVisArea) && pPageFrame->GetNext()) + { + pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext()); + rPageRect = pPageFrame->getFrameArea(); + if (rPageRect.Top() > 0) + rPageRect.AddBottom(-pPageFrame->GetBottomMargin()); + } + rVisiblePageNumbers.nFirstPhy = pPageFrame->GetPhyPageNum(); + rVisiblePageNumbers.nFirstVirt = pPageFrame->GetVirtPageNum(); + const SvxNumberType& rFirstVisNum = pPageFrame->GetPageDesc()->GetNumType(); + rVisiblePageNumbers.sFirstCustomPhy = rFirstVisNum.GetNumStr(rVisiblePageNumbers.nFirstPhy); + rVisiblePageNumbers.sFirstCustomVirt = rFirstVisNum.GetNumStr(rVisiblePageNumbers.nFirstVirt); + pPageFrame = Imp()->GetLastVisPage(pRenderContext); + rPageRect = pPageFrame->getFrameArea(); + rPageRect.AddTop(pPageFrame->GetTopMargin()); + while (!rPageRect.Overlaps(rViewVisArea) && pPageFrame->GetPrev()) + { + pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetPrev()); + rPageRect = pPageFrame->getFrameArea(); + rPageRect.AddTop(pPageFrame->GetTopMargin()); + } + rVisiblePageNumbers.nLastPhy = pPageFrame->GetPhyPageNum(); + rVisiblePageNumbers.nLastVirt = pPageFrame->GetVirtPageNum(); + const SvxNumberType& rLastVisNum = pPageFrame->GetPageDesc()->GetNumType(); + rVisiblePageNumbers.sLastCustomPhy = rLastVisNum.GetNumStr(rVisiblePageNumbers.nLastPhy); + rVisiblePageNumbers.sLastCustomVirt = rLastVisNum.GetNumStr(rVisiblePageNumbers.nLastVirt); +} + +/* + * Document Interface Access + */ +const IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() const { return mxDoc->GetDocumentSettingManager(); } +IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() { return mxDoc->GetDocumentSettingManager(); } +const IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() const { return mxDoc->getIDocumentDeviceAccess(); } +IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() { return mxDoc->getIDocumentDeviceAccess(); } +const IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() const { return mxDoc->getIDocumentMarkAccess(); } +IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() { return mxDoc->getIDocumentMarkAccess(); } +const IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() const { return mxDoc->getIDocumentDrawModelAccess(); } +IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() { return mxDoc->getIDocumentDrawModelAccess(); } +const IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() const { return mxDoc->getIDocumentRedlineAccess(); } +IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() { return mxDoc->getIDocumentRedlineAccess(); } +const IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() const { return mxDoc->getIDocumentLayoutAccess(); } +IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() { return mxDoc->getIDocumentLayoutAccess(); } +IDocumentContentOperations& SwViewShell::getIDocumentContentOperations() { return mxDoc->getIDocumentContentOperations(); } +IDocumentStylePoolAccess& SwViewShell::getIDocumentStylePoolAccess() { return mxDoc->getIDocumentStylePoolAccess(); } +const IDocumentStatistics& SwViewShell::getIDocumentStatistics() const { return mxDoc->getIDocumentStatistics(); } + +IDocumentUndoRedo & SwViewShell::GetIDocumentUndoRedo() +{ return mxDoc->GetIDocumentUndoRedo(); } +IDocumentUndoRedo const& SwViewShell::GetIDocumentUndoRedo() const +{ return mxDoc->GetIDocumentUndoRedo(); } + +// --> OD 2007-11-14 #i83479# +const IDocumentListItems* SwViewShell::getIDocumentListItemsAccess() const +{ + return &mxDoc->getIDocumentListItems(); +} + +const IDocumentOutlineNodes* SwViewShell::getIDocumentOutlineNodesAccess() const +{ + return &mxDoc->getIDocumentOutlineNodes(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/vnew.cxx b/sw/source/core/view/vnew.cxx new file mode 100644 index 0000000000..613a15df63 --- /dev/null +++ b/sw/source/core/view/vnew.cxx @@ -0,0 +1,383 @@ +/* -*- 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 <sfx2/printer.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentState.hxx> +#include <docsh.hxx> +#include <viewsh.hxx> +#include <rootfrm.hxx> +#include <viewimp.hxx> +#include <viewopt.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <fntcache.hxx> +#include <docufld.hxx> +#include <ptqueue.hxx> +#include <dview.hxx> +#include <ndgrf.hxx> +#include <ndindex.hxx> +#include <accessibilityoptions.hxx> + +void SwViewShell::Init( const SwViewOption *pNewOpt ) +{ + mbDocSizeChgd = false; + + // We play it safe: Remove old font information whenever the printer + // resolution or the zoom factor changes. For that, Init() and Reformat() + // are the most secure places. + pFntCache->Flush( ); + + // ViewOptions are created dynamically + + if( !mpOpt ) + { + mpOpt.reset(new SwViewOption); + + // ApplyViewOptions() does not need to be called + if( pNewOpt ) + { + *mpOpt = *pNewOpt; + // Zoom factor needs to be set because there is no call to ApplyViewOptions() during + // CTOR for performance reasons. + if( GetWin() && 100 != mpOpt->GetZoom() ) + { + MapMode aMode( mpWin->GetMapMode() ); + const Fraction aNewFactor( mpOpt->GetZoom(), 100 ); + aMode.SetScaleX( aNewFactor ); + aMode.SetScaleY( aNewFactor ); + mpWin->SetMapMode( aMode ); + } + } + } + + SwDocShell* pDShell = mxDoc->GetDocShell(); + mxDoc->GetDocumentSettingManager().set(DocumentSettingId::HTML_MODE, 0 != ::GetHtmlMode( pDShell ) ); + // set readonly flag at ViewOptions before creating layout. Otherwise, + // one would have to reformat again. + + if( pDShell && pDShell->IsReadOnly() ) + mpOpt->SetReadonly( true ); + + SAL_INFO( "sw.core", "View::Init - before InitPrt" ); + OutputDevice* pPDFOut = nullptr; + + if (mpOut && (OUTDEV_PDF == mpOut->GetOutDevType())) + pPDFOut = mpOut; + + // Only setup the printer if we need one: + const bool bBrowseMode = mpOpt->getBrowseMode(); + if( pPDFOut ) + InitPrt( pPDFOut ); + + // i#44963 Good occasion to check if page sizes in + // page descriptions are still set to (LONG_MAX, LONG_MAX) (html import) + if ( !bBrowseMode ) + { + mxDoc->CheckDefaultPageFormat(); + } + + SAL_INFO( "sw.core", "View::Init - after InitPrt" ); + if( GetWin() ) + { + SwViewOption::Init( GetWin()->GetOutDev() ); + GetWin()->GetOutDev()->SetFillColor(); + GetWin()->SetBackground(); + GetWin()->GetOutDev()->SetLineColor(); + } + + // Create a new layout, if there is no one available + if( !mpLayout ) + { + // Here's the code which disables the usage of "multiple" layouts at the moment + // If the problems with controls and groups objects are solved, + // this code can be removed... + SwViewShell *pCurrShell = GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell(); + if( pCurrShell ) + mpLayout = pCurrShell->mpLayout; + // end of "disable multiple layouts" + if( !mpLayout ) + { + // switched to two step construction because creating the layout in SwRootFrame needs a valid pLayout set + mpLayout = SwRootFramePtr(new SwRootFrame(mxDoc->GetDfltFrameFormat(), this), + &SwFrame::DestroyFrame); + mpLayout->Init( mxDoc->GetDfltFrameFormat() ); + } + } + SizeChgNotify(); + + // XForms mode: initialize XForms mode, based on design mode (draw view) + // MakeDrawView() requires layout + if( GetDoc()->isXForms() ) + { + if( ! HasDrawView() ) + MakeDrawView(); + mpOpt->SetFormView( ! GetDrawView()->IsDesignMode() ); + } +} + +/// CTor for the first Shell. +SwViewShell::SwViewShell( SwDoc& rDocument, vcl::Window *pWindow, + const SwViewOption *pNewOpt, OutputDevice *pOutput, + tools::Long nFlags ) + : + mpSfxViewShell( nullptr ), + mpImp( new SwViewShellImp( this ) ), + mpWin( pWindow ), + mpOut( pOutput ? pOutput + : pWindow ? pWindow->GetOutDev() + : static_cast<OutputDevice*>(rDocument.getIDocumentDeviceAccess().getPrinter( true ))), + mpAccOptions( new SwAccessibilityOptions ), + mbShowHeaderSeparator( false ), + mbShowFooterSeparator( false ), + mbHeaderFooterEdit( false ), + mpTargetPaintWindow(nullptr), + mpBufferedOut(nullptr), + mxDoc( &rDocument ), + mnStartAction( 0 ), + mnLockPaint( 0 ), + mbSelectAll(false), + mbOutputToWindow(false), + mpPrePostOutDev(nullptr) +{ + // in order to suppress event handling in + // <SwDrawContact::Changed> during construction of <SwViewShell> instance + mbInConstructor = true; + + mbPaintInProgress = mbViewLocked = mbInEndAction = false; + mbPaintWorks = mbEnableSmooth = true; + mbPreview = 0 !=( VSHELLFLAG_ISPREVIEW & nFlags ); + + // i#38810 Do not reset modified state of document, + // if it's already been modified. + const bool bIsDocModified( mxDoc->getIDocumentState().IsModified() ); + OutputDevice* pOrigOut = mpOut; + Init( pNewOpt ); // may change the Outdev (InitPrt()) + mpOut = pOrigOut; + + // initialize print preview layout after layout + // is created in <SwViewShell::Init(..)> - called above. + if ( mbPreview ) + { + // init page preview layout + mpImp->InitPagePreviewLayout(); + } + + CurrShell aCurr( this ); + + static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))-> + SetHiddenFlag( !mpOpt->IsShowHiddenField() ); + + // In Init a standard FrameFormat is created. + if ( !mxDoc->GetIDocumentUndoRedo().IsUndoNoResetModified() + && !bIsDocModified ) + { + mxDoc->getIDocumentState().ResetModified(); + } + + // extend format cache. + if ( SwTextFrame::GetTextCache()->GetCurMax() < 2550 ) + SwTextFrame::GetTextCache()->IncreaseMax( 100 ); + if( mpOpt->IsGridVisible() || getIDocumentDrawModelAccess().GetDrawModel() ) + Imp()->MakeDrawView(); + + mbInConstructor = false; +} + +/// CTor for further Shells on a document. +SwViewShell::SwViewShell( SwViewShell& rShell, vcl::Window *pWindow, + OutputDevice * pOutput, tools::Long const nFlags) + : Ring( &rShell ) , + maBrowseBorder( rShell.maBrowseBorder ), + mpSfxViewShell( nullptr ), + mpImp( new SwViewShellImp( this ) ), + mpWin( pWindow ), + mpOut( pOutput ? pOutput + : pWindow ? pWindow->GetOutDev() + : static_cast<OutputDevice*>(rShell.GetDoc()->getIDocumentDeviceAccess().getPrinter( true ))), + mpAccOptions( new SwAccessibilityOptions ), + mbShowHeaderSeparator( false ), + mbShowFooterSeparator( false ), + mbHeaderFooterEdit( false ), + mpTargetPaintWindow(nullptr), + mpBufferedOut(nullptr), + mxDoc( rShell.GetDoc() ), + mnStartAction( 0 ), + mnLockPaint( 0 ), + mbSelectAll(false), + mbOutputToWindow(false), + mpPrePostOutDev(nullptr) +{ + // in order to suppress event handling in + // <SwDrawContact::Changed> during construction of <SwViewShell> instance + mbInConstructor = true; + + mbPaintWorks = mbEnableSmooth = true; + mbPaintInProgress = mbViewLocked = mbInEndAction = false; + mbPreview = 0 !=( VSHELLFLAG_ISPREVIEW & nFlags ); + + if( nFlags & VSHELLFLAG_SHARELAYOUT ) + mpLayout = rShell.mpLayout; + + CurrShell aCurr( this ); + + bool bModified = mxDoc->getIDocumentState().IsModified(); + + OutputDevice* pOrigOut = mpOut; + Init( rShell.GetViewOptions() ); // might change Outdev (InitPrt()) + mpOut = pOrigOut; + + if ( mbPreview ) + mpImp->InitPagePreviewLayout(); + + static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))-> + SetHiddenFlag( !mpOpt->IsShowHiddenField() ); + + // In Init a standard FrameFormat is created. + if( !bModified && !mxDoc->GetIDocumentUndoRedo().IsUndoNoResetModified() ) + { + mxDoc->getIDocumentState().ResetModified(); + } + + // extend format cache. + if ( SwTextFrame::GetTextCache()->GetCurMax() < 2550 ) + SwTextFrame::GetTextCache()->IncreaseMax( 100 ); + if( mpOpt->IsGridVisible() || getIDocumentDrawModelAccess().GetDrawModel() ) + Imp()->MakeDrawView(); + + mbInConstructor = false; + +} + +SwViewShell::~SwViewShell() +{ + IDocumentLayoutAccess* const pLayoutAccess + = mxDoc ? &mxDoc->getIDocumentLayoutAccess() : nullptr; + + { + CurrShell aCurr( this ); + mbPaintWorks = false; + + // i#9684 Stopping the animated graphics is not + // necessary during printing or pdf export, because the animation + // has not been started in this case. + if( mxDoc && GetWin() ) + { + SwNodes& rNds = mxDoc->GetNodes(); + + SwStartNode *pStNd; + SwNodeIndex aIdx( *rNds.GetEndOfAutotext().StartOfSectionNode(), 1 ); + while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) ) + { + ++aIdx; + SwGrfNode *pGNd = aIdx.GetNode().GetGrfNode(); + if ( nullptr != pGNd ) + { + if( pGNd->IsAnimated() ) + { + SwIterator<SwFrame,SwGrfNode> aIter( *pGNd ); + for( SwFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) + { + OSL_ENSURE( pFrame->IsNoTextFrame(), "GraphicNode with Text?" ); + static_cast<SwNoTextFrame*>(pFrame)->StopAnimation( mpOut ); + } + } + } + aIdx.Assign( *pStNd->EndOfSectionNode(), +1 ); + } + + GetDoc()->StopNumRuleAnimations( mpOut ); + } + + mpImp.reset(); + + if (mxDoc) + { + if( mxDoc->getReferenceCount() > 1 ) + GetLayout()->ResetNewLayout(); + } + + mpOpt.reset(); + + // resize format cache. + if ( SwTextFrame::GetTextCache()->GetCurMax() > 250 ) + SwTextFrame::GetTextCache()->DecreaseMax( 100 ); + + // Remove from PaintQueue if necessary + SwPaintQueue::Remove( this ); + + OSL_ENSURE( !mnStartAction, "EndAction() pending." ); + } + + if ( pLayoutAccess ) + { + GetLayout()->DeRegisterShell( this ); + if(pLayoutAccess->GetCurrentViewShell()==this) + { + pLayoutAccess->SetCurrentViewShell(nullptr); + for(SwViewShell& rShell : GetRingContainer()) + { + if(&rShell != this) + { + pLayoutAccess->SetCurrentViewShell(&rShell); + break; + } + } + } + } + + mpAccOptions.reset(); +} + +bool SwViewShell::HasDrawView() const +{ + return Imp() && Imp()->HasDrawView(); +} + +void SwViewShell::MakeDrawView() +{ + Imp()->MakeDrawView( ); +} + +bool SwViewShell::HasDrawViewDrag() const +{ + return Imp()->HasDrawView() && Imp()->GetDrawView()->IsDragObj(); +} + +SdrView* SwViewShell::GetDrawView() +{ + return Imp()->GetDrawView(); +} + +SdrView* SwViewShell::GetDrawViewWithValidMarkList() +{ + SwDrawView* pDView = Imp()->GetDrawView(); + pDView->ValidateMarkList(); + return pDView; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/view/vprint.cxx b/sw/source/core/view/vprint.cxx new file mode 100644 index 0000000000..bc5dd5bd6c --- /dev/null +++ b/sw/source/core/view/vprint.cxx @@ -0,0 +1,676 @@ +/* -*- 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 <hintids.hxx> +#include <sfx2/printer.hxx> +#include <svx/svdview.hxx> +#include <osl/diagnose.h> +#include <tools/UnitConversion.hxx> + +#include <txtfld.hxx> +#include <fmtfld.hxx> +#include <fmtfsize.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <cntfrm.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <fesh.hxx> +#include <pam.hxx> +#include <viewimp.hxx> +#include <layact.hxx> +#include <ndtxt.hxx> +#include <viewopt.hxx> +#include <printdata.hxx> +#include <pagedesc.hxx> +#include <ptqueue.hxx> +#include <viscrs.hxx> +#include <fmtpdsc.hxx> +#include <PostItMgr.hxx> +#include <vprint.hxx> + +using namespace ::com::sun::star; + +/// Painting buffer +class SwQueuedPaint +{ +public: + SwQueuedPaint *pNext; + SwViewShell *pSh; + SwRect aRect; + + SwQueuedPaint( SwViewShell *pNew, const SwRect &rRect ) : + pNext( nullptr ), + pSh( pNew ), + aRect( rRect ) + {} +}; + +SwQueuedPaint *SwPaintQueue::s_pPaintQueue = nullptr; + +namespace { + +// saves some settings from the draw view +class SwDrawViewSave +{ + SdrView* pDV; + bool bPrintControls; +public: + explicit SwDrawViewSave( SdrView* pSdrView ); + ~SwDrawViewSave(); +}; + +} + +void SwPaintQueue::Add( SwViewShell *pNew, const SwRect &rNew ) +{ + SwQueuedPaint *pPt = s_pPaintQueue; + if (nullptr != pPt) + { + while ( pPt->pSh != pNew && pPt->pNext ) + pPt = pPt->pNext; + if ( pPt->pSh == pNew ) + { + pPt->aRect.Union( rNew ); + return; + } + } + SwQueuedPaint *pNQ = new SwQueuedPaint( pNew, rNew ); + if ( pPt ) + pPt->pNext = pNQ; + else + s_pPaintQueue = pNQ; +} + +void SwPaintQueue::Repaint() +{ + if (SwRootFrame::IsInPaint() || !s_pPaintQueue) + return; + + SwQueuedPaint *pPt = s_pPaintQueue; + do + { SwViewShell *pSh = pPt->pSh; + CurrShell aCurr( pSh ); + if ( pSh->IsPreview() ) + { + if ( pSh->GetWin() ) + { + // for previewing, since rows/columns are known in PaintHdl (UI) + pSh->GetWin()->Invalidate(); + } + } + else + pSh->Paint(*pSh->GetOut(), pPt->aRect.SVRect()); + pPt = pPt->pNext; + } while ( pPt ); + + do + { + pPt = s_pPaintQueue; + s_pPaintQueue = s_pPaintQueue->pNext; + delete pPt; + } while (s_pPaintQueue); +} + +void SwPaintQueue::Remove( SwViewShell const *pSh ) +{ + SwQueuedPaint *pPt = s_pPaintQueue; + if (nullptr == pPt) + return; + + SwQueuedPaint *pPrev = nullptr; + while ( pPt && pPt->pSh != pSh ) + { + pPrev = pPt; + pPt = pPt->pNext; + } + if ( pPt ) + { + if ( pPrev ) + pPrev->pNext = pPt->pNext; + else if (pPt == s_pPaintQueue) + s_pPaintQueue = nullptr; + delete pPt; + } +} + +void SetSwVisArea( SwViewShell *pSh, const SwRect &rRect ) +{ + OSL_ENSURE( !pSh->GetWin(), "Print with window?" ); + pSh->maVisArea = rRect; + pSh->Imp()->SetFirstVisPageInvalid(); + Point aPt( rRect.Pos() ); + + // calculate an offset for the rectangle of the n-th page to + // move the start point of the output operation to a position + // such that in the output device all pages will be painted + // at the same position + aPt.setX( -aPt.X() ); aPt.setY( -aPt.Y() ); + + vcl::RenderContext *pOut = pSh->GetOut(); + + MapMode aMapMode( pOut->GetMapMode() ); + aMapMode.SetOrigin( aPt ); + pOut->SetMapMode( aMapMode ); +} + +void SwViewShell::InitPrt( OutputDevice *pOutDev ) +{ + // For printing we use a negative offset (exactly the offset of OutputSize). + // This is necessary because the origin is in the upper left corner of the + // physical page while the output uses OutputOffset as origin. + if ( pOutDev ) + { + maPrtOffset = Point(); + + maPrtOffset += pOutDev->GetMapMode().GetOrigin(); + MapMode aMapMode( pOutDev->GetMapMode() ); + aMapMode.SetMapUnit( MapUnit::MapTwip ); + pOutDev->SetMapMode( aMapMode ); + pOutDev->SetLineColor(); + pOutDev->SetFillColor(); + } + else + { + maPrtOffset.setX(0); + maPrtOffset.setY(0); + } + + if ( !mpWin ) + mpOut = pOutDev; +} + +void SwViewShell::ChgAllPageOrientation( Orientation eOri ) +{ + OSL_ENSURE( mnStartAction, "missing an Action" ); + CurrShell aCurr( this ); + + const size_t nAll = GetDoc()->GetPageDescCnt(); + bool bNewOri = eOri != Orientation::Portrait; + + for( size_t i = 0; i < nAll; ++ i ) + { + const SwPageDesc& rOld = GetDoc()->GetPageDesc( i ); + + if( rOld.GetLandscape() != bNewOri ) + { + SwPageDesc aNew( rOld ); + { + ::sw::UndoGuard const ug(GetDoc()->GetIDocumentUndoRedo()); + GetDoc()->CopyPageDesc(rOld, aNew); + } + aNew.SetLandscape( bNewOri ); + SwFrameFormat& rFormat = aNew.GetMaster(); + SwFormatFrameSize aSz( rFormat.GetFrameSize() ); + // adjust size + // PORTRAIT -> higher than wide + // LANDSCAPE -> wider than high + // Height is the VarSize, width the FixSize (per Def.) + if( bNewOri ? aSz.GetHeight() > aSz.GetWidth() + : aSz.GetHeight() < aSz.GetWidth() ) + { + SwTwips aTmp = aSz.GetHeight(); + aSz.SetHeight( aSz.GetWidth() ); + aSz.SetWidth( aTmp ); + rFormat.SetFormatAttr( aSz ); + } + GetDoc()->ChgPageDesc( i, aNew ); + } + } +} + +void SwViewShell::ChgAllPageSize( Size const &rSz ) +{ + OSL_ENSURE( mnStartAction, "missing an Action" ); + CurrShell aCurr( this ); + + SwDoc* pMyDoc = GetDoc(); + const size_t nAll = pMyDoc->GetPageDescCnt(); + + for( size_t i = 0; i < nAll; ++i ) + { + const SwPageDesc &rOld = pMyDoc->GetPageDesc( i ); + SwPageDesc aNew( rOld ); + { + ::sw::UndoGuard const ug(GetDoc()->GetIDocumentUndoRedo()); + GetDoc()->CopyPageDesc( rOld, aNew ); + } + SwFrameFormat& rPgFormat = aNew.GetMaster(); + Size aSz( rSz ); + const bool bOri = aNew.GetLandscape(); + if( bOri ? aSz.Height() > aSz.Width() + : aSz.Height() < aSz.Width() ) + { + SwTwips aTmp = aSz.Height(); + aSz.setHeight( aSz.Width() ); + aSz.setWidth( aTmp ); + } + + SwFormatFrameSize aFrameSz( rPgFormat.GetFrameSize() ); + aFrameSz.SetSize( aSz ); + rPgFormat.SetFormatAttr( aFrameSz ); + pMyDoc->ChgPageDesc( i, aNew ); + } +} + +void SwViewShell::CalcPagesForPrint( sal_uInt16 nMax ) +{ + CurrShell aCurr( this ); + + SwRootFrame* pMyLayout = GetLayout(); + + const SwFrame *pPage = pMyLayout->Lower(); + SwLayAction aAction( pMyLayout, Imp() ); + + pMyLayout->StartAllAction(); + for ( sal_uInt16 i = 1; pPage && i <= nMax; pPage = pPage->GetNext(), ++i ) + { + pPage->Calc(GetOut()); + SwRect aOldVis( VisArea() ); + maVisArea = pPage->getFrameArea(); + Imp()->SetFirstVisPageInvalid(); + aAction.Reset(); + aAction.SetPaint( false ); + aAction.SetWaitAllowed( false ); + aAction.SetReschedule( true ); + + aAction.Action(GetOut()); + + maVisArea = aOldVis; //reset due to the paints + Imp()->SetFirstVisPageInvalid(); + } + + pMyLayout->EndAllAction(); +} + +void SwViewShell::FillPrtDoc( SwDoc& rPrtDoc, const SfxPrinter* pPrt) +{ + assert( dynamic_cast<const SwFEShell*>( this) && "SwViewShell::Prt for FEShell only"); + SwFEShell* pFESh = static_cast<SwFEShell*>(this); + rPrtDoc.getIDocumentFieldsAccess().LockExpFields(); + + // use given printer + //! Make a copy of it since it gets destroyed with the temporary document + //! used for PDF export + if (pPrt) + rPrtDoc.getIDocumentDeviceAccess().setPrinter( VclPtr<SfxPrinter>::Create(*pPrt), true, true ); + + const SfxItemPool& rPool = GetAttrPool(); + for( sal_uInt16 nWh = POOLATTR_BEGIN; nWh < POOLATTR_END; ++nWh ) + { + const SfxPoolItem* pCpyItem = rPool.GetPoolDefaultItem( nWh ); + if( nullptr != pCpyItem ) + rPrtDoc.GetAttrPool().SetPoolDefaultItem( *pCpyItem ); + } + + // JP 29.07.99 - Bug 67951 - set all Styles from the SourceDoc into + // the PrintDoc - will be replaced! + rPrtDoc.ReplaceStyles( *GetDoc() ); + + SwShellCursor *pActCursor = pFESh->GetCursor_(); + SwShellCursor *pFirstCursor = pActCursor->GetNext(); + if( !pActCursor->HasMark() ) // with a multi-selection the current cursor might be empty + { + pActCursor = pActCursor->GetPrev(); + } + + // Y-position of the first selection + Point aSelPoint; + if( pFESh->IsTableMode() ) + { + SwShellTableCursor* pShellTableCursor = pFESh->GetTableCursor(); + + const SwContentNode *const pContentNode = + pShellTableCursor->Start()->GetNode().GetContentNode(); + const SwContentFrame *const pContentFrame = pContentNode ? pContentNode->getLayoutFrame(GetLayout(), pShellTableCursor->Start()) : nullptr; + if( pContentFrame ) + { + SwRect aCharRect; + SwCursorMoveState aTmpState( CursorMoveState::NONE ); + pContentFrame->GetCharRect( aCharRect, *pShellTableCursor->Start(), &aTmpState ); + aSelPoint = Point( aCharRect.Left(), aCharRect.Top() ); + } + } + else if (pFirstCursor) + { + aSelPoint = pFirstCursor->GetSttPos(); + } + + const SwPageFrame* pPage = GetLayout()->GetPageAtPos( aSelPoint ); + OSL_ENSURE( pPage, "no page found!" ); + + // get page descriptor - fall back to the first one if pPage could not be found + const SwPageDesc* pPageDesc = pPage ? rPrtDoc.FindPageDesc( + pPage->GetPageDesc()->GetName() ) : &rPrtDoc.GetPageDesc( 0 ); + + if( !pFESh->IsTableMode() && pActCursor && pActCursor->HasMark() ) + { // Tweak paragraph attributes of last paragraph + SwNodeIndex aNodeIdx( *rPrtDoc.GetNodes().GetEndOfContent().StartOfSectionNode() ); + SwTextNode* pTextNd = rPrtDoc.GetNodes().GoNext( &aNodeIdx )->GetTextNode(); + SwContentNode *pLastNd = + (*pActCursor->GetMark()) <= (*pActCursor->GetPoint()) + ? pActCursor->GetPointContentNode() + : pActCursor->GetMarkContentNode(); + // copy the paragraph attributes of the first paragraph + if( pLastNd && pLastNd->IsTextNode() ) + static_cast<SwTextNode*>(pLastNd)->CopyCollFormat( *pTextNd ); + } + + // fill it with the selected content + pFESh->Copy(rPrtDoc); + + // set the page style at the first paragraph + { + SwNodeIndex aNodeIdx( *rPrtDoc.GetNodes().GetEndOfContent().StartOfSectionNode() ); + SwContentNode* pCNd = rPrtDoc.GetNodes().GoNext( &aNodeIdx ); // go to 1st ContentNode + if( pFESh->IsTableMode() ) + { + SwTableNode* pTNd = pCNd->FindTableNode(); + if( pTNd ) + pTNd->GetTable().GetFrameFormat()->SetFormatAttr( SwFormatPageDesc( pPageDesc ) ); + } + else + { + pCNd->SetAttr( SwFormatPageDesc( pPageDesc ) ); + if( pFirstCursor && pFirstCursor->HasMark() ) + { + SwTextNode *pTextNd = pCNd->GetTextNode(); + if( pTextNd ) + { + SwContentNode *pFirstNd = + (*pFirstCursor->GetMark()) > (*pFirstCursor->GetPoint()) + ? pFirstCursor->GetPointContentNode() + : pFirstCursor->GetMarkContentNode(); + // copy paragraph attributes of the first paragraph + if( pFirstNd && pFirstNd->IsTextNode() ) + static_cast<SwTextNode*>(pFirstNd)->CopyCollFormat( *pTextNd ); + } + } + } + } +} + +// TODO: there is already a GetPageByPageNum, but it checks some physical page +// number; unsure if we want that here, should find out what that is... +SwPageFrame const* +sw_getPage(SwRootFrame const& rLayout, sal_Int32 const nPage) +{ + // yes this is O(n^2) but at least it does not crash... + SwPageFrame const* pPage = dynamic_cast<const SwPageFrame*>(rLayout.Lower()); + for (sal_Int32 i = nPage; pPage && (i > 0); --i) + { + if (1 == i) { // note: nPage is 1-based, i.e. 0 is invalid! + return pPage; + } + pPage = dynamic_cast<SwPageFrame const*>(pPage->GetNext()); + } + OSL_ENSURE(pPage, "ERROR: SwPageFrame expected"); + OSL_FAIL("non-existent page requested"); + return nullptr; +} + +namespace sw +{ + // tdf#91680 Reserve space in margin for comments only if there are comments + bool IsShrinkPageForPostIts(SwViewShell const& rShell, SwPrintData const& rPrintData) + { + SwPostItMode const nPostItMode(rPrintData.GetPrintPostIts()); + return nPostItMode == SwPostItMode::InMargins + && sw_GetPostIts(rShell.GetDoc()->getIDocumentFieldsAccess(), nullptr); + } +} + +bool SwViewShell::PrintOrPDFExport( + OutputDevice *pOutDev, + SwPrintData const& rPrintData, + sal_Int32 nRenderer, /* the index in the vector of pages to be printed */ + bool bIsPDFExport ) +{ + // CAUTION: Do also always update the printing routines in viewpg.cxx (PrintProspect)! + + const sal_Int32 nMaxRenderer = rPrintData.GetRenderData().GetPagesToPrint().size() - 1; + OSL_ENSURE( 0 <= nRenderer && nRenderer <= nMaxRenderer, "nRenderer out of bounds"); + if (!pOutDev || nMaxRenderer < 0 || nRenderer < 0 || nRenderer > nMaxRenderer) + return false; + + // save settings of OutputDevice (should be done always since the + // output device is now provided by a call from outside the Writer) + pOutDev->Push(); + + + const bool bHasPostItsToPrintInMargins(::sw::IsShrinkPageForPostIts(*this, rPrintData)); + ::std::optional<tools::Long> oOrigHeight; + + // Print/PDF export for (multi-)selection has already generated a + // temporary document with the selected text. + // (see XRenderable implementation in unotxdoc.cxx) + // It is implemented this way because PDF export calls this Prt function + // once per page and we do not like to always have the temporary document + // to be created that often here. + std::unique_ptr<SwViewShell> pShell(new SwViewShell(*this, nullptr, pOutDev)); + + SdrView *pDrawView = pShell->GetDrawView(); + if (pDrawView) + { + pDrawView->SetBufferedOutputAllowed( false ); + pDrawView->SetBufferedOverlayAllowed( false ); + } + + { // additional scope so that the CurrShell is reset before destroying the shell + + CurrShell aCurr( pShell.get() ); + + //JP 01.02.99: Bug 61335 - the ReadOnly flag is never copied + if( mpOpt->IsReadonly() ) + pShell->mpOpt->SetReadonly( true ); + + // save options at draw view: + SwDrawViewSave aDrawViewSave( pShell->GetDrawView() ); + pShell->PrepareForPrint( rPrintData, bIsPDFExport ); + + const sal_Int32 nPage = rPrintData.GetRenderData().GetPagesToPrint()[ nRenderer ]; + OSL_ENSURE( nPage < 0 || + rPrintData.GetRenderData().GetValidPagesSet().count( nPage ) == 1, + "SwViewShell::PrintOrPDFExport: nPage not valid" ); + SwViewShell *const pViewSh2 = (nPage < 0) + ? rPrintData.GetRenderData().m_pPostItShell.get()// post-it page + : pShell.get(); // a 'regular' page, not one from the post-it doc + + SwPageFrame const*const pStPage = + sw_getPage(*pViewSh2->GetLayout(), abs(nPage)); + OSL_ENSURE( pStPage, "failed to get start page" ); + if (!pStPage) + { + return false; + } + + //!! applying view options and formatting the document should now only be done in getRendererCount! + + ::SetSwVisArea( pViewSh2, pStPage->getFrameArea() ); + + pShell->InitPrt(pOutDev); + + ::SetSwVisArea( pViewSh2, pStPage->getFrameArea() ); + + pStPage->GetUpper()->PaintSwFrame( *pOutDev, pStPage->getFrameArea(), &rPrintData ); + + SwPaintQueue::Repaint(); + + SwPostItMgr *pPostItManager = bHasPostItsToPrintInMargins ? pShell->GetPostItMgr() : nullptr; + + if (pPostItManager) + { + pPostItManager->CalcRects(); + pPostItManager->LayoutPostIts(); + pPostItManager->DrawNotesForPage(pOutDev, nPage-1); + oOrigHeight.emplace(pStPage->getFrameArea().Height()); + } + } + + pShell.reset(); + + // restore settings of OutputDevice (should be done always now since the + // output device is now provided by a call from outside the Writer) + pOutDev->Pop(); + + // avoid a warning about unbalanced Push/Pop by doing this last: + if (oOrigHeight) + { + // fdo#36815 Now scale the recorded page down so the comments in + // margins will fit in the final page + double fScale = 0.75; + tools::Long nNewHeight = *oOrigHeight*fScale; + tools::Long nShiftY = (*oOrigHeight-nNewHeight)/2; + GDIMetaFile *const pMetaFile = pOutDev->GetConnectMetaFile(); + pMetaFile->ScaleActions(fScale, fScale); + //Move the scaled page down to center it + //the other variant of Move does not map pixels + //back to the logical units correctly + pMetaFile->Move(0, convertTwipToMm100(nShiftY), pOutDev->GetDPIX(), pOutDev->GetDPIY()); + } + + return true; +} + +void SwViewShell::PrtOle2( SwDoc *pDoc, const SwViewOption *pOpt, const SwPrintData& rOptions, + vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, + bool bOutputForScreen ) +{ + // For printing a shell is needed. Either the Doc already has one, then we + // create a new view, or it has none, then we create the first view. + std::unique_ptr<SwViewShell> pSh; + if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) + pSh.reset(new SwViewShell( *pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(), nullptr, &rRenderContext,VSHELLFLAG_SHARELAYOUT )); + else + pSh.reset(new SwViewShell( *pDoc, nullptr, pOpt, &rRenderContext)); + + pSh->setOutputToWindow(bOutputForScreen); + + { + CurrShell aCurr( pSh.get() ); + pSh->PrepareForPrint( rOptions ); + pSh->SetPrtFormatOption( true ); + + SwRect aSwRect( rRect ); + pSh->maVisArea = aSwRect; + + if ( pSh->GetViewOptions()->getBrowseMode() && + pSh->GetRingContainer().size() == 1 ) + { + pSh->InvalidateLayout( false ); + pSh->GetLayout()->Lower()->InvalidateSize(); + } + + // CalcPagesForPrint() should not be necessary here. The pages in the + // visible area will be formatted in SwRootFrame::PaintSwFrame(). + // Removing this gives us a performance gain during saving the + // document because the thumbnail creation will not trigger a complete + // formatting of the document. + + rRenderContext.Push( vcl::PushFlags::CLIPREGION ); + rRenderContext.IntersectClipRegion( aSwRect.SVRect() ); + pSh->GetLayout()->PaintSwFrame( rRenderContext, aSwRect ); + + rRenderContext.Pop(); + // first the CurrShell object needs to be destroyed! + } +} + +/// Check if the DocNodesArray contains fields. +bool SwViewShell::IsAnyFieldInDoc() const +{ + for (const SfxPoolItem* pItem : mxDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_FIELD)) + { + auto pFormatField = dynamic_cast<const SwFormatField*>(pItem); + if(pFormatField) + { + const SwTextField* pTextField = pFormatField->GetTextField(); + if( pTextField && pTextField->GetTextNode().GetNodes().IsDocNodes() ) + { + return true; + } + } + } + + for (const SfxPoolItem* pItem : mxDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INPUTFIELD)) + { + const SwFormatField* pFormatField = dynamic_cast<const SwFormatField*>(pItem); + if(pFormatField) + { + const SwTextField* pTextField = pFormatField->GetTextField(); + if( pTextField && pTextField->GetTextNode().GetNodes().IsDocNodes() ) + { + return true; + } + } + } + + return false; +} + +/// Saves some settings at the draw view +SwDrawViewSave::SwDrawViewSave( SdrView* pSdrView ) + : pDV( pSdrView ) + , bPrintControls(true) +{ + if ( pDV ) + { + bPrintControls = pDV->IsLayerPrintable( "Controls" ); + } +} + +SwDrawViewSave::~SwDrawViewSave() +{ + if ( pDV ) + { + pDV->SetLayerPrintable( "Controls", bPrintControls ); + } +} + +// OD 09.01.2003 #i6467# - method also called for page preview +void SwViewShell::PrepareForPrint( const SwPrintData &rOptions, bool bIsPDFExport ) + { + mpOpt->SetGraphic ( rOptions.m_bPrintGraphic ); + mpOpt->SetTable ( rOptions.m_bPrintTable ); + mpOpt->SetDraw ( rOptions.m_bPrintDraw ); + mpOpt->SetControl ( rOptions.m_bPrintControl ); + mpOpt->SetPageBack ( rOptions.m_bPrintPageBackground ); + // Font should not be black if it's a PDF Export + mpOpt->SetBlackFont( rOptions.m_bPrintBlackFont && !bIsPDFExport ); + + if ( !HasDrawView() ) + return; + + SdrView *pDrawView = GetDrawView(); + // OD 09.01.2003 #i6467# - consider, if view shell belongs to page preview + if ( !IsPreview() ) + { + pDrawView->SetLayerPrintable( "Controls", rOptions.m_bPrintControl ); + } + else + { + pDrawView->SetLayerVisible( "Controls", rOptions.m_bPrintControl ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |