diff options
Diffstat (limited to 'sw/source/core/doc/DocumentLayoutManager.cxx')
-rw-r--r-- | sw/source/core/doc/DocumentLayoutManager.cxx | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx new file mode 100644 index 000000000..87c75b3e2 --- /dev/null +++ b/sw/source/core/doc/DocumentLayoutManager.cxx @@ -0,0 +1,500 @@ +/* -*- 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 <DocumentLayoutManager.hxx> +#include <doc.hxx> +#include <IDocumentState.hxx> +#include <IDocumentUndoRedo.hxx> +#include <DocumentContentOperationsManager.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <undobj.hxx> +#include <viewsh.hxx> +#include <layouter.hxx> +#include <poolfmt.hxx> +#include <frmfmt.hxx> +#include <fmtcntnt.hxx> +#include <fmtcnct.hxx> +#include <ndole.hxx> +#include <fmtanchr.hxx> +#include <txtflcnt.hxx> +#include <fmtflcnt.hxx> +#include <ndtxt.hxx> +#include <unoframe.hxx> +#include <textboxhelper.hxx> +#include <ndindex.hxx> +#include <pam.hxx> +#include <frameformats.hxx> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <svx/svdobj.hxx> +#include <svx/svdpage.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star; + +namespace sw +{ + +DocumentLayoutManager::DocumentLayoutManager( SwDoc& i_rSwdoc ) : + m_rDoc( i_rSwdoc ), + mpCurrentView( nullptr ) +{ +} + +const SwViewShell *DocumentLayoutManager::GetCurrentViewShell() const +{ + return mpCurrentView; +} + +SwViewShell *DocumentLayoutManager::GetCurrentViewShell() +{ + return mpCurrentView; +} + +void DocumentLayoutManager::SetCurrentViewShell( SwViewShell* pNew ) +{ + mpCurrentView = pNew; +} + +// It must be able to communicate to a SwViewShell. This is going to be removed later. +const SwRootFrame *DocumentLayoutManager::GetCurrentLayout() const +{ + if(GetCurrentViewShell()) + return GetCurrentViewShell()->GetLayout(); + return nullptr; +} + +SwRootFrame *DocumentLayoutManager::GetCurrentLayout() +{ + if(GetCurrentViewShell()) + return GetCurrentViewShell()->GetLayout(); + return nullptr; +} + +bool DocumentLayoutManager::HasLayout() const +{ + // if there is a view, there is always a layout + return (mpCurrentView != nullptr); +} + +SwLayouter* DocumentLayoutManager::GetLayouter() +{ + return mpLayouter.get(); +} + +const SwLayouter* DocumentLayoutManager::GetLayouter() const +{ + return mpLayouter.get(); +} + +void DocumentLayoutManager::SetLayouter( SwLayouter* pNew ) +{ + mpLayouter.reset( pNew ); +} + +/** Create a new format whose settings fit to the Request by default. + + The format is put into the respective format array. + If there already is a fitting format, it is returned instead. */ +SwFrameFormat *DocumentLayoutManager::MakeLayoutFormat( RndStdIds eRequest, const SfxItemSet* pSet ) +{ + SwFrameFormat *pFormat = nullptr; + const bool bMod = m_rDoc.getIDocumentState().IsModified(); + bool bHeader = false; + + switch ( eRequest ) + { + case RndStdIds::HEADER: + case RndStdIds::HEADERL: + case RndStdIds::HEADERR: + { + bHeader = true; + [[fallthrough]]; + } + case RndStdIds::FOOTER: + { + pFormat = new SwFrameFormat( m_rDoc.GetAttrPool(), + (bHeader ? "Right header" : "Right footer"), + m_rDoc.GetDfltFrameFormat() ); + + SwNodeIndex aTmpIdx( m_rDoc.GetNodes().GetEndOfAutotext() ); + SwStartNode* pSttNd = + m_rDoc.GetNodes().MakeTextSection + ( aTmpIdx, + bHeader ? SwHeaderStartNode : SwFooterStartNode, + m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>( bHeader + ? ( eRequest == RndStdIds::HEADERL + ? RES_POOLCOLL_HEADERL + : eRequest == RndStdIds::HEADERR + ? RES_POOLCOLL_HEADERR + : RES_POOLCOLL_HEADER ) + : RES_POOLCOLL_FOOTER + ) ) ); + pFormat->SetFormatAttr( SwFormatContent( pSttNd )); + + if( pSet ) // Set a few more attributes + pFormat->SetFormatAttr( *pSet ); + + // Why set it back? Doc has changed, or not? + // In any case, wrong for the FlyFrames! + if ( !bMod ) + m_rDoc.getIDocumentState().ResetModified(); + } + break; + + case RndStdIds::DRAW_OBJECT: + { + pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() ); + if( pSet ) // Set a few more attributes + pFormat->SetFormatAttr( *pSet ); + + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( + std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0)); + } + } + break; + +#if OSL_DEBUG_LEVEL > 0 + case RndStdIds::FLY_AT_PAGE: + case RndStdIds::FLY_AT_CHAR: + case RndStdIds::FLY_AT_FLY: + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AS_CHAR: + OSL_FAIL( "use new interface instead: SwDoc::MakeFlySection!" ); + break; +#endif + + default: + OSL_ENSURE( false, + "LayoutFormat was requested with an invalid Request." ); + + } + return pFormat; +} + +/// Deletes the denoted format and its content. +void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat *pFormat ) +{ + // A chain of frames needs to be merged, if necessary, + // so that the Frame's contents are adjusted accordingly before we destroy the Frames. + const SwFormatChain &rChain = pFormat->GetChain(); + if ( rChain.GetPrev() ) + { + SwFormatChain aChain( rChain.GetPrev()->GetChain() ); + aChain.SetNext( rChain.GetNext() ); + m_rDoc.SetAttr( aChain, *rChain.GetPrev() ); + } + if ( rChain.GetNext() ) + { + SwFormatChain aChain( rChain.GetNext()->GetChain() ); + aChain.SetPrev( rChain.GetPrev() ); + m_rDoc.SetAttr( aChain, *rChain.GetNext() ); + } + + const SwNodeIndex* pCntIdx = nullptr; + // The draw format doesn't own its content, it just has a pointer to it. + if (pFormat->Which() != RES_DRAWFRMFMT) + pCntIdx = pFormat->GetContent().GetContentIdx(); + if (pCntIdx && !m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + // Disconnect if it's an OLE object + SwOLENode* pOLENd = m_rDoc.GetNodes()[ pCntIdx->GetIndex()+1 ]->GetOLENode(); + if( pOLENd && pOLENd->GetOLEObj().IsOleRef() ) + { + try + { + pOLENd->GetOLEObj().GetOleRef()->changeState( embed::EmbedStates::LOADED ); + } + catch ( uno::Exception& ) + { + } + } + } + + // Destroy Frames + pFormat->DelFrames(); + + // Only FlyFrames are undoable at first + const sal_uInt16 nWh = pFormat->Which(); + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() && + (RES_FLYFRMFMT == nWh || RES_DRAWFRMFMT == nWh)) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelLayFormat>( pFormat )); + } + else + { + // #i32089# - delete at-frame anchored objects + if ( nWh == RES_FLYFRMFMT ) + { + // determine frame formats of at-frame anchored objects + const SwNodeIndex* pContentIdx = nullptr; + if (pFormat->Which() != RES_DRAWFRMFMT) + pContentIdx = pFormat->GetContent().GetContentIdx(); + if (pContentIdx) + { + const SwFrameFormats* pTable = pFormat->GetDoc()->GetSpzFrameFormats(); + if ( pTable ) + { + std::vector<SwFrameFormat*> aToDeleteFrameFormats; + const SwNodeOffset nNodeIdxOfFlyFormat( pContentIdx->GetIndex() ); + + for ( size_t i = 0; i < pTable->size(); ++i ) + { + SwFrameFormat* pTmpFormat = (*pTable)[i]; + const SwFormatAnchor &rAnch = pTmpFormat->GetAnchor(); + if ( rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY && + rAnch.GetContentAnchor()->nNode.GetIndex() == nNodeIdxOfFlyFormat ) + { + aToDeleteFrameFormats.push_back( pTmpFormat ); + } + } + + // delete found frame formats + while ( !aToDeleteFrameFormats.empty() ) + { + SwFrameFormat* pTmpFormat = aToDeleteFrameFormats.back(); + pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pTmpFormat ); + + aToDeleteFrameFormats.pop_back(); + } + } + } + } + + // Delete content + if( pCntIdx ) + { + SwNode *pNode = &pCntIdx->GetNode(); + const_cast<SwFormatContent&>(pFormat->GetFormatAttr( RES_CNTNT )).SetNewContentIdx( nullptr ); + m_rDoc.getIDocumentContentOperations().DeleteSection( pNode ); + } + + // Delete the character for FlyFrames anchored as char (if necessary) + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && rAnchor.GetContentAnchor()) + { + const SwPosition* pPos = rAnchor.GetContentAnchor(); + SwTextNode *pTextNd = pPos->nNode.GetNode().GetTextNode(); + + // attribute is still in text node, delete it + if ( pTextNd ) + { + SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>( + pTextNd->GetTextAttrForCharAt( pPos->nContent.GetIndex(), + RES_TXTATR_FLYCNT )); + if ( pAttr && (pAttr->GetFlyCnt().GetFrameFormat() == pFormat) ) + { + // don't delete, set pointer to 0 + const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat(); + SwIndex aIdx( pPos->nContent ); + pTextNd->EraseText( aIdx, 1 ); + } + } + } + + m_rDoc.DelFrameFormat( pFormat ); + } + m_rDoc.getIDocumentState().SetModified(); +} + +/** Copies the stated format (pSrc) to pDest and returns pDest. + + If there's no pDest, it is created. + If the source format is located in another document, also copy correctly + in this case. + The Anchor attribute's position is always set to 0! */ +SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat( + const SwFrameFormat& rSource, + const SwFormatAnchor& rNewAnchor, + bool bSetTextFlyAtt, + bool bMakeFrames ) +{ + const bool bFly = RES_FLYFRMFMT == rSource.Which(); + const bool bDraw = RES_DRAWFRMFMT == rSource.Which(); + OSL_ENSURE( bFly || bDraw, "this method only works for fly or draw" ); + + SwDoc* pSrcDoc = const_cast<SwDoc*>(rSource.GetDoc()); + + // May we copy this object? + // We may, unless it's 1) it's a control (and therefore a draw) + // 2) anchored in a header/footer + // 3) anchored (to paragraph?) + bool bMayNotCopy = false; + const auto pCAnchor = rNewAnchor.GetContentAnchor(); + bool bInHeaderFooter = pCAnchor && m_rDoc.IsInHeaderFooter(pCAnchor->nNode); + if(bDraw) + { + bool bCheckControlLayer = false; + rSource.CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer)); + bMayNotCopy = + bCheckControlLayer && + ((RndStdIds::FLY_AT_PARA == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_FLY == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_CHAR == rNewAnchor.GetAnchorId())) && + bInHeaderFooter; + } + + // just return if we can't copy this + if( bMayNotCopy ) + return nullptr; + + SwFrameFormat* pDest = m_rDoc.GetDfltFrameFormat(); + if( rSource.GetRegisteredIn() != pSrcDoc->GetDfltFrameFormat() ) + pDest = m_rDoc.CopyFrameFormat( *static_cast<const SwFrameFormat*>(rSource.GetRegisteredIn()) ); + if( bFly ) + { + // #i11176# + // To do a correct cloning concerning the ZOrder for all objects + // it is necessary to actually create a draw object for fly frames, too. + // These are then added to the DrawingLayer (which needs to exist). + // Together with correct sorting of all drawinglayer based objects + // before cloning ZOrder transfer works correctly then. + SwFlyFrameFormat *pFormat = m_rDoc.MakeFlyFrameFormat( rSource.GetName(), pDest ); + pDest = pFormat; + + SwXFrame::GetOrCreateSdrObject(*pFormat); + } + else + pDest = m_rDoc.MakeDrawFrameFormat( OUString(), pDest ); + + // Copy all other or new attributes + pDest->CopyAttrs( rSource ); + + // Do not copy chains + pDest->ResetFormatAttr( RES_CHAIN ); + + if( bFly ) + { + // Duplicate the content. + const SwNode& rCSttNd = rSource.GetContent().GetContentIdx()->GetNode(); + SwNodeRange aRg( rCSttNd, SwNodeOffset(1), *rCSttNd.EndOfSectionNode() ); + + SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfAutotext() ); + SwStartNode* pSttNd = SwNodes::MakeEmptySection( aIdx, SwFlyStartNode ); + + // Set the Anchor/ContentIndex first. + // Within the copying part, we can access the values (DrawFormat in Headers and Footers) + aIdx = *pSttNd; + SwFormatContent aAttr( rSource.GetContent() ); + aAttr.SetNewContentIdx( &aIdx ); + pDest->SetFormatAttr( aAttr ); + pDest->SetFormatAttr( rNewAnchor ); + + if( !m_rDoc.IsCopyIsMove() || &m_rDoc != pSrcDoc ) + { + if( (m_rDoc.IsInReading() && !bInHeaderFooter) || m_rDoc.IsInMailMerge() ) + pDest->SetName( OUString() ); + else + { + // Test first if the name is already taken, if so generate a new one. + SwNodeType nNdTyp = aRg.aStart.GetNode().GetNodeType(); + + OUString sOld( pDest->GetName() ); + pDest->SetName( OUString() ); + if( m_rDoc.FindFlyByName( sOld, nNdTyp ) ) // found one + switch( nNdTyp ) + { + case SwNodeType::Grf: sOld = m_rDoc.GetUniqueGrfName(sOld); break; + case SwNodeType::Ole: sOld = m_rDoc.GetUniqueOLEName(); break; + default: sOld = m_rDoc.GetUniqueFrameName(); break; + } + + pDest->SetName( sOld ); + } + } + + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0)); + } + + // Make sure that FlyFrames in FlyFrames are copied + aIdx = *pSttNd->EndOfSectionNode(); + + //fdo#36631 disable (scoped) any undo operations associated with the + //contact object itself. They should be managed by SwUndoInsLayFormat. + const ::sw::DrawUndoGuard drawUndoGuard(m_rDoc.GetIDocumentUndoRedo()); + + pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aIdx, nullptr, false, true, true); + } + else + { + OSL_ENSURE( RES_DRAWFRMFMT == rSource.Which(), "Neither Fly nor Draw." ); + // #i52780# - Note: moving object to visible layer not needed. + rSource.CallSwClientNotify(sw::DrawFormatLayoutCopyHint(static_cast<SwDrawFrameFormat&>(*pDest), m_rDoc)); + + if(pDest->GetAnchor() == rNewAnchor) + { + // Do *not* connect to layout, if a <MakeFrames> will not be called. + if(bMakeFrames) + pDest->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES)); + + } + else + pDest->SetFormatAttr( rNewAnchor ); + + if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) + { + m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0)); + } + } + + if (bSetTextFlyAtt && (RndStdIds::FLY_AS_CHAR == rNewAnchor.GetAnchorId())) + { + const SwPosition* pPos = rNewAnchor.GetContentAnchor(); + SwFormatFlyCnt aFormat( pDest ); + pPos->nNode.GetNode().GetTextNode()->InsertItem( + aFormat, pPos->nContent.GetIndex(), 0 ); + } + + if( bMakeFrames ) + pDest->MakeFrames(); + + if (pDest->GetName().isEmpty()) + { + // Format name should have unique name. Let's use object name as a fallback + SdrObject *pObj = pDest->FindSdrObject(); + if (pObj) + pDest->SetName(pObj->GetName()); + } + + // If the draw format has a TextBox, then copy its fly format as well. + if (const auto& pTextBoxes = rSource.GetOtherTextBoxFormats()) + pTextBoxes->Clone(&m_rDoc, rNewAnchor, pDest, bSetTextFlyAtt, bMakeFrames); + + return pDest; +} + +//Load document from fdo#42534 under valgrind, drag the scrollbar down so full +//document layout is triggered. Close document before layout has completed, and +//SwAnchoredObject objects deleted by the deletion of layout remain referenced +//by the SwLayouter +void DocumentLayoutManager::ClearSwLayouterEntries() +{ + SwLayouter::ClearMovedFwdFrames( m_rDoc ); + SwLayouter::ClearObjsTmpConsiderWrapInfluence( m_rDoc ); + // #i65250# + SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc ); +} + +DocumentLayoutManager::~DocumentLayoutManager() +{ +} + +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + |