diff options
Diffstat (limited to 'sw/source/core/docnode/node2lay.cxx')
-rw-r--r-- | sw/source/core/docnode/node2lay.cxx | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/sw/source/core/docnode/node2lay.cxx b/sw/source/core/docnode/node2lay.cxx new file mode 100644 index 000000000..5cc15c310 --- /dev/null +++ b/sw/source/core/docnode/node2lay.cxx @@ -0,0 +1,517 @@ +/* -*- 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 <calbck.hxx> +#include <node.hxx> +#include <ndindex.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <ftnfrm.hxx> +#include <sectfrm.hxx> +#include <cntfrm.hxx> +#include <tabfrm.hxx> +#include <frmtool.hxx> +#include <section.hxx> +#include <node2lay.hxx> +#include <osl/diagnose.h> + +/** + * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is + * just the public interface. + */ +class SwNode2LayImpl +{ + std::unique_ptr<SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>> mpIter; + sw::BroadcastingModify* mpMod; + std::vector<SwFrame*> mvUpperFrames; // To collect the Upper + SwNodeOffset mnIndex; // The Index of the to-be-inserted Nodes + bool mbMaster : 1; // true => only Master, false => only Frames without Follow + bool mbInit : 1; // Did we already call First() at SwClient? + + SwNode2LayImpl(const SwNode2LayImpl&) = delete; + SwNode2LayImpl& operator=(const SwNode2LayImpl&) = delete; + +public: + SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch ); + SwFrame* NextFrame(); // Returns the next "useful" Frame + SwLayoutFrame* UpperFrame( SwFrame* &rpFrame, const SwNode &rNode ); + void SaveUpperFrames(); // Saves (and locks if needed) the pUpper + // Inserts a Frame under every pUpper of the array + void RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd ); + + SwFrame* GetFrame( const Point* pDocPos ) const; +}; + +static SwNode* GoNextWithFrame(const SwNodes& rNodes, SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame) +{ + if( pIdx->GetIndex() >= rNodes.Count() - 1 ) + return nullptr; + + SwNodeIndex aTmp(*pIdx, +1); + SwNode* pNd = nullptr; + while( aTmp < rNodes.Count()-1 ) + { + pNd = &aTmp.GetNode(); + SwFrame const* pFound(nullptr); + if ( pNd->IsContentNode() ) + // sw_redlinehide: assume that it's OK to find a node with the same + // frame as the caller's one + pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First(); + else if ( pNd->IsTableNode() ) + pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First() ; + else if( pNd->IsEndNode() && !pNd->StartOfSectionNode()->IsSectionNode() ) + { + pNd = nullptr; + break; + } + if (pFound != nullptr) + { + if (ppFrame) + { + *ppFrame = SwFlowFrame::CastFlowFrame(pFound); + assert(*ppFrame); + } + break; + } + ++aTmp; + } + + if( aTmp == rNodes.Count()-SwNodeOffset(1) ) + pNd = nullptr; + else if( pNd ) + (*pIdx) = aTmp; + return pNd; +} + +static SwNode* GoPreviousWithFrame(SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame) +{ + if( !pIdx->GetIndex() ) + return nullptr; + + SwNodeIndex aTmp( *pIdx, -1 ); + SwNode* pNd(nullptr); + while( aTmp.GetIndex() ) + { + pNd = &aTmp.GetNode(); + SwFrame const* pFound(nullptr); + if ( pNd->IsContentNode() ) + // sw_redlinehide: assume that it's OK to find a node with the same + // frame as the caller's one + pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First(); + else if ( pNd->IsTableNode() ) + pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First(); + else if( pNd->IsStartNode() && !pNd->IsSectionNode() ) + { + pNd = nullptr; + break; + } + if (pFound != nullptr) + { + if (ppFrame) + { + *ppFrame = SwFlowFrame::CastFlowFrame(pFound); + assert(*ppFrame); + } + break; + } + --aTmp; + } + + if( !aTmp.GetIndex() ) + pNd = nullptr; + else if( pNd ) + (*pIdx) = aTmp; + return pNd; +} + +namespace sw { + +SwFrame const* FindNeighbourFrameForNode(SwNode const& rNode) +{ + SwNodeIndex idx(rNode); + SwFlowFrame const* pFlow(nullptr); + if (SwNode *const pNode = GoPreviousWithFrame(&idx, &pFlow)) + { + if (::CheckNodesRange(rNode, idx, true)) + { + while (pFlow->HasFollow()) + { // try to get the one on the current page + pFlow = pFlow->GetFollow(); + } + return &pFlow->GetFrame(); + } + } + idx = rNode; + if (SwNode *const pNode = GoNextWithFrame(idx.GetNodes(), &idx, &pFlow)) + { + if (::CheckNodesRange(rNode, idx, true)) + { + while (pFlow->IsFollow()) + { // try to get the one on the current page + pFlow = pFlow->GetPrecede(); + } + return &pFlow->GetFrame(); + } + } + return nullptr; +} + +} + +/** + * The main purpose of this ctor is to find the right sw::BroadcastingModify to iterate over. + * + * @param bSearch true: find the next Content or TableNode which contains + * Frames (to collect the pUpper). + * Else we assume that rNode points already to such a + * Content or TableNode. + * We insert before or after it. + */ +SwNode2LayImpl::SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch ) + : mnIndex( nIdx ), mbInit( false ) +{ + const SwNode* pNd; + if( bSearch || rNode.IsSectionNode() ) + { + // Find the next Content/TableNode that contains a Frame, so that we can add + // ourselves before/after it + if( !bSearch && rNode.GetIndex() < mnIndex ) + { + SwNodeIndex aTmp( *rNode.EndOfSectionNode(), +1 ); + pNd = GoPreviousWithFrame(&aTmp, nullptr); + if( pNd && rNode.GetIndex() > pNd->GetIndex() ) + pNd = nullptr; // Do not go over the limits + mbMaster = false; + } + else + { + SwNodeIndex aTmp( rNode, -1 ); + pNd = GoNextWithFrame(rNode.GetNodes(), &aTmp, nullptr); + mbMaster = true; + if( !bSearch && pNd && rNode.EndOfSectionIndex() < pNd->GetIndex() ) + pNd = nullptr; // Do not go over the limits + } + } + else + { + pNd = &rNode; + mbMaster = mnIndex < rNode.GetIndex(); + } + if( pNd ) + { + if( pNd->IsContentNode() ) + mpMod = const_cast<sw::BroadcastingModify*>(static_cast<sw::BroadcastingModify const *>(pNd->GetContentNode())); + else + { + assert(pNd->IsTableNode()); + mpMod = pNd->GetTableNode()->GetTable().GetFrameFormat(); + } + mpIter.reset(new SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>(*mpMod)); + } + else + { + mpIter = nullptr; + mpMod = nullptr; + } +} + +/** + * Returns the next "useful" Frame. + * + * When calling this method for the first time, a First is triggered at the + * actual Iterator. The result is check for suitability: Follows are not + * accepted, a Master is accepted when collecting the pUpper and when + * inserting before it. + * When inserting after it, we find and return the last Follow starting + * from the Master. + * + * If the Frame is located in a SectionFrame, we check to see whether the + * SectionFrame is the suitable return value (instead of the Frame itself). + * This is the case if the to-be-inserted Node is outside of the Section. + */ +SwFrame* SwNode2LayImpl::NextFrame() +{ + SwFrame* pRet; + if( !mpIter ) + return nullptr; + if( !mbInit ) + { + pRet = mpIter->First(); + mbInit = true; + } + else + pRet = mpIter->Next(); + while( pRet ) + { + SwFlowFrame* pFlow = SwFlowFrame::CastFlowFrame( pRet ); + assert(pFlow); + // Follows are pretty volatile, thus we ignore them. + // Even if we insert after the Frame, we start from the Master + // and iterate through it until the last Follow + if( !pFlow->IsFollow() ) + { + if( !mbMaster ) + { + while( pFlow->HasFollow() ) + pFlow = pFlow->GetFollow(); + pRet = &(pFlow->GetFrame()); + } + if( pRet->IsInSct() ) + { + SwSectionFrame* pSct = pRet->FindSctFrame(); + // ATTENTION: If we are in a Footnote, from a Layout point of view + // it could be located in a Section with columns, although it + // should be outside of it when looking at the Nodes. + // Thus, when dealing with Footnotes, we need to check whether the + // SectionFrame is also located within the Footnote and not outside of it. + if( !pRet->IsInFootnote() || pSct->IsInFootnote() ) + { + assert(pSct && pSct->GetSection()); + SwSectionNode* pNd = pSct->GetSection()->GetFormat()->GetSectionNode(); + assert(pNd); + // If the result Frame is located within a Section Frame + // whose Section does not contain the Node, we return with + // the SectionFrame, else we return with the Content/TabFrame + if( mbMaster ) + { + if( pNd->GetIndex() >= mnIndex ) + pRet = pSct; + } + else if( pNd->EndOfSectionIndex() < mnIndex ) + pRet = pSct; + } + } + return pRet; + } + pRet = mpIter->Next(); + } + return nullptr; +} + +void SwNode2LayImpl::SaveUpperFrames() +{ + SwFrame* pFrame; + while( nullptr != (pFrame = NextFrame()) ) + { + SwFrame* pPrv = pFrame->GetPrev(); + pFrame = pFrame->GetUpper(); + if( pFrame ) + { + if( pFrame->IsFootnoteFrame() ) + static_cast<SwFootnoteFrame*>(pFrame)->ColLock(); + else if( pFrame->IsInSct() ) + pFrame->FindSctFrame()->ColLock(); + if( pPrv && pPrv->IsSctFrame() ) + static_cast<SwSectionFrame*>(pPrv)->LockJoin(); + mvUpperFrames.push_back( pPrv ); + mvUpperFrames.push_back( pFrame ); + } + } + mpIter.reset(); + mpMod = nullptr; +} + +SwLayoutFrame* SwNode2LayImpl::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode ) +{ + rpFrame = NextFrame(); + if( !rpFrame ) + return nullptr; + SwLayoutFrame* pUpper = rpFrame->GetUpper(); + if( rpFrame->IsSctFrame() ) + { + const SwNode* pNode = rNode.StartOfSectionNode(); + if( pNode->IsSectionNode() ) + { + SwFrame* pFrame = mbMaster ? rpFrame->FindPrev() : rpFrame->FindNext(); + if( pFrame && pFrame->IsSctFrame() ) + { + // pFrame could be a "dummy"-section + if( static_cast<SwSectionFrame*>(pFrame)->GetSection() && + (&static_cast<const SwSectionNode*>(pNode)->GetSection() == + static_cast<SwSectionFrame*>(pFrame)->GetSection()) ) + { + // #i22922# - consider columned sections + // 'Go down' the section frame as long as the layout frame + // is found, which would contain content. + while ( pFrame->IsLayoutFrame() && + static_cast<SwLayoutFrame*>(pFrame)->Lower() && + !static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsFlowFrame() && + static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsLayoutFrame() ) + { + pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower(); + } + assert(pFrame->IsLayoutFrame()); + rpFrame = mbMaster ? nullptr + : static_cast<SwLayoutFrame*>(pFrame)->Lower(); + assert((!rpFrame || rpFrame->IsFlowFrame()) && + "<SwNode2LayImpl::UpperFrame(..)> - expected sibling isn't a flow frame." ); + return static_cast<SwLayoutFrame*>(pFrame); + } + + pUpper = new SwSectionFrame(const_cast<SwSectionNode*>(static_cast<const SwSectionNode*>(pNode))->GetSection(), rpFrame); + pUpper->Paste( rpFrame->GetUpper(), + mbMaster ? rpFrame : rpFrame->GetNext() ); + static_cast<SwSectionFrame*>(pUpper)->Init(); + rpFrame = nullptr; + // 'Go down' the section frame as long as the layout frame + // is found, which would contain content. + while ( pUpper->Lower() && + !pUpper->Lower()->IsFlowFrame() && + pUpper->Lower()->IsLayoutFrame() ) + { + pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); + } + return pUpper; + } + } + } + if( !mbMaster ) + rpFrame = rpFrame->GetNext(); + return pUpper; +} + +void SwNode2LayImpl::RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd ) +{ + SwNode* pNd; + SwDoc& rDoc = rNds.GetDoc(); + bool bFirst = true; + for( ; nStt < nEnd; ++nStt ) + { + SwFrame* pNew = nullptr; + SwFrame* pNxt; + SwLayoutFrame* pUp; + if( (pNd = rNds[nStt])->IsContentNode() ) + for( std::vector<SwFrame*>::size_type n = 0; n < mvUpperFrames.size(); ) + { + pNxt = mvUpperFrames[n++]; + if( bFirst && pNxt && pNxt->IsSctFrame() ) + static_cast<SwSectionFrame*>(pNxt)->UnlockJoin(); + pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[n++]); + if( pNxt ) + pNxt = pNxt->GetNext(); + else + pNxt = pUp->Lower(); + pNew = static_cast<SwContentNode*>(pNd)->MakeFrame( pUp ); + pNew->Paste( pUp, pNxt ); + mvUpperFrames[n-2] = pNew; + } + else if( pNd->IsTableNode() ) + for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); ) + { + pNxt = mvUpperFrames[x++]; + if( bFirst && pNxt && pNxt->IsSctFrame() ) + static_cast<SwSectionFrame*>(pNxt)->UnlockJoin(); + pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]); + if( pNxt ) + pNxt = pNxt->GetNext(); + else + pNxt = pUp->Lower(); + pNew = static_cast<SwTableNode*>(pNd)->MakeFrame( pUp ); + assert(pNew->IsTabFrame()); + pNew->Paste( pUp, pNxt ); + static_cast<SwTabFrame*>(pNew)->RegistFlys(); + mvUpperFrames[x-2] = pNew; + } + else if( pNd->IsSectionNode() ) + { + nStt = pNd->EndOfSectionIndex(); + for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); ) + { + pNxt = mvUpperFrames[x++]; + if( bFirst && pNxt && pNxt->IsSctFrame() ) + static_cast<SwSectionFrame*>(pNxt)->UnlockJoin(); + pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]); + OSL_ENSURE( pUp->GetUpper() || pUp->IsFlyFrame(), "Lost Upper" ); + ::InsertCnt_( pUp, &rDoc, pNd->GetIndex(), false, nStt+1, pNxt ); + pNxt = pUp->GetLastLower(); + mvUpperFrames[x-2] = pNxt; + } + } + bFirst = false; + } + for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); ++x ) + { + SwFrame* pTmp = mvUpperFrames[++x]; + if( pTmp->IsFootnoteFrame() ) + static_cast<SwFootnoteFrame*>(pTmp)->ColUnlock(); + else if ( pTmp->IsInSct() ) + { + SwSectionFrame* pSctFrame = pTmp->FindSctFrame(); + pSctFrame->ColUnlock(); + // #i18103# - invalidate size of section in order to + // assure, that the section is formatted, unless it was 'Collocked' + // from its 'collection' until its 'restoration'. + pSctFrame->InvalidateSize_(); + } + } +} + +SwFrame* SwNode2LayImpl::GetFrame( const Point* pDocPos ) const +{ + // test if change of member pIter -> pMod broke anything + std::pair<Point, bool> tmp; + if (pDocPos) + { + tmp.first = *pDocPos; + tmp.second = false; + } + return mpMod ? ::GetFrameOfModify(nullptr, *mpMod, FRM_ALL, nullptr, pDocPos ? &tmp : nullptr) : nullptr; +} + +SwNode2Layout::SwNode2Layout( const SwNode& rNd, SwNodeOffset nIdx ) + : m_pImpl( new SwNode2LayImpl( rNd, nIdx, false ) ) +{ +} + +SwNode2LayoutSaveUpperFrames::SwNode2LayoutSaveUpperFrames(const SwNode& rNd) + : m_pImpl( new SwNode2LayImpl( rNd, rNd.GetIndex(), true ) ) +{ + m_pImpl->SaveUpperFrames(); +} + +void SwNode2LayoutSaveUpperFrames::RestoreUpperFrames( + SwNodes& rNds, SwNodeOffset const nStt, SwNodeOffset const nEnd) +{ + m_pImpl->RestoreUpperFrames( rNds, nStt, nEnd ); +} + +SwFrame* SwNode2Layout::NextFrame() +{ + return m_pImpl->NextFrame(); +} + +SwLayoutFrame* SwNode2Layout::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode ) +{ + return m_pImpl->UpperFrame( rpFrame, rNode ); +} + +SwNode2Layout::~SwNode2Layout() +{ +} + +SwNode2LayoutSaveUpperFrames::~SwNode2LayoutSaveUpperFrames() +{ +} + +SwFrame* SwNode2Layout::GetFrame( const Point* pDocPos ) const +{ + return m_pImpl->GetFrame( pDocPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |