diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/frmedt/tblsel.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/frmedt/tblsel.cxx')
-rw-r--r-- | sw/source/core/frmedt/tblsel.cxx | 2614 |
1 files changed, 2614 insertions, 0 deletions
diff --git a/sw/source/core/frmedt/tblsel.cxx b/sw/source/core/frmedt/tblsel.cxx new file mode 100644 index 000000000..45fc820b2 --- /dev/null +++ b/sw/source/core/frmedt/tblsel.cxx @@ -0,0 +1,2614 @@ +/* -*- 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 <editeng/boxitem.hxx> +#include <editeng/protitem.hxx> +#include <osl/diagnose.h> + +#include <hintids.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <tblsel.hxx> +#include <crsrsh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <cntfrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <rootfrm.hxx> +#include <viscrs.hxx> +#include <swtblfmt.hxx> +#include <UndoTable.hxx> +#include <sectfrm.hxx> +#include <frmtool.hxx> +#include <calbck.hxx> +#include <frameformats.hxx> +#include <deque> +#include <memory> + +// see also swtable.cxx +#define COLFUZZY 20L + +// macros, determining how table boxes are merged: +// - 1. remove empty lines, all boxes separated with blanks, +// all lines separated with ParaBreak +// - 2. remove all empty lines and remove all empty boxes at beginning and end, +// all boxes separated with Blank, +// all lines separated with ParaBreak +// - 3. remove all empty boxes, all boxes separated with blanks, +// all lines separated with ParaBreak + +#undef DEL_ONLY_EMPTY_LINES +#undef DEL_EMPTY_BOXES_AT_START_AND_END + +namespace { + +struct CmpLPt +{ + Point aPos; + const SwTableBox* pSelBox; + bool bVert; + + CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical ); + + bool operator<( const CmpLPt& rCmp ) const + { + if ( bVert ) + return X() > rCmp.X() || ( X() == rCmp.X() && Y() < rCmp.Y() ); + else + return Y() < rCmp.Y() || ( Y() == rCmp.Y() && X() < rCmp.X() ); + } + + tools::Long X() const { return aPos.X(); } + tools::Long Y() const { return aPos.Y(); } +}; + +} + +typedef o3tl::sorted_vector<CmpLPt> MergePos; + +namespace { + +struct Sort_CellFrame +{ + const SwCellFrame* pFrame; + + explicit Sort_CellFrame( const SwCellFrame& rCFrame ) + : pFrame( &rCFrame ) {} +}; + +} + +static const SwLayoutFrame *lcl_FindCellFrame( const SwLayoutFrame *pLay ) +{ + while ( pLay && !pLay->IsCellFrame() ) + pLay = pLay->GetUpper(); + return pLay; +} + +static const SwLayoutFrame *lcl_FindNextCellFrame( const SwLayoutFrame *pLay ) +{ + // ensure we leave the cell (sections) + const SwLayoutFrame *pTmp = pLay; + do { + pTmp = pTmp->GetNextLayoutLeaf(); + } while( pLay->IsAnLower( pTmp ) ); + + while( pTmp && !pTmp->IsCellFrame() ) + pTmp = pTmp->GetUpper(); + return pTmp; +} + +void GetTableSelCrs( const SwCursorShell &rShell, SwSelBoxes& rBoxes ) +{ + rBoxes.clear(); + if( rShell.IsTableMode() && const_cast<SwCursorShell&>(rShell).UpdateTableSelBoxes()) + { + rBoxes.insert(rShell.GetTableCursor()->GetSelectedBoxes()); + } +} + +void GetTableSelCrs( const SwTableCursor& rTableCursor, SwSelBoxes& rBoxes ) +{ + rBoxes.clear(); + + if (rTableCursor.IsChgd() || !rTableCursor.GetSelectedBoxesCount()) + { + SwTableCursor* pTCursor = const_cast<SwTableCursor*>(&rTableCursor); + pTCursor->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout()->MakeTableCursors( *pTCursor ); + } + + if (rTableCursor.GetSelectedBoxesCount()) + { + rBoxes.insert(rTableCursor.GetSelectedBoxes()); + } +} + +void GetTableSel( const SwCursorShell& rShell, SwSelBoxes& rBoxes, + const SwTableSearchType eSearchType ) +{ + // get start and end cell + if ( !rShell.IsTableMode() ) + rShell.GetCursor(); + + GetTableSel( *rShell.getShellCursor(false), rBoxes, eSearchType ); +} + +void GetTableSel( const SwCursor& rCursor, SwSelBoxes& rBoxes, + const SwTableSearchType eSearchType ) +{ + // get start and end cell + OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ), + "Tabselection not on Cnt." ); + + // Row-selection: + // Check for complex tables. If Yes, search selected boxes via + // the layout. Otherwise via the table structure (for macros !!) + const SwContentNode* pContentNd = rCursor.GetNode().GetContentNode(); + const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr; + if( pTableNd && pTableNd->GetTable().IsNewModel() ) + { + SwTable::SearchType eSearch; + switch( SwTableSearchType::Col & eSearchType ) + { + case SwTableSearchType::Row: eSearch = SwTable::SEARCH_ROW; break; + case SwTableSearchType::Col: eSearch = SwTable::SEARCH_COL; break; + default: eSearch = SwTable::SEARCH_NONE; break; + } + const bool bChkP( SwTableSearchType::Protect & eSearchType ); + pTableNd->GetTable().CreateSelection( rCursor, rBoxes, eSearch, bChkP ); + return; + } + if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) && + pTableNd && !pTableNd->GetTable().IsTableComplex() ) + { + const SwTable& rTable = pTableNd->GetTable(); + const SwTableLines& rLines = rTable.GetTabLines(); + + const SwNode& rMarkNode = rCursor.GetNode( false ); + const SwNodeOffset nMarkSectionStart = rMarkNode.StartOfSectionIndex(); + const SwTableBox* pMarkBox = rTable.GetTableBox( nMarkSectionStart ); + + OSL_ENSURE( pMarkBox, "Point in table, mark outside?" ); + + const SwTableLine* pLine = pMarkBox ? pMarkBox->GetUpper() : nullptr; + sal_uInt16 nSttPos = rLines.GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nSttPos, "Where is my row in the table?" ); + pLine = rTable.GetTableBox( rCursor.GetNode().StartOfSectionIndex() )->GetUpper(); + sal_uInt16 nEndPos = rLines.GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nEndPos, "Where is my row in the table?" ); + // pb: #i20193# if tableintable then nSttPos == nEndPos == USHRT_MAX + if ( nSttPos != USHRT_MAX && nEndPos != USHRT_MAX ) + { + if( nEndPos < nSttPos ) // exchange + { + sal_uInt16 nTmp = nSttPos; nSttPos = nEndPos; nEndPos = nTmp; + } + + bool bChkProtected( SwTableSearchType::Protect & eSearchType ); + for( ; nSttPos <= nEndPos; ++nSttPos ) + { + pLine = rLines[ nSttPos ]; + for( auto n = pLine->GetTabBoxes().size(); n ; ) + { + SwTableBox* pBox = pLine->GetTabBoxes()[ --n ]; + // check for cell protection?? + if( !bChkProtected || + !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + rBoxes.insert( pBox ); + } + } + } + } + else + { + Point aPtPos, aMkPos; + const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor); + if( pShCursor ) + { + aPtPos = pShCursor->GetPtPos(); + aMkPos = pShCursor->GetMkPos(); + } + const SwContentNode *pCntNd = rCursor.GetContentNode(); + std::pair<Point, bool> tmp(aPtPos, true); + const SwLayoutFrame *pStart = pCntNd ? + pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr; + pCntNd = rCursor.GetContentNode(false); + tmp.first = aMkPos; + const SwLayoutFrame *pEnd = pCntNd ? + pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr; + if( pStart && pEnd ) + GetTableSel( pStart, pEnd, rBoxes, nullptr, eSearchType ); + } +} + +void GetTableSel( const SwLayoutFrame* pStart, const SwLayoutFrame* pEnd, + SwSelBoxes& rBoxes, SwCellFrames* pCells, + const SwTableSearchType eSearchType ) +{ + const SwTabFrame* pStartTab = pStart->FindTabFrame(); + if ( !pStartTab ) + { + OSL_FAIL( "GetTableSel without start table" ); + return; + } + + bool bChkProtected( SwTableSearchType::Protect & eSearchType ); + + // #i55421# Reduced value 10 + int nLoopMax = 10; + + do { + bool bTableIsValid = true; + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType ); + + Point aCurrentTopLeft( LONG_MAX, LONG_MAX ); + Point aCurrentTopRight( 0, LONG_MAX ); + Point aCurrentBottomLeft( LONG_MAX, 0 ); + Point aCurrentBottomRight( 0, 0 ); + const SwCellFrame* pCurrentTopLeftFrame = nullptr; + const SwCellFrame* pCurrentTopRightFrame = nullptr; + const SwCellFrame* pCurrentBottomLeftFrame = nullptr; + const SwCellFrame* pCurrentBottomRightFrame = nullptr; + + // Now find boxes for each entry and emit + for (size_t i = 0; i < aUnions.size() && bTableIsValid; ++i) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + if( !pTable->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow && bTableIsValid ) + { + if( !pRow->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + if ( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while (pCell && pRow->IsAnLower(pCell)) + { + if( !pCell->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + { + SwTableBox* pBox = const_cast<SwTableBox*>( + static_cast<const SwCellFrame*>(pCell)->GetTabBox()); + // check for cell protection?? + if( !bChkProtected || + !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + rBoxes.insert( pBox ); + + if ( pCells ) + { + const Point aTopLeft( pCell->getFrameArea().TopLeft() ); + const Point aTopRight( pCell->getFrameArea().TopRight() ); + const Point aBottomLeft( pCell->getFrameArea().BottomLeft() ); + const Point aBottomRight( pCell->getFrameArea().BottomRight() ); + + if ( aTopLeft.getY() < aCurrentTopLeft.getY() || + ( aTopLeft.getY() == aCurrentTopLeft.getY() && + aTopLeft.getX() < aCurrentTopLeft.getX() ) ) + { + aCurrentTopLeft = aTopLeft; + pCurrentTopLeftFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aTopRight.getY() < aCurrentTopRight.getY() || + ( aTopRight.getY() == aCurrentTopRight.getY() && + aTopRight.getX() > aCurrentTopRight.getX() ) ) + { + aCurrentTopRight = aTopRight; + pCurrentTopRightFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aBottomLeft.getY() > aCurrentBottomLeft.getY() || + ( aBottomLeft.getY() == aCurrentBottomLeft.getY() && + aBottomLeft.getX() < aCurrentBottomLeft.getX() ) ) + { + aCurrentBottomLeft = aBottomLeft; + pCurrentBottomLeftFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aBottomRight.getY() > aCurrentBottomRight.getY() || + ( aBottomRight.getY() == aCurrentBottomRight.getY() && + aBottomRight.getX() > aCurrentBottomRight.getX() ) ) + { + aCurrentBottomRight = aBottomRight; + pCurrentBottomRightFrame = static_cast<const SwCellFrame*>( pCell ); + } + + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + if ( pCells ) + { + pCells->clear(); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopLeftFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopRightFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomLeftFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomRightFrame) ); + } + + if( bTableIsValid ) + break; + + SwDeletionChecker aDelCheck( pStart ); + + // otherwise quickly "calculate" the table layout and start over + SwTabFrame *pTable = aUnions.front().GetTable(); + while( pTable ) + { + if( pTable->isFrameAreaDefinitionValid() ) + { + pTable->InvalidatePos(); + } + + pTable->SetONECalcLowers(); + pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut()); + pTable->SetCompletePaint(); + + pTable = pTable->GetFollow(); + if( nullptr == pTable ) + break; + } + + // --> Make code robust, check if pStart has + // been deleted due to the formatting of the table: + if ( aDelCheck.HasBeenDeleted() ) + { + OSL_FAIL( "Current box has been deleted during GetTableSel()" ); + break; + } + + rBoxes.clear(); + --nLoopMax; + + } while( true ); + OSL_ENSURE( nLoopMax, "Table layout is still invalid!" ); +} + +bool ChkChartSel( const SwNode& rSttNd, const SwNode& rEndNd ) +{ + const SwTableNode* pTNd = rSttNd.FindTableNode(); + if( !pTNd ) + return false; + + Point aNullPos; + SwNodeIndex aIdx( rSttNd ); + const SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false ); + + // if table is invisible, return + // (layout needed for forming table selection further down, so we can't + // continue with invisible tables) + // #i22135# - Also the content of the table could be + // invisible - e.g. in a hidden section + // Robust: check, if content was found (e.g. empty table cells) + if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr ) + return false; + + std::pair<Point, bool> tmp(aNullPos, true); + const SwLayoutFrame *const pStart = pCNd->getLayoutFrame( + pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + OSL_ENSURE( pStart, "without frame nothing works" ); + + aIdx = rEndNd; + pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false ); + + // #i22135# - Robust: check, if content was found and if it's visible + if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr ) + { + return false; + } + + const SwLayoutFrame *const pEnd = pCNd->getLayoutFrame( + pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + OSL_ENSURE( pEnd, "without frame nothing works" ); + + bool bValidChartSel; + // #i55421# Reduced value 10 + int nLoopMax = 10; //JP 28.06.99: max 100 loops - Bug 67292 + + do { + bool bTableIsValid = true; + bValidChartSel = true; + + sal_uInt16 nRowCells = USHRT_MAX; + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::NoUnionCorrect ); + + // find boxes for each entry and emit + for( auto & rSelUnion : aUnions ) + { + if (!bTableIsValid || !bValidChartSel) + break; + + SwSelUnion *pUnion = &rSelUnion; + const SwTabFrame *pTable = pUnion->GetTable(); + + SwRectFnSet aRectFnSet(pTable); + bool bRTL = pTable->IsRightToLeft(); + + if( !pTable->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + std::deque< Sort_CellFrame > aCellFrames; + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow && bTableIsValid && bValidChartSel ) + { + if( !pRow->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while (pCell && pRow->IsAnLower(pCell)) + { + if( !pCell->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" ); + const SwRect& rUnion = pUnion->GetUnion(), + & rFrameRect = pCell->getFrameArea(); + + const tools::Long nUnionRight = rUnion.Right(); + const tools::Long nUnionBottom = rUnion.Bottom(); + const tools::Long nFrameRight = rFrameRect.Right(); + const tools::Long nFrameBottom = rFrameRect.Bottom(); + + // ignore if FrameRect is outside the union + + const tools::Long nXFuzzy = aRectFnSet.IsVert() ? 0 : 20; + const tools::Long nYFuzzy = aRectFnSet.IsVert() ? 20 : 0; + + if( !( rUnion.Top() + nYFuzzy > nFrameBottom || + nUnionBottom < rFrameRect.Top() + nYFuzzy || + rUnion.Left() + nXFuzzy > nFrameRight || + nUnionRight < rFrameRect.Left() + nXFuzzy )) + { + // ok, rUnion is _not_ completely outside of rFrameRect + + // if not completely inside the union, then + // for Chart it is an invalid selection + if( rUnion.Left() <= rFrameRect.Left() + nXFuzzy && + rFrameRect.Left() <= nUnionRight && + rUnion.Left() <= nFrameRight && + nFrameRight <= nUnionRight + nXFuzzy && + rUnion.Top() <= rFrameRect.Top() + nYFuzzy && + rFrameRect.Top() <= nUnionBottom && + rUnion.Top() <= nFrameBottom && + nFrameBottom <= nUnionBottom+ nYFuzzy ) + + aCellFrames.emplace_back( *static_cast<const SwCellFrame*>(pCell) ); + else + { + bValidChartSel = false; + break; + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + + if( !bValidChartSel ) + break; + + // all cells of the (part) table together. Now check if + // they're all adjacent + size_t n; + sal_uInt16 nCellCnt = 0; + tools::Long nYPos = LONG_MAX; + tools::Long nXPos = 0; + tools::Long nHeight = 0; + + for( n = 0 ; n < aCellFrames.size(); ++n ) + { + const Sort_CellFrame& rCF = aCellFrames[ n ]; + if( aRectFnSet.GetTop(rCF.pFrame->getFrameArea()) != nYPos ) + { + // new row + if( n ) + { + if( USHRT_MAX == nRowCells ) // 1. row change + nRowCells = nCellCnt; + else if( nRowCells != nCellCnt ) + { + bValidChartSel = false; + break; + } + } + nCellCnt = 1; + nYPos = aRectFnSet.GetTop(rCF.pFrame->getFrameArea()); + nHeight = aRectFnSet.GetHeight(rCF.pFrame->getFrameArea()); + + nXPos = bRTL ? + aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) : + aRectFnSet.GetRight(rCF.pFrame->getFrameArea()); + } + else if( nXPos == ( bRTL ? + aRectFnSet.GetRight(rCF.pFrame->getFrameArea()) : + aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) ) && + nHeight == aRectFnSet.GetHeight(rCF.pFrame->getFrameArea()) ) + { + nXPos += ( bRTL ? -1 : 1 ) * + aRectFnSet.GetWidth(rCF.pFrame->getFrameArea()); + ++nCellCnt; + } + else + { + bValidChartSel = false; + break; + } + } + if( bValidChartSel ) + { + if( USHRT_MAX == nRowCells ) + nRowCells = nCellCnt; + else if( nRowCells != nCellCnt ) + bValidChartSel = false; + } + } + + if( bTableIsValid ) + break; + + // otherwise quickly "calculate" table layout and start over + SwTabFrame *pTable = aUnions.front().GetTable(); + + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + if( pTable->isFrameAreaDefinitionValid() ) + { + pTable->InvalidatePos(); + } + + pTable->SetONECalcLowers(); + pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut()); + pTable->SetCompletePaint(); + + pTable = pTable->GetFollow(); + if( nullptr == pTable ) + break; + } + --nLoopMax; + } while( true ); + + OSL_ENSURE( nLoopMax, "table layout is still invalid!" ); + + return bValidChartSel; +} + +bool IsFrameInTableSel( const SwRect& rUnion, const SwFrame* pCell ) +{ + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Gazelle" ); + + if( pCell->FindTabFrame()->IsVertical() ) + return rUnion.Right() >= pCell->getFrameArea().Right() && + rUnion.Left() <= pCell->getFrameArea().Left() && + (( rUnion.Top() <= pCell->getFrameArea().Top()+20 && + rUnion.Bottom() > pCell->getFrameArea().Top() ) || + ( rUnion.Top() >= pCell->getFrameArea().Top() && + rUnion.Bottom() < pCell->getFrameArea().Bottom() )); + + return + rUnion.Top() <= pCell->getFrameArea().Top() && + rUnion.Bottom() >= pCell->getFrameArea().Bottom() && + + (( rUnion.Left() <= pCell->getFrameArea().Left()+20 && + rUnion.Right() > pCell->getFrameArea().Left() ) || + + ( rUnion.Left() >= pCell->getFrameArea().Left() && + rUnion.Right() < pCell->getFrameArea().Right() )); +} + +bool GetAutoSumSel( const SwCursorShell& rShell, SwCellFrames& rBoxes ) +{ + SwShellCursor* pCursor = rShell.m_pCurrentCursor; + if ( rShell.IsTableMode() ) + pCursor = rShell.m_pTableCursor; + + std::pair<Point, bool> tmp(pCursor->GetPtPos(), true); + const SwLayoutFrame *const pStart = pCursor->GetContentNode()->getLayoutFrame( + rShell.GetLayout(), nullptr, &tmp)->GetUpper(); + tmp.first = pCursor->GetMkPos(); + const SwLayoutFrame *const pEnd = pCursor->GetContentNode(false)->getLayoutFrame( + rShell.GetLayout(), nullptr, &tmp)->GetUpper(); + + const SwLayoutFrame* pSttCell = pStart; + while( pSttCell && !pSttCell->IsCellFrame() ) + pSttCell = pSttCell->GetUpper(); + + // First, compute tables and rectangles + SwSelUnions aUnions; + + // by default, first test above and then to the left + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Col ); + + bool bTstRow = true, bFound = false; + + // 1. check if box above contains value/formula + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow ) + { + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwCellFrame* pUpperCell = nullptr; + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while( pCell && pRow->IsAnLower( pCell ) ) + { + if( pCell == pSttCell ) + { + sal_uInt16 nWhichId = 0; + for( size_t n = rBoxes.size(); n; ) + { + nWhichId = rBoxes[ --n ]->GetTabBox()->IsFormulaOrValueBox(); + if( USHRT_MAX != nWhichId ) + break; + } + + // all boxes together, do not check the + // row, if a formula or value was found + bTstRow = 0 == nWhichId || USHRT_MAX == nWhichId; + bFound = true; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + pUpperCell = static_cast<const SwCellFrame*>(pCell); + + if( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + + if( pUpperCell ) + rBoxes.push_back( const_cast< SwCellFrame* >(pUpperCell) ); + } + if( bFound ) + { + i = aUnions.size(); + break; + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + // 2. check if box on left contains value/formula + if( bTstRow ) + { + bFound = false; + + rBoxes.clear(); + aUnions.clear(); + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Row ); + + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow ) + { + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while( pCell && pRow->IsAnLower( pCell ) ) + { + if( pCell == pSttCell ) + { + sal_uInt16 nWhichId = 0; + for( size_t n = rBoxes.size(); n; ) + { + nWhichId = rBoxes[ --n ] + ->GetTabBox()->IsFormulaOrValueBox(); + if( USHRT_MAX != nWhichId ) + break; + } + + // all boxes together, do not check the + // row if a formula or value was found + bFound = 0 != nWhichId && USHRT_MAX != nWhichId; + bTstRow = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + { + SwCellFrame* pC = const_cast<SwCellFrame*>(static_cast<const SwCellFrame*>(pCell)); + rBoxes.push_back( pC ); + } + if( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + if( !bTstRow ) + { + i = aUnions.size(); + break; + } + + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + } + + return bFound; +} + +bool HasProtectedCells( const SwSelBoxes& rBoxes ) +{ + bool bRet = false; + for (size_t n = 0; n < rBoxes.size(); ++n) + { + if( rBoxes[ n ]->GetFrameFormat()->GetProtect().IsContentProtected() ) + { + bRet = true; + break; + } + } + return bRet; +} + +CmpLPt::CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical ) + : aPos( rPt ), pSelBox( pBox ), bVert( bVertical ) +{} + +static void lcl_InsTableBox( SwTableNode* pTableNd, SwDoc* pDoc, SwTableBox* pBox, + sal_uInt16 nInsPos, sal_uInt16 nCnt = 1 ) +{ + OSL_ENSURE( pBox->GetSttNd(), "Box without Start-Node" ); + SwContentNode* pCNd = pDoc->GetNodes()[ pBox->GetSttIdx() + 1 ] + ->GetContentNode(); + if( pCNd && pCNd->IsTextNode() ) + pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(), + static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()), + static_cast<SwTextNode*>(pCNd)->GetTextColl(), + pCNd->GetpSwAttrSet(), + nInsPos, nCnt ); + else + pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(), + static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()), + pDoc->GetDfltTextFormatColl(), nullptr, + nInsPos, nCnt ); +} + +bool IsEmptyBox( const SwTableBox& rBox, SwPaM& rPam ) +{ + rPam.GetPoint()->nNode = *rBox.GetSttNd()->EndOfSectionNode(); + rPam.Move( fnMoveBackward, GoInContent ); + rPam.SetMark(); + rPam.GetPoint()->nNode = *rBox.GetSttNd(); + rPam.Move( fnMoveForward, GoInContent ); + bool bRet = *rPam.GetMark() == *rPam.GetPoint() + && ( rBox.GetSttNd()->GetIndex() + 1 == rPam.GetPoint()->nNode.GetIndex() ); + + if( bRet ) + { + // now check for paragraph bound flies + const SwFrameFormats& rFormats = *rPam.GetDoc().GetSpzFrameFormats(); + SwNodeOffset nSttIdx = rPam.GetPoint()->nNode.GetIndex(), + nEndIdx = rBox.GetSttNd()->EndOfSectionIndex(), + nIdx; + + for( auto pFormat : rFormats ) + { + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + const SwPosition* pAPos = rAnchor.GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) && + nSttIdx <= ( nIdx = pAPos->nNode.GetIndex() ) && + nIdx < nEndIdx ) + { + bRet = false; + break; + } + } + } + return bRet; +} + +void GetMergeSel( const SwPaM& rPam, SwSelBoxes& rBoxes, + SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo ) +{ + rBoxes.clear(); + + OSL_ENSURE( rPam.GetContentNode() && rPam.GetContentNode( false ), + "Tabselection not on Cnt." ); + +//JP 24.09.96: Merge with repeating TableHeadLines does not work properly. +// Why not use point 0,0? Then it is assured the first +// headline is contained. + Point aPt( 0, 0 ); + + const SwContentNode* pCntNd = rPam.GetContentNode(); + std::pair<Point, bool> const tmp(aPt, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rPam.GetContentNode(false); + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd ); + if( aUnions.empty() ) + return; + + const SwTable *pTable = aUnions.front().GetTable()->GetTable(); + SwDoc* pDoc = const_cast<SwDoc*>(pStart->GetFormat()->GetDoc()); + SwTableNode* pTableNd = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[ 0 ]-> + GetSttNd()->FindTableNode()); + + MergePos aPosArr; // Sort-Array with the frame positions + tools::Long nWidth; + SwTableBox* pLastBox = nullptr; + + SwRectFnSet aRectFnSet(pStart->GetUpper()); + + for ( auto & rSelUnion : aUnions ) + { + const SwTabFrame *pTabFrame = rSelUnion.GetTable(); + + SwRect &rUnion = rSelUnion.GetUnion(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTabFrame->IsFollow() ? + pTabFrame->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTabFrame->Lower()); + + while ( pRow ) + { + if ( pRow->getFrameArea().Overlaps( rUnion ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while ( pCell && pRow->IsAnLower( pCell ) ) + { + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + // overlap in full width? + if( rUnion.Top() <= pCell->getFrameArea().Top() && + rUnion.Bottom() >= pCell->getFrameArea().Bottom() ) + { + SwTableBox* pBox = const_cast<SwTableBox*>(static_cast<const SwCellFrame*>(pCell)->GetTabBox()); + + // only overlap to the right? + if( ( rUnion.Left() - COLFUZZY ) <= pCell->getFrameArea().Left() && + ( rUnion.Right() - COLFUZZY ) > pCell->getFrameArea().Left() ) + { + if( ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() ) + { + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos ); + pBox->ClaimFrameFormat(); + SwFormatFrameSize aNew( + pBox->GetFrameFormat()->GetFrameSize() ); + nWidth = rUnion.Right() - pCell->getFrameArea().Left(); + nWidth = nWidth * aNew.GetWidth() / + pCell->getFrameArea().Width(); + tools::Long nTmpWidth = aNew.GetWidth() - nWidth; + aNew.SetWidth( nWidth ); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nTmpWidth ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + else + { + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + } + } + // overlapping on left- or right-side + else if( ( rUnion.Left() - COLFUZZY ) >= pCell->getFrameArea().Left() && + ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() ) + { + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos, 2 ); + pBox->ClaimFrameFormat(); + SwFormatFrameSize aNew( + pBox->GetFrameFormat()->GetFrameSize() ); + tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left(); + nLeft = nLeft * aNew.GetWidth() / + pCell->getFrameArea().Width(); + tools::Long nRight = pCell->getFrameArea().Right() - rUnion.Right(); + nRight = nRight * aNew.GetWidth() / + pCell->getFrameArea().Width(); + nWidth = aNew.GetWidth() - nLeft - nRight; + + aNew.SetWidth( nLeft ); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( const SvxBoxItem* pItem = pBox->GetFrameFormat()->GetAttrSet() + .GetItemIfSet( RES_BOX, false )) + { + SvxBoxItem aBox( *pItem ); + aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT ); + pBox->GetFrameFormat()->SetFormatAttr( aBox ); + } + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nWidth ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos+1 ]; + aNew.SetWidth( nRight ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + // is right side of box part of the selected area? + else if( ( pCell->getFrameArea().Right() - COLFUZZY ) < rUnion.Right() && + ( pCell->getFrameArea().Right() - COLFUZZY ) > rUnion.Left() && + ( pCell->getFrameArea().Left() + COLFUZZY ) < rUnion.Left() ) + { + // then we should insert a new box and adjust the widths + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos ); + + SwFormatFrameSize aNew(pBox->GetFrameFormat()->GetFrameSize() ); + tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left(), + nRight = pCell->getFrameArea().Right() - rUnion.Left(); + + nLeft = nLeft * aNew.GetWidth() / + pCell->getFrameArea().Width(); + nRight = nRight * aNew.GetWidth() / + pCell->getFrameArea().Width(); + + aNew.SetWidth( nLeft ); + pBox->ClaimFrameFormat()->SetFormatAttr( aNew ); + + // this box is selected + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nRight ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( CmpLPt( Point( rUnion.Left(), + pCell->getFrameArea().Top()), pBox, aRectFnSet.IsVert() )); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + // --> Check if table cell is not empty + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + // no SSelection / no boxes found + if( 1 >= rBoxes.size() ) + return; + + // now search all horizontally adjacent boxes and connect + // their contents with blanks. All vertically adjacent will be tied + // together as paragraphs + + // 1. Solution: map array and all on same Y-level + // are separated with blanks + // all others are separated with paragraphs + bool bCalcWidth = true; + const SwTableBox* pFirstBox = aPosArr[ 0 ].pSelBox; + + // JP 27.03.98: Optimise - if boxes on one row are empty, + // then do not insert blanks or carriage returns + //Block to assure SwPaM, SwPosition are deleted from stack + { + SwPaM aPam( pDoc->GetNodes() ); + +#if defined( DEL_ONLY_EMPTY_LINES ) + nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth(); + bool bEmptyLine = sal_True; + sal_uInt16 n, nSttPos = 0; + + for( n = 0; n < aPosArr.Count(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level? + { + if( bEmptyLine && !IsEmptyBox( *rPt.pSelBox, aPam )) + bEmptyLine = sal_False; + if( bCalcWidth ) + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + } + else + { + if( bCalcWidth && n ) + bCalcWidth = false; // one line is ready + + if( bEmptyLine && nSttPos < n ) + { + // now complete line is empty and should not + // be filled with blanks and be inserted as paragraph + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + aPosArr.Remove( nSttPos, n - nSttPos ); + n = nSttPos; + } + else + nSttPos = n; + + bEmptyLine = IsEmptyBox( *aPosArr[n].pSelBox, aPam ); + } + } + if( bEmptyLine && nSttPos < n ) + { + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + aPosArr.Remove( nSttPos, n - nSttPos ); + } +#elif defined( DEL_EMPTY_BOXES_AT_START_AND_END ) + + nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth(); + sal_uInt16 n, nSttPos = 0, nSEndPos = 0, nESttPos = 0; + + for( n = 0; n < aPosArr.Count(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level? + { + bool bEmptyBox = IsEmptyBox( *rPt.pSelBox, aPam ); + if( bEmptyBox ) + { + if( nSEndPos == n ) // beginning is empty + nESttPos = ++nSEndPos; + } + else // end could be empty + nESttPos = n+1; + + if( bCalcWidth ) + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + } + else + { + if( bCalcWidth && n ) + bCalcWidth = false; // one line ready + + // first those at the beginning + if( nSttPos < nSEndPos ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = nSEndPos - nSttPos; + aPosArr.Remove( nSttPos, nCnt ); + nESttPos -= nCnt; + n -= nCnt; + } + + if( nESttPos < n ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nESttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = n - nESttPos; + aPosArr.Remove( nESttPos, nCnt ); + n -= nCnt; + } + + nSttPos = nSEndPos = nESttPos = n; + if( IsEmptyBox( *aPosArr[n].pSelBox, aPam )) + ++nSEndPos; + else + ++nESttPos; + } + } + + // first those at the beginning + if( nSttPos < nSEndPos ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = nSEndPos - nSttPos; + aPosArr.Remove( nSttPos, nCnt ); + nESttPos -= nCnt; + n -= nCnt; + } + if( nESttPos < n ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nESttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = n - nESttPos; + aPosArr.Remove( nESttPos, nCnt ); + } +#else +// DEL_ALL_EMPTY_BOXES + + nWidth = 0; + tools::Long nY = !aPosArr.empty() ? + ( aRectFnSet.IsVert() ? + aPosArr[ 0 ].X() : + aPosArr[ 0 ].Y() ) : + 0; + + for( MergePos::size_type n = 0; n < aPosArr.size(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( bCalcWidth ) + { + if( nY == ( aRectFnSet.IsVert() ? rPt.X() : rPt.Y() ) ) // same Y level? + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + else + bCalcWidth = false; // one line ready + } + + if( IsEmptyBox( *rPt.pSelBox, aPam ) ) + { + if( pUndo ) + pUndo->SaveCollection( *rPt.pSelBox ); + + aPosArr.erase( aPosArr.begin() + n ); + --n; + } + } +#endif + } + + // first create new box + { + SwTableBox* pTmpBox = rBoxes[0]; + SwTableLine* pInsLine = pTmpBox->GetUpper(); + sal_uInt16 nInsPos = pInsLine->GetBoxPos( pTmpBox ); + + lcl_InsTableBox( pTableNd, pDoc, pTmpBox, nInsPos ); + (*ppMergeBox) = pInsLine->GetTabBoxes()[ nInsPos ]; + pInsLine->GetTabBoxes().erase( pInsLine->GetTabBoxes().begin() + nInsPos ); // remove again + (*ppMergeBox)->SetUpper( nullptr ); + (*ppMergeBox)->ClaimFrameFormat(); + + // define the border: the upper/left side of the first box, + // the lower/right side of the last box: + if( pLastBox && pFirstBox ) + { + SvxBoxItem aBox( pFirstBox->GetFrameFormat()->GetBox() ); + const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox(); + aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT ); + aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM ); + if( aBox.GetLeft() || aBox.GetTop() || + aBox.GetRight() || aBox.GetBottom() ) + (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox ); + } + } + + //Block to delete SwPaM, SwPosition from stack + if( !aPosArr.empty() ) + { + SwPosition aInsPos( *(*ppMergeBox)->GetSttNd() ); + SwNodeIndex& rInsPosNd = aInsPos.nNode; + + SwPaM aPam( aInsPos ); + + for( const auto &rPt : aPosArr ) + { + aPam.GetPoint()->nNode.Assign( *rPt.pSelBox->GetSttNd()-> + EndOfSectionNode(), SwNodeOffset(-1) ); + SwContentNode* pCNd = aPam.GetContentNode(); + aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 ); + + SwNodeIndex aSttNdIdx( *rPt.pSelBox->GetSttNd(), 1 ); + // one node should be kept in the box (otherwise the + // section would be deleted during a move) + bool const bUndo(pDoc->GetIDocumentUndoRedo().DoesUndo()); + if( pUndo ) + { + pDoc->GetIDocumentUndoRedo().DoUndo(false); + } + pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() ); + if( pUndo ) + { + pDoc->GetIDocumentUndoRedo().DoUndo(bUndo); + } + SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode ); + ++rInsPosNd; + if( pUndo ) + pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd ); + else + { + pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd, + SwMoveFlags::DEFAULT ); + } + // where is now aInsPos ?? + + if( bCalcWidth ) + bCalcWidth = false; // one line is ready + + // skip the first TextNode + rInsPosNd.Assign( pDoc->GetNodes(), + rInsPosNd.GetNode().EndOfSectionIndex() - 2 ); + SwTextNode* pTextNd = rInsPosNd.GetNode().GetTextNode(); + if( pTextNd ) + aInsPos.nContent.Assign(pTextNd, pTextNd->GetText().getLength()); + } + + // the MergeBox should contain the complete text + // now erase the initial TextNode + OSL_ENSURE( (*ppMergeBox)->GetSttIdx()+2 < + (*ppMergeBox)->GetSttNd()->EndOfSectionIndex(), + "empty box" ); + SwNodeIndex aIdx( *(*ppMergeBox)->GetSttNd()->EndOfSectionNode(), -1 ); + pDoc->GetNodes().Delete( aIdx ); + } + + // set width of the box + (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 )); + if( pUndo ) + pUndo->AddNewBox( (*ppMergeBox)->GetSttIdx() ); +} + +static bool lcl_CheckCol(FndBox_ const&, bool* pPara); + +static bool lcl_CheckRow( const FndLine_& rFndLine, bool* pPara ) +{ + for (auto const& it : rFndLine.GetBoxes()) + { + lcl_CheckCol(*it, pPara); + } + return *pPara; +} + +static bool lcl_CheckCol( FndBox_ const& rFndBox, bool* pPara ) +{ + if (!rFndBox.GetBox()->GetSttNd()) + { + if (rFndBox.GetLines().size() != + rFndBox.GetBox()->GetTabLines().size()) + { + *pPara = false; + } + else + { + for (auto const& rpFndLine : rFndBox.GetLines()) + { + lcl_CheckRow( *rpFndLine, pPara ); + } + } + } + // is box protected ?? + else if (rFndBox.GetBox()->GetFrameFormat()->GetProtect().IsContentProtected()) + *pPara = false; + return *pPara; +} + +TableMergeErr CheckMergeSel( const SwPaM& rPam ) +{ + SwSelBoxes aBoxes; +//JP 24.09.96: Merge with repeating TableHeadLines does not work properly. +// Why not use point 0,0? Then it is assured the first +// headline is contained. + + Point aPt; + const SwContentNode* pCntNd = rPam.GetContentNode(); + std::pair<Point, bool> tmp(aPt, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rPam.GetContentNode(false); + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + GetTableSel( pStart, pEnd, aBoxes, nullptr ); + return CheckMergeSel( aBoxes ); +} + +TableMergeErr CheckMergeSel( const SwSelBoxes& rBoxes ) +{ + TableMergeErr eRet = TableMergeErr::NoSelection; + if( !rBoxes.empty() ) + { + eRet = TableMergeErr::Ok; + + FndBox_ aFndBox( nullptr, nullptr ); + FndPara aPara( rBoxes, &aFndBox ); + const SwTableNode* pTableNd = aPara.rBoxes[0]->GetSttNd()->FindTableNode(); + ForEach_FndLineCopyCol( const_cast<SwTableLines&>(pTableNd->GetTable().GetTabLines()), &aPara ); + if( !aFndBox.GetLines().empty() ) + { + bool bMergeSelOk = true; + FndBox_* pFndBox = &aFndBox; + FndLine_* pFndLine = nullptr; + while( pFndBox && 1 == pFndBox->GetLines().size() ) + { + pFndLine = pFndBox->GetLines().front().get(); + if( 1 == pFndLine->GetBoxes().size() ) + pFndBox = pFndLine->GetBoxes().front().get(); + else + pFndBox = nullptr; + } + if( pFndBox ) + { + for (auto const& it : pFndBox->GetLines()) + { + lcl_CheckRow(*it, &bMergeSelOk); + } + } + else if( pFndLine ) + { + for (auto const& it : pFndLine->GetBoxes()) + { + lcl_CheckCol(*it, &bMergeSelOk); + } + } + if( !bMergeSelOk ) + eRet = TableMergeErr::TooComplex; + } + else + eRet = TableMergeErr::NoSelection; + } + return eRet; +} + +static SwTwips lcl_CalcWish( const SwLayoutFrame *pCell, tools::Long nWish, + const tools::Long nAct ) +{ + const SwLayoutFrame *pTmp = pCell; + if ( !nWish ) + nWish = 1; + + const bool bRTL = pCell->IsRightToLeft(); + SwTwips nRet = bRTL ? + nAct - pCell->getFrameArea().Width() : + 0; + + while ( pTmp ) + { + while ( pTmp->GetPrev() ) + { + pTmp = static_cast<const SwLayoutFrame*>(pTmp->GetPrev()); + sal_Int64 nTmp = pTmp->GetFormat()->GetFrameSize().GetWidth(); + // multiply in 64-bit to avoid overflow here! + nRet += ( bRTL ? -1 : 1 ) * nTmp * nAct / nWish; + } + pTmp = pTmp->GetUpper()->GetUpper(); + if ( pTmp && !pTmp->IsCellFrame() ) + pTmp = nullptr; + } + return nRet; +} + +static void lcl_FindStartEndRow( const SwLayoutFrame *&rpStart, + const SwLayoutFrame *&rpEnd, + const bool bChkProtected ) +{ + // Put Start at beginning of a row. + // Put End at the end of its row. + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetUpper()->Lower()); + while ( rpEnd->GetNext() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext()); + + std::deque<const SwLayoutFrame *> aSttArr, aEndArr; + const SwLayoutFrame *pTmp; + for( pTmp = rpStart; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType(); + pTmp = pTmp->GetUpper() ) + { + aSttArr.push_front( pTmp ); + } + for( pTmp = rpEnd; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType(); + pTmp = pTmp->GetUpper() ) + { + aEndArr.push_front( pTmp ); + } + + for( std::deque<const SwLayoutFrame *>::size_type n = 0; n < aEndArr.size() && n < aSttArr.size(); ++n ) + if( aSttArr[ n ] != aEndArr[ n ] ) + { + // first unequal line or box - all odds are + if( n & 1 ) // 1, 3, 5, ... are boxes + { + rpStart = aSttArr[ n ]; + rpEnd = aEndArr[ n ]; + } + else // 0, 2, 4, ... are lines + { + // check if start & end line are the first & last Line of the + // box. If not return these cells. + // Else the whole line with all Boxes has to be deleted. + rpStart = aSttArr[ n+1 ]; + rpEnd = aEndArr[ n+1 ]; + if( n ) + { + const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(aSttArr[ n-1 ]); + const SwTableLines& rLns = pCellFrame-> + GetTabBox()->GetTabLines(); + if( rLns[ 0 ] == static_cast<const SwRowFrame*>(aSttArr[ n ])->GetTabLine() && + rLns[ rLns.size() - 1 ] == + static_cast<const SwRowFrame*>(aEndArr[ n ])->GetTabLine() ) + { + rpStart = rpEnd = pCellFrame; + while ( rpStart->GetPrev() ) + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetPrev()); + while ( rpEnd->GetNext() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext()); + } + } + } + break; + } + + if( !bChkProtected ) // protected cell ? + return; + + // Beginning and end should not be in protected cells + while ( rpStart->GetFormat()->GetProtect().IsContentProtected() ) + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetNext()); + while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetPrev()); +} + +static void lcl_FindStartEndCol( const SwLayoutFrame *&rpStart, + const SwLayoutFrame *&rpEnd, + const bool bChkProtected ) +{ + // Beginning and end vertical till the border of the table; + // Consider the whole table, including master and follows. + // In order to start we need the mother-tableFrame + if( !rpStart ) + return; + const SwTabFrame *pOrg = rpStart->FindTabFrame(); + const SwTabFrame *pTab = pOrg; + + SwRectFnSet aRectFnSet(pTab); + + bool bRTL = pTab->IsRightToLeft(); + const tools::Long nTmpWish = pOrg->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nWish = ( nTmpWish > 0 ) ? nTmpWish : 1; + + while ( pTab->IsFollow() ) + { + const SwFrame *pTmp = pTab->FindPrev(); + OSL_ENSURE( pTmp->IsTabFrame(), "Predecessor of Follow is not Master." ); + pTab = static_cast<const SwTabFrame*>(pTmp); + } + + SwTwips nSX = 0; + SwTwips nSX2 = 0; + + if ( pTab->GetTable()->IsNewModel() ) + { + nSX = aRectFnSet.GetLeft(rpStart->getFrameArea()); + nSX2 = aRectFnSet.GetRight(rpStart->getFrameArea()); + } + else + { + const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + nSX = ::lcl_CalcWish( rpStart, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab); + nSX2 = nSX + (rpStart->GetFormat()->GetFrameSize().GetWidth() * nPrtWidth / nWish); + } + + const SwLayoutFrame *pTmp = pTab->FirstCell(); + + while ( pTmp && + (!pTmp->IsCellFrame() || + ( ( ! bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) < nSX && + aRectFnSet.GetRight(pTmp->getFrameArea())< nSX2 ) || + ( bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) > nSX && + aRectFnSet.GetRight(pTmp->getFrameArea())> nSX2 ) ) ) ) + pTmp = pTmp->GetNextLayoutLeaf(); + + if ( pTmp ) + rpStart = pTmp; + + pTab = pOrg; + + const SwTabFrame* pLastValidTab = pTab; + while ( pTab->GetFollow() ) + { + + // Check if pTab->GetFollow() is a valid follow table: + // Only follow tables with at least on non-FollowFlowLine + // should be considered. + + if ( pTab->HasFollowFlowLine() ) + { + pTab = pTab->GetFollow(); + const SwFrame* pTmpRow = pTab->GetFirstNonHeadlineRow(); + if ( pTmpRow && pTmpRow->GetNext() ) + pLastValidTab = pTab; + } + else + pLastValidTab = pTab = pTab->GetFollow(); + } + pTab = pLastValidTab; + + SwTwips nEX = 0; + + if ( pTab->GetTable()->IsNewModel() ) + { + nEX = aRectFnSet.GetLeft(rpEnd->getFrameArea()); + } + else + { + const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + nEX = ::lcl_CalcWish( rpEnd, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab); + } + + SwFrame const*const pLastContent = pTab->FindLastContentOrTable(); + rpEnd = pLastContent ? pLastContent->GetUpper() : nullptr; + // --> Made code robust. If pTab does not have a lower, + // we would crash here. + if ( !pLastContent ) return; + + while( !rpEnd->IsCellFrame() ) + rpEnd = rpEnd->GetUpper(); + + while ( ( bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) < nEX ) || + ( ! bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX ) ) + { + const SwLayoutFrame* pTmpLeaf = rpEnd->GetPrevLayoutLeaf(); + if( !pTmpLeaf || !pTab->IsAnLower( pTmpLeaf ) ) + break; + rpEnd = pTmpLeaf; + } + + if( !bChkProtected ) // check for protected cell ? + return; + + // Beginning and end should not be in protected cells. + // If necessary we should search backwards again + while ( rpStart->GetFormat()->GetProtect().IsContentProtected() ) + { + const SwLayoutFrame *pTmpLeaf = rpStart->GetNextLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX ) // first skip line + pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nSX && + aRectFnSet.GetRight(pTmpLeaf->getFrameArea())< nSX2 ) + pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf(); + const SwTabFrame *pTmpTab = rpStart->FindTabFrame(); + if ( !pTmpTab->IsAnLower( pTmpLeaf ) ) + { + pTmpTab = pTmpTab->GetFollow(); + rpStart = pTmpTab->FirstCell(); + while ( rpStart && + aRectFnSet.GetLeft(rpStart->getFrameArea()) < nSX && + aRectFnSet.GetRight(rpStart->getFrameArea())< nSX2 ) + rpStart = rpStart->GetNextLayoutLeaf(); + } + else + rpStart = pTmpLeaf; + } + while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() ) + { + const SwLayoutFrame *pTmpLeaf = rpEnd->GetPrevLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nEX ) // skip the line for now + pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX ) + pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf(); + const SwTabFrame *pTmpTab = rpEnd->FindTabFrame(); + if ( !pTmpLeaf || !pTmpTab->IsAnLower( pTmpLeaf ) ) + { + pTmpTab = static_cast<const SwTabFrame*>(pTmpTab->FindPrev()); + OSL_ENSURE( pTmpTab->IsTabFrame(), "Predecessor of Follow not Master."); + rpEnd = pTmpTab->FindLastContentOrTable()->GetUpper(); + while( !rpEnd->IsCellFrame() ) + rpEnd = rpEnd->GetUpper(); + while ( aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX ) + rpEnd = rpEnd->GetPrevLayoutLeaf(); + } + else + rpEnd = pTmpLeaf; + } +} + +void MakeSelUnions( SwSelUnions& rUnions, const SwLayoutFrame *pStart, + const SwLayoutFrame *pEnd, const SwTableSearchType eSearchType ) +{ + while ( pStart && !pStart->IsCellFrame() ) + pStart = pStart->GetUpper(); + while ( pEnd && !pEnd->IsCellFrame() ) + pEnd = pEnd->GetUpper(); + + if ( !pStart || !pEnd ) + { + OSL_FAIL( "MakeSelUnions with pStart or pEnd not in CellFrame" ); + return; + } + + const SwTabFrame *pTable = pStart->FindTabFrame(); + const SwTabFrame *pEndTable = pEnd->FindTabFrame(); + if( !pTable || !pEndTable ) + return; + bool bExchange = false; + + if ( pTable != pEndTable ) + { + if ( !pTable->IsAnFollow( pEndTable ) ) + { + OSL_ENSURE( pEndTable->IsAnFollow( pTable ), "Tabchain in knots." ); + bExchange = true; + } + } + else + { + SwRectFnSet aRectFnSet(pTable); + tools::Long nSttTop = aRectFnSet.GetTop(pStart->getFrameArea()); + tools::Long nEndTop = aRectFnSet.GetTop(pEnd->getFrameArea()); + if( nSttTop == nEndTop ) + { + if( aRectFnSet.GetLeft(pStart->getFrameArea()) > + aRectFnSet.GetLeft(pEnd->getFrameArea()) ) + bExchange = true; + } + else if( aRectFnSet.IsVert() == ( nSttTop < nEndTop ) ) + bExchange = true; + } + if ( bExchange ) + { + const SwLayoutFrame *pTmp = pStart; + pStart = pEnd; + pEnd = pTmp; + // do no resort pTable and pEndTable, set new below + // MA: 28. Dec. 93 Bug: 5190 + } + + // Beginning and end now nicely sorted, if required we + // should move them + if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) ) + ::lcl_FindStartEndRow( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) ); + else if( SwTableSearchType::Col == ((~SwTableSearchType::Protect ) & eSearchType ) ) + ::lcl_FindStartEndCol( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) ); + + if ( !pEnd || !pStart ) return; // Made code robust. + + // retrieve again, as they have been moved + pTable = pStart->FindTabFrame(); + pEndTable = pEnd->FindTabFrame(); + + const tools::Long nStSz = pStart->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nEdSz = pEnd->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nWish = std::max( tools::Long(1), pTable->GetFormat()->GetFrameSize().GetWidth() ); + while ( pTable ) + { + SwRectFnSet aRectFnSet(pTable); + const tools::Long nOfst = aRectFnSet.GetPrtLeft(*pTable); + const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTable->getFramePrintArea()); + tools::Long nSt1 = ::lcl_CalcWish( pStart, nWish, nPrtWidth ) + nOfst; + tools::Long nEd1 = ::lcl_CalcWish( pEnd, nWish, nPrtWidth ) + nOfst; + + if ( nSt1 <= nEd1 ) + nEd1 += static_cast<tools::Long>((nEdSz * nPrtWidth) / nWish) - 1; + else + nSt1 += static_cast<tools::Long>((nStSz * nPrtWidth) / nWish) - 1; + + tools::Long nSt2; + tools::Long nEd2; + if( pTable->IsAnLower( pStart ) ) + nSt2 = aRectFnSet.GetTop(pStart->getFrameArea()); + else + nSt2 = aRectFnSet.GetTop(pTable->getFrameArea()); + if( pTable->IsAnLower( pEnd ) ) + nEd2 = aRectFnSet.GetBottom(pEnd->getFrameArea()); + else + nEd2 = aRectFnSet.GetBottom(pTable->getFrameArea()); + Point aSt, aEd; + if( nSt1 > nEd1 ) + { + tools::Long nTmp = nSt1; + nSt1 = nEd1; + nEd1 = nTmp; + } + if( nSt2 > nEd2 ) + { + tools::Long nTmp = nSt2; + nSt2 = nEd2; + nEd2 = nTmp; + } + if( aRectFnSet.IsVert() ) + { + aSt = Point( nSt2, nSt1 ); + aEd = Point( nEd2, nEd1 ); + } + else + { + aSt = Point( nSt1, nSt2 ); + aEd = Point( nEd1, nEd2 ); + } + + const Point aDiff( aEd - aSt ); + SwRect aUnion( aSt, Size( aDiff.X(), aDiff.Y() ) ); + aUnion.Justify(); + + if( !(SwTableSearchType::NoUnionCorrect & eSearchType )) + { + // Unfortunately the union contains rounding errors now, therefore + // erroneous results could occur during split/merge. + // To prevent these we will determine the first and last row + // within the union and use their values for a new union + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while ( pRow && !pRow->getFrameArea().Overlaps( aUnion ) ) + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + + // #i31976# + // A follow flow row may contain empty cells. These are not + // considered by FirstCell(). Therefore we have to find + // the first cell manually: + const SwFrame* pTmpCell = nullptr; + if ( pTable->IsFollow() && pRow && pRow->IsInFollowFlowRow() ) + { + const SwFrame* pTmpRow = pRow; + while ( pTmpRow && pTmpRow->IsRowFrame() ) + { + pTmpCell = static_cast<const SwRowFrame*>(pTmpRow)->Lower(); + pTmpRow = static_cast<const SwCellFrame*>(pTmpCell)->Lower(); + } + OSL_ENSURE( !pTmpCell || pTmpCell->IsCellFrame(), "Lower of rowframe != cellframe?!" ); + } + + const SwLayoutFrame* pFirst = pTmpCell ? + static_cast<const SwLayoutFrame*>(pTmpCell) : + pRow ? + pRow->FirstCell() : + nullptr; + + while ( pFirst && !::IsFrameInTableSel( aUnion, pFirst ) ) + { + if ( pFirst->GetNext() ) + { + pFirst = static_cast<const SwLayoutFrame*>(pFirst->GetNext()); + if ( pFirst->Lower() && pFirst->Lower()->IsRowFrame() ) + pFirst = pFirst->FirstCell(); + } + else + pFirst = ::lcl_FindNextCellFrame( pFirst ); + } + const SwLayoutFrame* pLast = nullptr; + SwFrame const*const pLastContent = pTable->FindLastContentOrTable(); + if ( pLastContent ) + pLast = ::lcl_FindCellFrame( pLastContent->GetUpper() ); + + while ( pLast && !::IsFrameInTableSel( aUnion, pLast ) ) + pLast = ::lcl_FindCellFrame( pLast->GetPrevLayoutLeaf() ); + + if ( pFirst && pLast ) //Robust + { + aUnion = pFirst->getFrameArea(); + aUnion.Union( pLast->getFrameArea() ); + } + else + aUnion.Width( 0 ); + } + + if( aRectFnSet.GetWidth(aUnion) ) + { + rUnions.emplace_back(aUnion, const_cast<SwTabFrame*>(pTable)); + } + + pTable = pTable->GetFollow(); + if ( pTable != pEndTable && pEndTable->IsAnFollow( pTable ) ) + pTable = nullptr; + } +} + +bool CheckSplitCells( const SwCursorShell& rShell, sal_uInt16 nDiv, + const SwTableSearchType eSearchType ) +{ + if( !rShell.IsTableMode() ) + rShell.GetCursor(); + + return CheckSplitCells( *rShell.getShellCursor(false), nDiv, eSearchType ); +} + +bool CheckSplitCells( const SwCursor& rCursor, sal_uInt16 nDiv, + const SwTableSearchType eSearchType ) +{ + if( 1 >= nDiv ) + return false; + + sal_uInt16 nMinValue = nDiv * MINLAY; + + // Get start and end cell + Point aPtPos, aMkPos; + const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor); + if( pShCursor ) + { + aPtPos = pShCursor->GetPtPos(); + aMkPos = pShCursor->GetMkPos(); + } + + const SwContentNode* pCntNd = rCursor.GetContentNode(); + std::pair<Point, bool> tmp(aPtPos, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rCursor.GetContentNode(false); + tmp.first = aMkPos; + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + + SwRectFnSet aRectFnSet(pStart->GetUpper()); + + // First, compute tables and rectangles + SwSelUnions aUnions; + + ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType ); + + // now search boxes for each entry and emit + for ( const auto& rSelUnion : aUnions ) + { + const SwTabFrame *pTable = rSelUnion.GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while ( pRow ) + { + if ( pRow->getFrameArea().Overlaps( rSelUnion.GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while ( pCell && pRow->IsAnLower( pCell ) ) + { + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( rSelUnion.GetUnion(), pCell ) ) + { + if( aRectFnSet.GetWidth(pCell->getFrameArea()) < nMinValue ) + return false; + } + + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + return true; +} + +// These Classes copy the current table selections (rBoxes), +// into a new structure, retaining the table structure +// new: SS for targeted erasing/restoring of the layout + +static void lcl_InsertRow( SwTableLine const &rLine, SwLayoutFrame *pUpper, SwFrame *pSibling ) +{ + SwRowFrame *pRow = new SwRowFrame( rLine, pUpper ); + if ( pUpper->IsTabFrame() && static_cast<SwTabFrame*>(pUpper)->IsFollow() ) + { + SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pUpper); + pTabFrame->FindMaster()->InvalidatePos(); //can absorb the line + + if ( pSibling && pTabFrame->IsInHeadline( *pSibling ) ) + { + // Skip any repeated headlines in the follow: + pSibling = pTabFrame->GetFirstNonHeadlineRow(); + } + } + pRow->Paste( pUpper, pSibling ); + pRow->RegistFlys(); +} + +static void FndBoxCopyCol( SwTableBox* pBox, FndPara* pFndPara ) +{ + std::unique_ptr<FndBox_> pFndBox(new FndBox_( pBox, pFndPara->pFndLine )); + if( !pBox->GetTabLines().empty() ) + { + FndPara aPara( *pFndPara, pFndBox.get() ); + ForEach_FndLineCopyCol( pFndBox->GetBox()->GetTabLines(), &aPara ); + if( pFndBox->GetLines().empty() ) + { + return; + } + } + else + { + if( pFndPara->rBoxes.find( pBox ) == pFndPara->rBoxes.end()) + { + return; + } + } + pFndPara->pFndLine->GetBoxes().push_back( std::move(pFndBox) ); +} + +static void FndLineCopyCol( SwTableLine* pLine, FndPara* pFndPara ) +{ + std::unique_ptr<FndLine_> pFndLine(new FndLine_(pLine, pFndPara->pFndBox)); + FndPara aPara(*pFndPara, pFndLine.get()); + for( auto& rpBox : pFndLine->GetLine()->GetTabBoxes() ) + FndBoxCopyCol(rpBox, &aPara ); + if( !pFndLine->GetBoxes().empty() ) + { + pFndPara->pFndBox->GetLines().push_back( std::move(pFndLine) ); + } +} + +void ForEach_FndLineCopyCol(SwTableLines& rLines, FndPara* pFndPara ) +{ + for( SwTableLines::iterator it = rLines.begin(); it != rLines.end(); ++it ) + FndLineCopyCol( *it, pFndPara ); +} + +void FndBox_::SetTableLines( const SwSelBoxes &rBoxes, const SwTable &rTable ) +{ + // Set pointers to lines before and after the area to process. + // If the first/last lines are contained in the area, then the pointers + // are 0. We first search for the positions of the first/last affected + // lines in array of the SwTable. In order to use 0 for 'no line' + // we adjust the positions by 1. + + sal_uInt16 nStPos = USHRT_MAX; + sal_uInt16 nEndPos= 0; + + for (size_t i = 0; i < rBoxes.size(); ++i) + { + SwTableLine *pLine = rBoxes[i]->GetUpper(); + while ( pLine->GetUpper() ) + pLine = pLine->GetUpper()->GetUpper(); + const sal_uInt16 nPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(pLine) ) + 1; + + OSL_ENSURE( nPos != USHRT_MAX, "TableLine not found." ); + + if( nStPos > nPos ) + nStPos = nPos; + + if( nEndPos < nPos ) + nEndPos = nPos; + } + if (USHRT_MAX != nStPos && nStPos > 1) + m_pLineBefore = rTable.GetTabLines()[nStPos - 2]; + if ( nEndPos < rTable.GetTabLines().size() ) + m_pLineBehind = rTable.GetTabLines()[nEndPos]; +} + +void FndBox_::SetTableLines( const SwTable &rTable ) +{ + // Set pointers to lines before and after the area to process. + // If the first/last lines are contained in the area, then the pointers + // are 0. The positions of the first/last affected lines in the array + // of the SwTable are in FndBox. In order to use 0 for 'no line' + // we adjust the positions by 1. + + if( GetLines().empty() ) + return; + + SwTableLine* pTmpLine = GetLines().front()->GetLine(); + sal_uInt16 nPos = rTable.GetTabLines().GetPos( pTmpLine ); + OSL_ENSURE( USHRT_MAX != nPos, "Line is not in table" ); + if( nPos ) + m_pLineBefore = rTable.GetTabLines()[ nPos - 1 ]; + + pTmpLine = GetLines().back()->GetLine(); + nPos = rTable.GetTabLines().GetPos( pTmpLine ); + OSL_ENSURE( USHRT_MAX != nPos, "Line is not in the table" ); + if( ++nPos < rTable.GetTabLines().size() ) + m_pLineBehind = rTable.GetTabLines()[nPos]; +} + +inline void UnsetFollow( SwFlowFrame *pTab ) +{ + pTab->m_pPrecede = nullptr; +} + +void FndBox_::DelFrames( SwTable &rTable ) +{ + // All lines between pLineBefore and pLineBehind should be cut + // from the layout and erased. + // If this creates empty Follows we should destroy these. + // If a master is destroyed, the follow should become master. + // Always a TabFrame should remain. + + sal_uInt16 nStPos = 0; + sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1; + if( rTable.IsNewModel() && m_pLineBefore ) + rTable.CheckRowSpan( m_pLineBefore, true ); + if ( m_pLineBefore ) + { + nStPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBefore) ); + OSL_ENSURE( nStPos != USHRT_MAX, "The fox stole the line!" ); + ++nStPos; + } + if( rTable.IsNewModel() && m_pLineBehind ) + rTable.CheckRowSpan( m_pLineBehind, false ); + if ( m_pLineBehind ) + { + nEndPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBehind) ); + OSL_ENSURE( nEndPos != USHRT_MAX, "The fox stole the line!" ); + if (nEndPos != 0) + --nEndPos; + } + + for ( sal_uInt16 i = nStPos; i <= nEndPos; ++i) + { + SwFrameFormat *pFormat = rTable.GetTabLines()[i]->GetFrameFormat(); + SwIterator<SwRowFrame,SwFormat> aIter( *pFormat ); + for ( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) + { + if ( pFrame->GetTabLine() == rTable.GetTabLines()[i] ) + { + bool bDel = true; + SwTabFrame *pUp = !pFrame->GetPrev() && !pFrame->GetNext() ? + static_cast<SwTabFrame*>(pFrame->GetUpper()) : nullptr; + if ( !pUp ) + { + const sal_uInt16 nRepeat = + static_cast<SwTabFrame*>(pFrame->GetUpper())->GetTable()->GetRowsToRepeat(); + if ( nRepeat > 0 && + static_cast<SwTabFrame*>(pFrame->GetUpper())->IsFollow() ) + { + if ( !pFrame->GetNext() ) + { + SwRowFrame* pFirstNonHeadline = + static_cast<SwTabFrame*>(pFrame->GetUpper())->GetFirstNonHeadlineRow(); + if ( pFirstNonHeadline == pFrame ) + { + pUp = static_cast<SwTabFrame*>(pFrame->GetUpper()); + } + } + } + } + if ( pUp ) + { + SwTabFrame *pFollow = pUp->GetFollow(); + SwTabFrame *pPrev = pUp->IsFollow() ? pUp : nullptr; + if ( pPrev ) + { + SwFrame *pTmp = pPrev->FindPrev(); + OSL_ENSURE( pTmp->IsTabFrame(), + "Predecessor of Follow is no Master."); + pPrev = static_cast<SwTabFrame*>(pTmp); + } + if ( pPrev ) + { + pPrev->SetFollow( pFollow ); + // #i60340# Do not transfer the + // flag from pUp to pPrev. pUp may still have the + // flag set although there is not more follow flow + // line associated with pUp. + pPrev->SetFollowFlowLine( false ); + } + else if ( pFollow ) + ::UnsetFollow( pFollow ); + + // A TableFrame should always remain! + if ( pPrev || pFollow ) + { + // OD 26.08.2003 #i18103# - if table is in a section, + // lock the section, to avoid its delete. + { + SwSectionFrame* pSctFrame = pUp->FindSctFrame(); + bool bOldSectLock = false; + if ( pSctFrame ) + { + bOldSectLock = pSctFrame->IsColLocked(); + pSctFrame->ColLock(); + } + pUp->Cut(); + if ( pSctFrame && !bOldSectLock ) + { + pSctFrame->ColUnlock(); + } + } + SwFrame::DestroyFrame(pUp); + bDel = false; // Row goes to /dev/null. + } + } + if ( bDel ) + { + SwFrame* pTabFrame = pFrame->GetUpper(); + if ( pTabFrame->IsTabFrame() && + !pFrame->GetNext() && + static_cast<SwTabFrame*>(pTabFrame)->GetFollow() ) + { + // We do not delete the follow flow line, + // this will be done automatically in the + // next turn. + static_cast<SwTabFrame*>(pTabFrame)->SetFollowFlowLine( false ); + } + pFrame->Cut(); + SwFrame::DestroyFrame(pFrame); + } + } + } + } +} + +static bool lcl_IsLineOfTableFrame( const SwTabFrame& rTable, const SwFrame& rChk ) +{ + const SwTabFrame* pTableFrame = rChk.FindTabFrame(); + if( pTableFrame->IsFollow() ) + pTableFrame = pTableFrame->FindMaster( true ); + return &rTable == pTableFrame; +} + +static void lcl_UpdateRepeatedHeadlines( SwTabFrame& rTabFrame, bool bCalcLowers ) +{ + OSL_ENSURE( rTabFrame.IsFollow(), "lcl_UpdateRepeatedHeadlines called for non-follow tab" ); + + // Delete remaining headlines: + SwRowFrame* pLower = nullptr; + while ( nullptr != ( pLower = static_cast<SwRowFrame*>(rTabFrame.Lower()) ) && pLower->IsRepeatedHeadline() ) + { + pLower->Cut(); + SwFrame::DestroyFrame(pLower); + } + + // Insert fresh set of headlines: + pLower = static_cast<SwRowFrame*>(rTabFrame.Lower()); + SwTable& rTable = *rTabFrame.GetTable(); + const sal_uInt16 nRepeat = rTable.GetRowsToRepeat(); + for ( sal_uInt16 nIdx = 0; nIdx < nRepeat; ++nIdx ) + { + SwRowFrame* pHeadline = new SwRowFrame( *rTable.GetTabLines()[ nIdx ], &rTabFrame ); + pHeadline->SetRepeatedHeadline( true ); + pHeadline->Paste( &rTabFrame, pLower ); + pHeadline->RegistFlys(); + } + + if ( bCalcLowers ) + rTabFrame.SetCalcLowers(); +} + +void FndBox_::MakeFrames( SwTable &rTable ) +{ + // All lines between pLineBefore and pLineBehind should be re-generated in layout. + // And this for all instances of a table (for example in header/footer). + sal_uInt16 nStPos = 0; + sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1; + SwRootFrame* pLayout = + rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(); + bool bHideChanges = pLayout && pLayout->IsHideRedlines(); + + if ( m_pLineBefore ) + { + nStPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBefore) ); + OSL_ENSURE( nStPos != USHRT_MAX, "Fox stole the line!" ); + ++nStPos; + + } + if ( m_pLineBehind ) + { + nEndPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBehind) ); + OSL_ENSURE( nEndPos != USHRT_MAX, "Fox stole the line!" ); + --nEndPos; + } + // now big insert operation for all tables. + SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() ); + for ( SwTabFrame *pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if ( !pTable->IsFollow() ) + { + SwRowFrame *pSibling = nullptr; + SwFrame *pUpperFrame = nullptr; + int i; + for ( i = rTable.GetTabLines().size()-1; + i >= 0 && !pSibling; --i ) + { + SwTableLine *pLine = m_pLineBehind ? m_pLineBehind : + rTable.GetTabLines()[o3tl::narrowing<sal_uInt16>(i)]; + SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() ); + pSibling = aIter.First(); + while ( pSibling && ( + pSibling->GetTabLine() != pLine || + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + pSibling->IsRepeatedHeadline() || + // #i53647# If !pLineBehind, + // IsInSplitTableRow() should be checked. + ( m_pLineBehind && pSibling->IsInFollowFlowRow() ) || + (!m_pLineBehind && pSibling->IsInSplitTableRow() ) ) ) + { + pSibling = aIter.Next(); + } + } + if ( pSibling ) + { + pUpperFrame = pSibling->GetUpper(); + if ( !m_pLineBehind ) + pSibling = nullptr; + } + else +// ???? or is this the last Follow of the table ???? + pUpperFrame = pTable; + + SwRedlineTable::size_type nRedlinePos = 0; + for ( sal_uInt16 j = nStPos; j <= nEndPos; ++j ) + { + SwTableLine * pLine = rTable.GetTabLines()[j]; + if ( !bHideChanges || !pLine->IsDeleted(nRedlinePos) ) + ::lcl_InsertRow( *pLine, + static_cast<SwLayoutFrame*>(pUpperFrame), pSibling ); + } + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + else if ( rTable.GetRowsToRepeat() > 0 ) + { + // Insert new headlines: + lcl_UpdateRepeatedHeadlines( *pTable, true ); + } + } +} + +void FndBox_::MakeNewFrames( SwTable &rTable, const sal_uInt16 nNumber, + const bool bBehind ) +{ + // Create Frames for newly inserted lines + // bBehind == true: before pLineBehind + // == false: after pLineBefore + const sal_uInt16 nBfPos = m_pLineBefore ? + rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBefore) ) : + USHRT_MAX; + const sal_uInt16 nBhPos = m_pLineBehind ? + rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBehind) ) : + USHRT_MAX; + + //nNumber: how often did we insert + //nCnt: how many were inserted nNumber times + + const sal_uInt16 nCnt = + ((nBhPos != USHRT_MAX ? nBhPos : rTable.GetTabLines().size()) - + (nBfPos != USHRT_MAX ? nBfPos + 1 : 0)) / (nNumber + 1); + + // search the Master-TabFrame + SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() ); + SwTabFrame *pTable; + for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if( !pTable->IsFollow() ) + { + SwRowFrame* pSibling = nullptr; + SwLayoutFrame *pUpperFrame = nullptr; + if ( bBehind ) + { + if ( m_pLineBehind ) + { + SwIterator<SwRowFrame,SwFormat> aIter( *m_pLineBehind->GetFrameFormat() ); + pSibling = aIter.First(); + while ( pSibling && ( + // only consider row frames associated with pLineBehind: + pSibling->GetTabLine() != m_pLineBehind || + // only consider row frames that are in pTables Master-Follow chain: + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + // only consider row frames that are not repeated headlines: + pSibling->IsRepeatedHeadline() || + // only consider row frames that are not follow flow rows + pSibling->IsInFollowFlowRow() ) ) + { + pSibling = aIter.Next(); + } + } + if ( pSibling ) + pUpperFrame = pSibling->GetUpper(); + else + { + while( pTable->GetFollow() ) + pTable = pTable->GetFollow(); + pUpperFrame = pTable; + } + const sal_uInt16 nMax = nBhPos != USHRT_MAX ? + nBhPos : rTable.GetTabLines().size(); + + sal_uInt16 i = nBfPos != USHRT_MAX ? nBfPos + 1 + nCnt : nCnt; + + for ( ; i < nMax; ++i ) + ::lcl_InsertRow( *rTable.GetTabLines()[i], pUpperFrame, pSibling ); + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + else // insert before + { + sal_uInt16 i; + + // We are looking for the frame that is behind the row frame + // that should be inserted. + for ( i = 0; !pSibling; ++i ) + { + SwTableLine* pLine = m_pLineBefore ? m_pLineBefore : rTable.GetTabLines()[i]; + + SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() ); + pSibling = aIter.First(); + + while ( pSibling && ( + // only consider row frames associated with pLineBefore: + pSibling->GetTabLine() != pLine || + // only consider row frames that are in pTables Master-Follow chain: + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + // only consider row frames that are not repeated headlines: + pSibling->IsRepeatedHeadline() || + // 1. case: pLineBefore == 0: + // only consider row frames that are not follow flow rows + // 2. case: pLineBefore != 0: + // only consider row frames that are not split table rows + // #i37476# If !pLineBefore, + // check IsInFollowFlowRow instead of IsInSplitTableRow. + ( ( !m_pLineBefore && pSibling->IsInFollowFlowRow() ) || + ( m_pLineBefore && pSibling->IsInSplitTableRow() ) ) ) ) + { + pSibling = aIter.Next(); + } + } + + pUpperFrame = pSibling->GetUpper(); + if ( m_pLineBefore ) + pSibling = static_cast<SwRowFrame*>( pSibling->GetNext() ); + + sal_uInt16 nMax = nBhPos != USHRT_MAX ? + nBhPos - nCnt : + rTable.GetTabLines().size() - nCnt; + + i = nBfPos != USHRT_MAX ? nBfPos + 1 : 0; + for ( ; i < nMax; ++i ) + ::lcl_InsertRow( *rTable.GetTabLines()[i], + pUpperFrame, pSibling ); + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + } + } + + // If necessary headlines should be processed. In order to + // not to fragment good code, we iterate once more. + const sal_uInt16 nRowsToRepeat = rTable.GetRowsToRepeat(); + if ( !(nRowsToRepeat > 0 && + ( ( !bBehind && ( nBfPos == USHRT_MAX || nBfPos + 1 < nRowsToRepeat ) ) || + ( bBehind && ( ( nBfPos == USHRT_MAX && nRowsToRepeat > 1 ) || nBfPos + 2 < nRowsToRepeat ) ) )) ) + return; + + for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if ( pTable->Lower() ) + { + if ( pTable->IsFollow() ) + { + lcl_UpdateRepeatedHeadlines( *pTable, true ); + } + + OSL_ENSURE( static_cast<SwRowFrame*>(pTable->Lower())->GetTabLine() == + rTable.GetTabLines()[0], "MakeNewFrames: Table corruption!" ); + } + } +} + +bool FndBox_::AreLinesToRestore( const SwTable &rTable ) const +{ + // Should we call MakeFrames here? + + if ( !m_pLineBefore && !m_pLineBehind && !rTable.GetTabLines().empty() ) + return true; + + sal_uInt16 nBfPos; + if(m_pLineBefore) + { + const SwTableLine* rLBefore = const_cast<const SwTableLine*>(m_pLineBefore); + nBfPos = rTable.GetTabLines().GetPos( rLBefore ); + } + else + nBfPos = USHRT_MAX; + + sal_uInt16 nBhPos; + if(m_pLineBehind) + { + const SwTableLine* rLBehind = const_cast<const SwTableLine*>(m_pLineBehind); + nBhPos = rTable.GetTabLines().GetPos( rLBehind ); + } + else + nBhPos = USHRT_MAX; + + if ( nBfPos == nBhPos ) // Should never occur. + { + OSL_FAIL( "Table, erase but not on any area !?!" ); + return false; + } + + if ( rTable.GetRowsToRepeat() > 0 ) + { + // oops: should the repeated headline have been deleted?? + SwIterator<SwTabFrame,SwFormat> aIter( *rTable.GetFrameFormat() ); + for( SwTabFrame* pTable = aIter.First(); pTable; pTable = aIter.Next() ) + { + if( pTable->IsFollow() ) + { + // Insert new headlines: + lcl_UpdateRepeatedHeadlines( *pTable, false ); + } + } + } + + // Some adjacent lines at the beginning of the table have been deleted: + if ( nBfPos == USHRT_MAX && nBhPos == 0 ) + return false; + + // Some adjacent lines at the end of the table have been deleted: + if ( nBhPos == USHRT_MAX && nBfPos == (rTable.GetTabLines().size() - 1) ) + return false; + + // Some adjacent lines in the middle of the table have been deleted: + if ( nBfPos != USHRT_MAX && nBhPos != USHRT_MAX && (nBfPos + 1) == nBhPos ) + return false; + + // The structure of the deleted lines is more complex due to split lines. + // A call of MakeFrames() is necessary. + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |