/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include /** * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is * just the public interface. */ class SwNode2LayImpl { std::unique_ptr> mpIter; sw::BroadcastingModify* mpMod; std::vector 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(*static_cast(pNd)).First(); else if ( pNd->IsTableNode() ) pFound = SwIterator(*static_cast(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(*static_cast(pNd)).First(); else if ( pNd->IsTableNode() ) pFound = SwIterator(*static_cast(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(static_cast(pNd->GetContentNode())); else { assert(pNd->IsTableNode()); mpMod = pNd->GetTableNode()->GetTable().GetFrameFormat(); } mpIter.reset(new SwIterator(*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(pFrame)->ColLock(); else if( pFrame->IsInSct() ) pFrame->FindSctFrame()->ColLock(); if( pPrv && pPrv->IsSctFrame() ) static_cast(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(pFrame)->GetSection() && (&static_cast(pNode)->GetSection() == static_cast(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(pFrame)->Lower() && !static_cast(pFrame)->Lower()->IsFlowFrame() && static_cast(pFrame)->Lower()->IsLayoutFrame() ) { pFrame = static_cast(pFrame)->Lower(); } assert(pFrame->IsLayoutFrame()); rpFrame = mbMaster ? nullptr : static_cast(pFrame)->Lower(); assert((!rpFrame || rpFrame->IsFlowFrame()) && " - expected sibling isn't a flow frame." ); return static_cast(pFrame); } pUpper = new SwSectionFrame(const_cast(static_cast(pNode))->GetSection(), rpFrame); pUpper->Paste( rpFrame->GetUpper(), mbMaster ? rpFrame : rpFrame->GetNext() ); static_cast(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(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::size_type n = 0; n < mvUpperFrames.size(); ) { pNxt = mvUpperFrames[n++]; if( bFirst && pNxt && pNxt->IsSctFrame() ) static_cast(pNxt)->UnlockJoin(); pUp = static_cast(mvUpperFrames[n++]); if( pNxt ) pNxt = pNxt->GetNext(); else pNxt = pUp->Lower(); pNew = static_cast(pNd)->MakeFrame( pUp ); pNew->Paste( pUp, pNxt ); mvUpperFrames[n-2] = pNew; } else if( pNd->IsTableNode() ) for( std::vector::size_type x = 0; x < mvUpperFrames.size(); ) { pNxt = mvUpperFrames[x++]; if( bFirst && pNxt && pNxt->IsSctFrame() ) static_cast(pNxt)->UnlockJoin(); pUp = static_cast(mvUpperFrames[x++]); if( pNxt ) pNxt = pNxt->GetNext(); else pNxt = pUp->Lower(); pNew = static_cast(pNd)->MakeFrame( pUp ); assert(pNew->IsTabFrame()); pNew->Paste( pUp, pNxt ); static_cast(pNew)->RegistFlys(); mvUpperFrames[x-2] = pNew; } else if( pNd->IsSectionNode() ) { nStt = pNd->EndOfSectionIndex(); for( std::vector::size_type x = 0; x < mvUpperFrames.size(); ) { pNxt = mvUpperFrames[x++]; if( bFirst && pNxt && pNxt->IsSctFrame() ) static_cast(pNxt)->UnlockJoin(); pUp = static_cast(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::size_type x = 0; x < mvUpperFrames.size(); ++x ) { SwFrame* pTmp = mvUpperFrames[++x]; if( pTmp->IsFootnoteFrame() ) static_cast(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 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: */