diff options
Diffstat (limited to 'sw/source/core/doc/tblrwcl.cxx')
-rw-r--r-- | sw/source/core/doc/tblrwcl.cxx | 3387 |
1 files changed, 3387 insertions, 0 deletions
diff --git a/sw/source/core/doc/tblrwcl.cxx b/sw/source/core/doc/tblrwcl.cxx new file mode 100644 index 000000000..b169b9d83 --- /dev/null +++ b/sw/source/core/doc/tblrwcl.cxx @@ -0,0 +1,3387 @@ +/* -*- 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 <memory> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <osl/diagnose.h> +#include <svl/numformat.hxx> +#include <hintids.hxx> + +#include <editeng/lrspitem.hxx> +#include <editeng/boxitem.hxx> +#include <tools/fract.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <doc.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentChartDataProviderAccess.hxx> +#include <DocumentContentOperationsManager.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentFieldsAccess.hxx> +#include <docsh.hxx> +#include <fesh.hxx> +#include <tabfrm.hxx> +#include <frmatr.hxx> +#include <frmtool.hxx> +#include <pam.hxx> +#include <swtable.hxx> +#include <tblsel.hxx> +#include <fldbas.hxx> +#include <rowfrm.hxx> +#include <ddefld.hxx> +#include <hints.hxx> +#include <UndoTable.hxx> +#include <cellatr.hxx> +#include <mvsave.hxx> +#include <swtblfmt.hxx> +#include <swddetbl.hxx> +#include <poolfmt.hxx> +#include <tblrwcl.hxx> +#include <unochart.hxx> +#include <o3tl/numeric.hxx> +#include <calbck.hxx> +#include <docary.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::uno; + +#define COLFUZZY 20 +#define ROWFUZZY 10 + +#ifdef DBG_UTIL +#define CHECK_TABLE(t) (t).CheckConsistency(); +#else +#define CHECK_TABLE(t) +#endif + +namespace { + +// In order to set the Frame Formats for the Boxes, it's enough to look +// up the current one in the array. If it's already there return the new one. +struct CpyTabFrame +{ + SwFrameFormat* pFrameFormat; + SwTableBoxFormat *pNewFrameFormat; + + explicit CpyTabFrame(SwFrameFormat* pCurrentFrameFormat) : pNewFrameFormat( nullptr ) + { pFrameFormat = pCurrentFrameFormat; } + + bool operator==( const CpyTabFrame& rCpyTabFrame ) const + { return pFrameFormat == rCpyTabFrame.pFrameFormat; } + bool operator<( const CpyTabFrame& rCpyTabFrame ) const + { return pFrameFormat < rCpyTabFrame.pFrameFormat; } +}; + +struct CR_SetBoxWidth +{ + SwShareBoxFormats aShareFormats; + SwTableNode* pTableNd; + SwTwips nDiff, nSide, nMaxSize, nLowerDiff; + TableChgMode nMode; + bool bBigger, bLeft; + + CR_SetBoxWidth( TableChgWidthHeightType eType, SwTwips nDif, SwTwips nSid, + SwTwips nMax, SwTableNode* pTNd ) + : pTableNd( pTNd ), + nDiff( nDif ), nSide( nSid ), nMaxSize( nMax ), nLowerDiff( 0 ) + { + bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) || + TableChgWidthHeightType::CellLeft == extractPosition( eType ); + bBigger = bool(eType & TableChgWidthHeightType::BiggerMode ); + nMode = pTableNd->GetTable().GetTableChgMode(); + } + CR_SetBoxWidth( const CR_SetBoxWidth& rCpy ) + : pTableNd( rCpy.pTableNd ), + nDiff( rCpy.nDiff ), nSide( rCpy.nSide ), + nMaxSize( rCpy.nMaxSize ), nLowerDiff( 0 ), + nMode( rCpy.nMode ), + bBigger( rCpy.bBigger ), bLeft( rCpy.bLeft ) + { + } + + void LoopClear() + { + nLowerDiff = 0; + } +}; + +} + +static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam, + SwTwips nDist, bool bCheck ); +static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam, + SwTwips nDist, bool bCheck ); + +typedef bool (*FN_lcl_SetBoxWidth)(SwTableLine*, CR_SetBoxWidth&, SwTwips, bool ); + +#ifdef DBG_UTIL + +#define CHECKBOXWIDTH \ + { \ + SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth(); \ + for (size_t nTmp = 0; nTmp < m_aLines.size(); ++nTmp) \ + ::CheckBoxWidth( *m_aLines[ nTmp ], nSize ); \ + } + +#define CHECKTABLELAYOUT \ + { \ + for ( size_t i = 0; i < GetTabLines().size(); ++i ) \ + { \ + SwFrameFormat* pFormat = GetTabLines()[i]->GetFrameFormat(); \ + SwIterator<SwRowFrame,SwFormat> aIter( *pFormat ); \ + for (SwRowFrame* pFrame=aIter.First(); pFrame; pFrame=aIter.Next())\ + { \ + if ( pFrame->GetTabLine() == GetTabLines()[i] ) \ + { \ + OSL_ENSURE( pFrame->GetUpper()->IsTabFrame(), \ + "Table layout does not match table structure" ); \ + } \ + } \ + } \ + } + +#else + +#define CHECKBOXWIDTH +#define CHECKTABLELAYOUT + +#endif // DBG_UTIL + +namespace { + +struct CR_SetLineHeight +{ + SwTableNode* pTableNd; + SwTwips nMaxSpace, nMaxHeight; + TableChgMode nMode; + bool bBigger; + + CR_SetLineHeight( TableChgWidthHeightType eType, SwTableNode* pTNd ) + : pTableNd( pTNd ), + nMaxSpace( 0 ), nMaxHeight( 0 ) + { + bBigger = bool(eType & TableChgWidthHeightType::BiggerMode ); + nMode = pTableNd->GetTable().GetTableChgMode(); + } + CR_SetLineHeight( const CR_SetLineHeight& rCpy ) + : pTableNd( rCpy.pTableNd ), + nMaxSpace( rCpy.nMaxSpace ), nMaxHeight( rCpy.nMaxHeight ), + nMode( rCpy.nMode ), + bBigger( rCpy.bBigger ) + {} +}; + +} + +static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam, + SwTwips nDist, bool bCheck ); +static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam, + SwTwips nDist, bool bCheck ); + +typedef bool (*FN_lcl_SetLineHeight)(SwTableLine*, CR_SetLineHeight&, SwTwips, bool ); + +typedef o3tl::sorted_vector<CpyTabFrame> CpyTabFrames; + +namespace { + +struct CpyPara +{ + std::shared_ptr< std::vector< std::vector< sal_uLong > > > pWidths; + SwDoc& rDoc; + SwTableNode* pTableNd; + CpyTabFrames& rTabFrameArr; + SwTableLine* pInsLine; + SwTableBox* pInsBox; + sal_uLong nOldSize, nNewSize; // in order to correct the size attributes + sal_uLong nMinLeft, nMaxRight; + sal_uInt16 nCpyCnt, nInsPos; + sal_uInt16 nLnIdx, nBoxIdx; + sal_uInt8 nDelBorderFlag; + bool bCpyContent; + + CpyPara( SwTableNode* pNd, sal_uInt16 nCopies, CpyTabFrames& rFrameArr ) + : rDoc( pNd->GetDoc() ), pTableNd( pNd ), rTabFrameArr(rFrameArr), + pInsLine(nullptr), pInsBox(nullptr), nOldSize(0), nNewSize(0), + nMinLeft(ULONG_MAX), nMaxRight(0), + nCpyCnt(nCopies), nInsPos(0), + nLnIdx(0), nBoxIdx(0), + nDelBorderFlag(0), bCpyContent( true ) + {} + CpyPara( const CpyPara& rPara, SwTableLine* pLine ) + : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd), + rTabFrameArr(rPara.rTabFrameArr), pInsLine(pLine), pInsBox(rPara.pInsBox), + nOldSize(0), nNewSize(rPara.nNewSize), nMinLeft( rPara.nMinLeft ), + nMaxRight( rPara.nMaxRight ), nCpyCnt(rPara.nCpyCnt), nInsPos(0), + nLnIdx( rPara.nLnIdx), nBoxIdx( rPara.nBoxIdx ), + nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent ) + {} + CpyPara( const CpyPara& rPara, SwTableBox* pBox ) + : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd), + rTabFrameArr(rPara.rTabFrameArr), pInsLine(rPara.pInsLine), pInsBox(pBox), + nOldSize(rPara.nOldSize), nNewSize(rPara.nNewSize), + nMinLeft( rPara.nMinLeft ), nMaxRight( rPara.nMaxRight ), + nCpyCnt(rPara.nCpyCnt), nInsPos(0), nLnIdx(rPara.nLnIdx), nBoxIdx(rPara.nBoxIdx), + nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent ) + {} +}; + +} + +static SwTableLine* lcl_CopyRow(FndLine_ & rFndLine, CpyPara *const pCpyPara); + +static void lcl_CopyCol( FndBox_ & rFndBox, CpyPara *const pCpyPara) +{ + // Look up the Frame Format in the Frame Format Array + SwTableBox* pBox = rFndBox.GetBox(); + CpyTabFrame aFindFrame(pBox->GetFrameFormat()); + + if( pCpyPara->nCpyCnt ) + { + sal_uInt16 nFndPos; + CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame ); + nFndPos = itFind - pCpyPara->rTabFrameArr.begin(); + if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) ) + { + // For nested copying, also save the new Format as an old one. + SwTableBoxFormat* pNewFormat = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()); + + // Find the selected Boxes in the Line: + FndLine_ const* pCmpLine = nullptr; + SwFormatFrameSize aFrameSz( pNewFormat->GetFrameSize() ); + + bool bDiffCount = false; + if( !pBox->GetTabLines().empty() ) + { + pCmpLine = rFndBox.GetLines().front().get(); + if ( pCmpLine->GetBoxes().size() != pCmpLine->GetLine()->GetTabBoxes().size() ) + bDiffCount = true; + } + + if( bDiffCount ) + { + // The first Line should be enough + FndBoxes_t const& rFndBoxes = pCmpLine->GetBoxes(); + tools::Long nSz = 0; + for( auto n = rFndBoxes.size(); n; ) + { + nSz += rFndBoxes[--n]->GetBox()-> + GetFrameFormat()->GetFrameSize().GetWidth(); + } + aFrameSz.SetWidth( aFrameSz.GetWidth() - + nSz / ( pCpyPara->nCpyCnt + 1 ) ); + pNewFormat->SetFormatAttr( aFrameSz ); + aFrameSz.SetWidth( nSz / ( pCpyPara->nCpyCnt + 1 ) ); + + // Create a new Format for the new Box, specifying its size. + aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pNewFormat->GetDoc()-> + MakeTableLineFormat()); + *aFindFrame.pNewFrameFormat = *pNewFormat; + aFindFrame.pNewFrameFormat->SetFormatAttr( aFrameSz ); + } + else + { + aFrameSz.SetWidth( aFrameSz.GetWidth() / ( pCpyPara->nCpyCnt + 1 ) ); + pNewFormat->SetFormatAttr( aFrameSz ); + + aFindFrame.pNewFrameFormat = pNewFormat; + pCpyPara->rTabFrameArr.insert( aFindFrame ); + aFindFrame.pFrameFormat = pNewFormat; + pCpyPara->rTabFrameArr.insert( aFindFrame ); + } + } + else + { + aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]; + pBox->ChgFrameFormat( aFindFrame.pNewFrameFormat ); + } + } + else + { + CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame ); + if( pCpyPara->nDelBorderFlag && + itFind != pCpyPara->rTabFrameArr.end() ) + aFindFrame = *itFind; + else + aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()); + } + + if (!rFndBox.GetLines().empty()) + { + pBox = new SwTableBox( aFindFrame.pNewFrameFormat, + rFndBox.GetLines().size(), pCpyPara->pInsLine ); + pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox ); + CpyPara aPara( *pCpyPara, pBox ); + aPara.nDelBorderFlag &= 7; + + for (auto const& pFndLine : rFndBox.GetLines()) + { + lcl_CopyRow(*pFndLine, &aPara); + } + } + else + { + ::InsTableBox( pCpyPara->rDoc, pCpyPara->pTableNd, pCpyPara->pInsLine, + aFindFrame.pNewFrameFormat, pBox, pCpyPara->nInsPos++ ); + + const FndBoxes_t& rFndBxs = rFndBox.GetUpper()->GetBoxes(); + if( 8 > pCpyPara->nDelBorderFlag + ? pCpyPara->nDelBorderFlag != 0 + : &rFndBox == rFndBxs[rFndBxs.size() - 1].get()) + { + const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox(); + if( 8 > pCpyPara->nDelBorderFlag + ? rBoxItem.GetTop() + : rBoxItem.GetRight() ) + { + aFindFrame.pFrameFormat = pBox->GetFrameFormat(); + + SvxBoxItem aNew( rBoxItem ); + if( 8 > pCpyPara->nDelBorderFlag ) + aNew.SetLine( nullptr, SvxBoxItemLine::TOP ); + else + aNew.SetLine( nullptr, SvxBoxItemLine::RIGHT ); + + if( 1 == pCpyPara->nDelBorderFlag || + 8 == pCpyPara->nDelBorderFlag ) + { + // For all Boxes that delete TopBorderLine, we copy after that + pBox = pCpyPara->pInsLine->GetTabBoxes()[ + pCpyPara->nInsPos - 1 ]; + } + + aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()); + + // Else we copy before that and the first Line keeps the TopLine + // and we remove it at the original + pBox->ClaimFrameFormat()->SetFormatAttr( aNew ); + + if( !pCpyPara->nCpyCnt ) + pCpyPara->rTabFrameArr.insert( aFindFrame ); + } + } + } +} + +static SwTableLine* lcl_CopyRow(FndLine_& rFndLine, CpyPara *const pCpyPara) +{ + SwTableLine* pNewLine = new SwTableLine( + static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()), + rFndLine.GetBoxes().size(), pCpyPara->pInsBox ); + if( pCpyPara->pInsBox ) + { + SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines(); + rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine ); + } + else + { + SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines(); + rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine ); + } + + CpyPara aPara( *pCpyPara, pNewLine ); + for (auto const& it : rFndLine.GetBoxes()) + { + lcl_CopyCol(*it, &aPara); + } + + pCpyPara->nDelBorderFlag &= 0xf8; + + return pNewLine; +} + +static void lcl_InsCol( FndLine_* pFndLn, CpyPara& rCpyPara, sal_uInt16 nCpyCnt, + bool bBehind ) +{ + // Bug 29124: Not only copy in the BaseLines. If possible, we go down as far as possible + FndBox_* pFBox; + if( 1 == pFndLn->GetBoxes().size() && + !( pFBox = pFndLn->GetBoxes()[0].get() )->GetBox()->GetSttNd() ) + { + // A Box with multiple Lines, so insert into these Lines + for (auto &rpLine : pFBox->GetLines()) + { + lcl_InsCol( rpLine.get(), rCpyPara, nCpyCnt, bBehind ); + } + } + else + { + rCpyPara.pInsLine = pFndLn->GetLine(); + SwTableBox* pBox = pFndLn->GetBoxes()[ bBehind ? + pFndLn->GetBoxes().size()-1 : 0 ]->GetBox(); + rCpyPara.nInsPos = pFndLn->GetLine()->GetBoxPos( pBox ); + if( bBehind ) + ++rCpyPara.nInsPos; + + for( sal_uInt16 n = 0; n < nCpyCnt; ++n ) + { + if( n + 1 == nCpyCnt && bBehind ) + rCpyPara.nDelBorderFlag = 9; + else + rCpyPara.nDelBorderFlag = 8; + for (auto const& it : pFndLn->GetBoxes()) + { + lcl_CopyCol(*it, &rCpyPara); + } + } + } +} + +static SwRowFrame* GetRowFrame( SwTableLine& rLine ) +{ + SwIterator<SwRowFrame,SwFormat> aIter( *rLine.GetFrameFormat() ); + for( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) + if( pFrame->GetTabLine() == &rLine ) + return pFrame; + return nullptr; +} + +bool SwTable::InsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind ) +{ + OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box List" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + bool bRes = true; + if( IsNewModel() ) + bRes = NewInsertCol( rDoc, rBoxes, nCnt, bBehind ); + else + { + // Find all Boxes/Lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( rBoxes, &aFndBox ); + ForEach_FndLineCopyCol( GetTabLines(), &aPara ); + } + if( aFndBox.GetLines().empty() ) + return false; + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + // Find Lines for the layout update + aFndBox.SetTableLines( *this ); + aFndBox.DelFrames( *this ); + + // TL_CHART2: nothing to be done since chart2 currently does not want to + // get notified about new rows/cols. + + CpyTabFrames aTabFrameArr; + CpyPara aCpyPara( pTableNd, nCnt, aTabFrameArr ); + + for (auto & rpLine : aFndBox.GetLines()) + { + lcl_InsCol( rpLine.get(), aCpyPara, nCnt, bBehind ); + } + + // clean up this Line's structure once again, generally all of them + GCLines(); + + // Update Layout + aFndBox.MakeFrames( *this ); + + CHECKBOXWIDTH; + CHECKTABLELAYOUT; + bRes = true; + } + + SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD && nCnt) + pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind ); + rDoc.UpdateCharts( GetFrameFormat()->GetName() ); + + if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell()) + pFEShell->UpdateTableStyleFormatting(); + + return bRes; +} + +bool SwTable::InsertRow_( SwDoc* pDoc, const SwSelBoxes& rBoxes, + sal_uInt16 nCnt, bool bBehind ) +{ + OSL_ENSURE( pDoc && !rBoxes.empty() && nCnt, "No valid Box List" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + // Find all Boxes/Lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( rBoxes, &aFndBox ); + ForEach_FndLineCopyCol( GetTabLines(), &aPara ); + } + if( aFndBox.GetLines().empty() ) + return false; + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + FndBox_* pFndBox = &aFndBox; + { + FndLine_* pFndLine; + while( 1 == pFndBox->GetLines().size() ) + { + pFndLine = pFndBox->GetLines()[0].get(); + if( 1 != pFndLine->GetBoxes().size() ) + break; + // Don't go down too far! One Line with Box needs to remain! + FndBox_ *const pTmpBox = pFndLine->GetBoxes().front().get(); + if( !pTmpBox->GetLines().empty() ) + pFndBox = pTmpBox; + else + break; + } + } + + // Find Lines for the layout update + const bool bLayout = !IsNewModel() && + nullptr != SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First(); + + if ( bLayout ) + { + aFndBox.SetTableLines( *this ); + if( pFndBox != &aFndBox ) + aFndBox.DelFrames( *this ); + // TL_CHART2: nothing to be done since chart2 currently does not want to + // get notified about new rows/cols. + } + + CpyTabFrames aTabFrameArr; + CpyPara aCpyPara( pTableNd, 0, aTabFrameArr ); + + SwTableLine* pLine = pFndBox->GetLines()[ bBehind ? + pFndBox->GetLines().size()-1 : 0 ]->GetLine(); + if( &aFndBox == pFndBox ) + aCpyPara.nInsPos = GetTabLines().GetPos( pLine ); + else + { + aCpyPara.pInsBox = pFndBox->GetBox(); + aCpyPara.nInsPos = pFndBox->GetBox()->GetTabLines().GetPos( pLine ); + } + + if( bBehind ) + { + ++aCpyPara.nInsPos; + aCpyPara.nDelBorderFlag = 1; + } + else + aCpyPara.nDelBorderFlag = 2; + + for( sal_uInt16 nCpyCnt = 0; nCpyCnt < nCnt; ++nCpyCnt ) + { + if( bBehind ) + aCpyPara.nDelBorderFlag = 1; + for (auto & rpFndLine : pFndBox->GetLines()) + { + SwTableLine* pNewTableLine = lcl_CopyRow( *rpFndLine, &aCpyPara ); + + // tracked insertion of empty table line + if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() ) + { + SvxPrintItem aSetTracking(RES_PRINT, false); + SwPosition aPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd()); + SwCursor aCursor( aPos, nullptr ); + SwNodeIndex aInsPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd(), 1 ); + SwPaM aPaM(aInsPos); + pDoc->getIDocumentContentOperations().InsertString( aPaM, + OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) ); + pDoc->SetRowNotTracked( aCursor, aSetTracking, /*bAll=*/false, /*bIns=*/true ); + } + } + } + + // clean up this Line's structure once again, generally all of them + if( !pDoc->IsInReading() ) + GCLines(); + + // Update Layout + if ( bLayout ) + { + if( pFndBox != &aFndBox ) + aFndBox.MakeFrames( *this ); + else + aFndBox.MakeNewFrames( *this, nCnt, bBehind ); + } + + CHECKBOXWIDTH; + CHECKTABLELAYOUT; + + SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + if (pPCD && nCnt) + pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind ); + pDoc->UpdateCharts( GetFrameFormat()->GetName() ); + + if (SwFEShell* pFEShell = pDoc->GetDocShell()->GetFEShell()) + pFEShell->UpdateTableStyleFormatting(pTableNd); + + return true; +} + +static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset, + bool bFirst, SwShareBoxFormats& rShareFormats ); + +static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const tools::Long nOffset, + bool bFirst, SwShareBoxFormats& rShareFormats ) +{ + for ( auto pLine : rLines ) + ::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats ); +} + +static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset, + bool bFirst, SwShareBoxFormats& rShareFormats ) +{ + SwTableBox& rBox = *(bFirst ? rBoxes.front() : rBoxes.back()); + if( !rBox.GetSttNd() ) + ::lcl_LastBoxSetWidthLine( rBox.GetTabLines(), nOffset, + bFirst, rShareFormats ); + + // Adapt the Box + const SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(); + SwFormatFrameSize aNew( pBoxFormat->GetFrameSize() ); + aNew.SetWidth( aNew.GetWidth() + nOffset ); + SwFrameFormat *pFormat = rShareFormats.GetFormat( *pBoxFormat, aNew ); + if( pFormat ) + rBox.ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFormat) ); + else + { + pFormat = rBox.ClaimFrameFormat(); + + pFormat->LockModify(); + pFormat->SetFormatAttr( aNew ); + pFormat->UnlockModify(); + + rShareFormats.AddFormat( *pBoxFormat, *pFormat ); + } +} + +void DeleteBox_( SwTable& rTable, SwTableBox* pBox, SwUndo* pUndo, + bool bCalcNewSize, const bool bCorrBorder, + SwShareBoxFormats* pShareFormats ) +{ + do { + SwTwips nBoxSz = bCalcNewSize ? + pBox->GetFrameFormat()->GetFrameSize().GetWidth() : 0; + SwTableLine* pLine = pBox->GetUpper(); + SwTableBoxes& rTableBoxes = pLine->GetTabBoxes(); + sal_uInt16 nDelPos = pLine->GetBoxPos( pBox ); + SwTableBox* pUpperBox = pBox->GetUpper()->GetUpper(); + + // Special treatment for the border: + if( bCorrBorder && 1 < rTableBoxes.size() ) + { + const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox(); + + if( rBoxItem.GetLeft() || rBoxItem.GetRight() ) + { + bool bChgd = false; + + // JP 02.04.97: 1st part for Bug 36271 + // First the left/right edges + if( nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) ) + { + SwTableBox* pNxtBox = rTableBoxes[ nDelPos + 1 ]; + const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox(); + + SwTableBox* pPrvBox = nDelPos ? rTableBoxes[ nDelPos - 1 ] : nullptr; + + if( pNxtBox->GetSttNd() && !rNxtBoxItem.GetLeft() && + ( !pPrvBox || !pPrvBox->GetFrameFormat()->GetBox().GetRight()) ) + { + SvxBoxItem aTmp( rNxtBoxItem ); + aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft() + : rBoxItem.GetRight(), + SvxBoxItemLine::LEFT ); + if( pShareFormats ) + pShareFormats->SetAttr( *pNxtBox, aTmp ); + else + pNxtBox->ClaimFrameFormat()->SetFormatAttr( aTmp ); + bChgd = true; + } + } + if( !bChgd && nDelPos ) + { + SwTableBox* pPrvBox = rTableBoxes[ nDelPos - 1 ]; + const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox(); + + SwTableBox* pNxtBox = nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) + ? rTableBoxes[ nDelPos + 1 ] : nullptr; + + if( pPrvBox->GetSttNd() && !rPrvBoxItem.GetRight() && + ( !pNxtBox || !pNxtBox->GetFrameFormat()->GetBox().GetLeft()) ) + { + SvxBoxItem aTmp( rPrvBoxItem ); + aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft() + : rBoxItem.GetRight(), + SvxBoxItemLine::RIGHT ); + if( pShareFormats ) + pShareFormats->SetAttr( *pPrvBox, aTmp ); + else + pPrvBox->ClaimFrameFormat()->SetFormatAttr( aTmp ); + } + } + } + } + + // Delete the Box first, then the Nodes! + SwStartNode* pSttNd = const_cast<SwStartNode*>(pBox->GetSttNd()); + if( pShareFormats ) + pShareFormats->RemoveFormat( *rTableBoxes[ nDelPos ]->GetFrameFormat() ); + + // Before deleting the 'Table Box' from memory - delete any redlines attached to it + rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableCellRedline( rTable.GetFrameFormat()->GetDoc(), *(rTableBoxes[nDelPos]), true, RedlineType::Any ); + delete rTableBoxes[nDelPos]; + rTableBoxes.erase( rTableBoxes.begin() + nDelPos ); + + if( pSttNd ) + { + // Has the UndoObject been prepared to save the Section? + if( pUndo && pUndo->IsDelBox() ) + static_cast<SwUndoTableNdsChg*>(pUndo)->SaveSection( pSttNd ); + else + pSttNd->GetDoc().getIDocumentContentOperations().DeleteSection( pSttNd ); + } + + // Also delete the Line? + if( !rTableBoxes.empty() ) + { + // Then adapt the Frame-SSize + bool bLastBox = nDelPos == rTableBoxes.size(); + if( bLastBox ) + --nDelPos; + pBox = rTableBoxes[nDelPos]; + if( bCalcNewSize ) + { + SwFormatFrameSize aNew( pBox->GetFrameFormat()->GetFrameSize() ); + aNew.SetWidth( aNew.GetWidth() + nBoxSz ); + if( pShareFormats ) + pShareFormats->SetSize( *pBox, aNew ); + else + pBox->ClaimFrameFormat()->SetFormatAttr( aNew ); + + if( !pBox->GetSttNd() ) + { + // We need to this recursively in all Lines in all Cells! + SwShareBoxFormats aShareFormats; + ::lcl_LastBoxSetWidthLine( pBox->GetTabLines(), nBoxSz, + !bLastBox, + pShareFormats ? *pShareFormats + : aShareFormats ); + } + } + break; // Stop deleting + } + // Delete the Line from the Table/Box + if( !pUpperBox ) + { + // Also delete the Line from the Table + nDelPos = rTable.GetTabLines().GetPos( pLine ); + if( pShareFormats ) + pShareFormats->RemoveFormat( *rTable.GetTabLines()[ nDelPos ]->GetFrameFormat() ); + + SwTableLine* pTabLineToDelete = rTable.GetTabLines()[ nDelPos ]; + // Before deleting the 'Table Line' from memory - delete any redlines attached to it + rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any ); + delete pTabLineToDelete; + rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nDelPos ); + break; // we cannot delete more + } + + // finally also delete the Line + pBox = pUpperBox; + nDelPos = pBox->GetTabLines().GetPos( pLine ); + if( pShareFormats ) + pShareFormats->RemoveFormat( *pBox->GetTabLines()[ nDelPos ]->GetFrameFormat() ); + + SwTableLine* pTabLineToDelete = pBox->GetTabLines()[ nDelPos ]; + // Before deleting the 'Table Line' from memory - delete any redlines attached to it + rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any ); + delete pTabLineToDelete; + pBox->GetTabLines().erase( pBox->GetTabLines().begin() + nDelPos ); + } while( pBox->GetTabLines().empty() ); +} + +static SwTableBox* +lcl_FndNxtPrvDelBox( const SwTableLines& rTableLns, + SwTwips nBoxStt, SwTwips nBoxWidth, + sal_uInt16 nLinePos, bool bNxt, + SwSelBoxes* pAllDelBoxes, size_t *const pCurPos) +{ + SwTableBox* pFndBox = nullptr; + do { + if( bNxt ) + ++nLinePos; + else + --nLinePos; + SwTableLine* pLine = rTableLns[ nLinePos ]; + SwTwips nFndBoxWidth = 0; + SwTwips nFndWidth = nBoxStt + nBoxWidth; + + pFndBox = pLine->GetTabBoxes()[ 0 ]; + for( auto pBox : pLine->GetTabBoxes() ) + { + if ( nFndWidth <= 0 ) + { + break; + } + pFndBox = pBox; + nFndBoxWidth = pFndBox->GetFrameFormat()->GetFrameSize().GetWidth(); + nFndWidth -= nFndBoxWidth; + } + + // Find the first ContentBox + while( !pFndBox->GetSttNd() ) + { + const SwTableLines& rLowLns = pFndBox->GetTabLines(); + if( bNxt ) + pFndBox = rLowLns.front()->GetTabBoxes().front(); + else + pFndBox = rLowLns.back()->GetTabBoxes().front(); + } + + if( std::abs( nFndWidth ) > COLFUZZY || + std::abs( nBoxWidth - nFndBoxWidth ) > COLFUZZY ) + pFndBox = nullptr; + else if( pAllDelBoxes ) + { + // If the predecessor will also be deleted, there's nothing to do + SwSelBoxes::const_iterator aFndIt = pAllDelBoxes->find( pFndBox); + if( aFndIt == pAllDelBoxes->end() ) + break; + size_t const nFndPos = aFndIt - pAllDelBoxes->begin() ; + + // else, we keep on searching. + // We do not need to recheck the Box, however + pFndBox = nullptr; + if( nFndPos <= *pCurPos ) + --*pCurPos; + pAllDelBoxes->erase( pAllDelBoxes->begin() + nFndPos ); + } + } while( bNxt ? ( nLinePos + 1 < o3tl::narrowing<sal_uInt16>(rTableLns.size()) ) : nLinePos != 0 ); + return pFndBox; +} + +static void +lcl_SaveUpperLowerBorder( SwTable& rTable, const SwTableBox& rBox, + SwShareBoxFormats& rShareFormats, + SwSelBoxes* pAllDelBoxes = nullptr, + size_t *const pCurPos = nullptr ) +{ +//JP 16.04.97: 2. part for Bug 36271 + const SwTableLine* pLine = rBox.GetUpper(); + const SwTableBoxes& rTableBoxes = pLine->GetTabBoxes(); + const SwTableBox* pUpperBox = &rBox; + sal_uInt16 nDelPos = pLine->GetBoxPos( pUpperBox ); + pUpperBox = rBox.GetUpper()->GetUpper(); + const SvxBoxItem& rBoxItem = rBox.GetFrameFormat()->GetBox(); + + // then the top/bottom edges + if( !rBoxItem.GetTop() && !rBoxItem.GetBottom() ) + return; + + bool bChgd = false; + const SwTableLines* pTableLns; + if( pUpperBox ) + pTableLns = &pUpperBox->GetTabLines(); + else + pTableLns = &rTable.GetTabLines(); + + sal_uInt16 nLnPos = pTableLns->GetPos( pLine ); + + // Calculate the attribute position of the top-be-deleted Box and then + // search in the top/bottom Line of the respective counterparts. + SwTwips nBoxStt = 0; + for( sal_uInt16 n = 0; n < nDelPos; ++n ) + nBoxStt += rTableBoxes[ n ]->GetFrameFormat()->GetFrameSize().GetWidth(); + SwTwips nBoxWidth = rBox.GetFrameFormat()->GetFrameSize().GetWidth(); + + SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr; + if( nLnPos ) // Predecessor? + pPrvBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth, + nLnPos, false, pAllDelBoxes, pCurPos ); + + if( nLnPos + 1 < o3tl::narrowing<sal_uInt16>(pTableLns->size()) ) // Successor? + pNxtBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth, + nLnPos, true, pAllDelBoxes, pCurPos ); + + if( pNxtBox && pNxtBox->GetSttNd() ) + { + const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox(); + if( !rNxtBoxItem.GetTop() && ( !pPrvBox || + !pPrvBox->GetFrameFormat()->GetBox().GetBottom()) ) + { + SvxBoxItem aTmp( rNxtBoxItem ); + aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop() + : rBoxItem.GetBottom(), + SvxBoxItemLine::TOP ); + rShareFormats.SetAttr( *pNxtBox, aTmp ); + bChgd = true; + } + } + if( !(!bChgd && pPrvBox && pPrvBox->GetSttNd()) ) + return; + + const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox(); + if( !rPrvBoxItem.GetTop() && ( !pNxtBox || + !pNxtBox->GetFrameFormat()->GetBox().GetTop()) ) + { + SvxBoxItem aTmp( rPrvBoxItem ); + aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop() + : rBoxItem.GetBottom(), + SvxBoxItemLine::BOTTOM ); + rShareFormats.SetAttr( *pPrvBox, aTmp ); + } + +} + +bool SwTable::DeleteSel( + SwDoc* pDoc + , + const SwSelBoxes& rBoxes, + const SwSelBoxes* pMerged, SwUndo* pUndo, + const bool bDelMakeFrames, const bool bCorrBorder ) +{ + OSL_ENSURE( pDoc, "No doc?" ); + SwTableNode* pTableNd = nullptr; + if( !rBoxes.empty() ) + { + pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + } + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + // Find Lines for the Layout update + FndBox_ aFndBox( nullptr, nullptr ); + if ( bDelMakeFrames ) + { + if( pMerged && !pMerged->empty() ) + aFndBox.SetTableLines( *pMerged, *this ); + else if( !rBoxes.empty() ) + aFndBox.SetTableLines( rBoxes, *this ); + aFndBox.DelFrames( *this ); + } + + SwShareBoxFormats aShareFormats; + + // First switch the Border, then delete + if( bCorrBorder ) + { + SwSelBoxes aBoxes( rBoxes ); + for (size_t n = 0; n < aBoxes.size(); ++n) + { + ::lcl_SaveUpperLowerBorder( *this, *rBoxes[ n ], aShareFormats, + &aBoxes, &n ); + } + } + + PrepareDelBoxes( rBoxes ); + + SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider(); + // Delete boxes from last to first + for (size_t n = 0; n < rBoxes.size(); ++n) + { + size_t const nIdx = rBoxes.size() - 1 - n; + + // First adapt the data-sequence for chart if necessary + // (needed to move the implementation cursor properly to its new + // position which can't be done properly if the cell is already gone) + if (pPCD && pTableNd) + pPCD->DeleteBox( &pTableNd->GetTable(), *rBoxes[nIdx] ); + + // ... then delete the boxes + DeleteBox_( *this, rBoxes[nIdx], pUndo, true, bCorrBorder, &aShareFormats ); + } + + // then clean up the structure of all Lines + GCLines(); + + if( bDelMakeFrames && aFndBox.AreLinesToRestore( *this ) ) + aFndBox.MakeFrames( *this ); + + // TL_CHART2: now inform chart that sth has changed + pDoc->UpdateCharts( GetFrameFormat()->GetName() ); + + CHECKTABLELAYOUT; + CHECK_TABLE( *this ); + + return true; +} + +bool SwTable::OldSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, + bool bSameHeight ) +{ + OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + // TL_CHART2: splitting/merging of a number of cells or rows will usually make + // the table too complex to be handled with chart. + // Thus we tell the charts to use their own data provider and forget about this table + rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this ); + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + // If the rows should get the same (min) height, we first have + // to store the old row heights before deleting the frames + std::unique_ptr<tools::Long[]> pRowHeights; + if ( bSameHeight ) + { + pRowHeights.reset(new tools::Long[ rBoxes.size() ]); + for (size_t n = 0; n < rBoxes.size(); ++n) + { + SwTableBox* pSelBox = rBoxes[n]; + const SwRowFrame* pRow = GetRowFrame( *pSelBox->GetUpper() ); + OSL_ENSURE( pRow, "Where is the SwTableLine's Frame?" ); + SwRectFnSet aRectFnSet(pRow); + pRowHeights[ n ] = aRectFnSet.GetHeight(pRow->getFrameArea()); + } + } + + // Find Lines for the Layout update + FndBox_ aFndBox( nullptr, nullptr ); + aFndBox.SetTableLines( rBoxes, *this ); + aFndBox.DelFrames( *this ); + + for (size_t n = 0; n < rBoxes.size(); ++n) + { + SwTableBox* pSelBox = rBoxes[n]; + OSL_ENSURE( pSelBox, "Box is not within the Table" ); + + // Insert nCnt new Lines into the Box + SwTableLine* pInsLine = pSelBox->GetUpper(); + SwTableBoxFormat* pFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat()); + + // Respect the Line's height, reset if needed + SwFormatFrameSize aFSz( pInsLine->GetFrameFormat()->GetFrameSize() ); + if ( bSameHeight && SwFrameSize::Variable == aFSz.GetHeightSizeType() ) + aFSz.SetHeightSizeType( SwFrameSize::Minimum ); + + bool bChgLineSz = 0 != aFSz.GetHeight() || bSameHeight; + if ( bChgLineSz ) + aFSz.SetHeight( ( bSameHeight ? pRowHeights[ n ] : aFSz.GetHeight() ) / + (nCnt + 1) ); + + SwTableBox* pNewBox = new SwTableBox( pFrameFormat, nCnt, pInsLine ); + sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox ); + pInsLine->GetTabBoxes()[nBoxPos] = pNewBox; // overwrite old one + + // Delete background/border attribute + SwTableBox* pLastBox = pSelBox; // To distribute the TextNodes! + // If Areas are contained in the Box, it stays as is + // !! If this is changed we need to adapt the Undo, too !!! + bool bMoveNodes = true; + { + SwNodeOffset nSttNd = pLastBox->GetSttIdx() + 1, + nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex(); + while( nSttNd < nEndNd ) + if( !rDoc.GetNodes()[ nSttNd++ ]->IsTextNode() ) + { + bMoveNodes = false; + break; + } + } + + SwTableBoxFormat* pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat()); + bool bChkBorder = nullptr != pCpyBoxFrameFormat->GetBox().GetTop(); + if( bChkBorder ) + pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat()); + + for( sal_uInt16 i = 0; i <= nCnt; ++i ) + { + // Create a new Line in the new Box + SwTableLine* pNewLine = new SwTableLine( + static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 1, pNewBox ); + if( bChgLineSz ) + { + pNewLine->ClaimFrameFormat()->SetFormatAttr( aFSz ); + } + + pNewBox->GetTabLines().insert( pNewBox->GetTabLines().begin() + i, pNewLine ); + // then a new Box in the Line + if( !i ) // hang up the original Box + { + pSelBox->SetUpper( pNewLine ); + pNewLine->GetTabBoxes().insert( pNewLine->GetTabBoxes().begin(), pSelBox ); + } + else + { + ::InsTableBox( rDoc, pTableNd, pNewLine, pCpyBoxFrameFormat, + pLastBox, 0 ); + + if( bChkBorder ) + { + pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pNewLine->GetTabBoxes()[ 0 ]->ClaimFrameFormat()); + SvxBoxItem aTmp( pCpyBoxFrameFormat->GetBox() ); + aTmp.SetLine( nullptr, SvxBoxItemLine::TOP ); + pCpyBoxFrameFormat->SetFormatAttr( aTmp ); + bChkBorder = false; + } + + if( bMoveNodes ) + { + const SwNode* pEndNd = pLastBox->GetSttNd()->EndOfSectionNode(); + if( pLastBox->GetSttIdx()+SwNodeOffset(2) != pEndNd->GetIndex() ) + { + // Move TextNodes + SwNodeRange aRg( *pLastBox->GetSttNd(), SwNodeOffset(+2), *pEndNd ); + pLastBox = pNewLine->GetTabBoxes()[0]; // reset + SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 ); + rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aInsPos, false); + rDoc.GetNodes().Delete( aInsPos ); // delete the empty one + } + } + } + } + // In Boxes with Lines, we can only have Size/Fillorder + pFrameFormat = static_cast<SwTableBoxFormat*>(pNewBox->ClaimFrameFormat()); + pFrameFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 ); + pFrameFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 ); + } + + pRowHeights.reset(); + + GCLines(); + + aFndBox.MakeFrames( *this ); + + CHECKBOXWIDTH + CHECKTABLELAYOUT + return true; +} + +bool SwTable::SplitCol(SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt) +{ + OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + // TL_CHART2: splitting/merging of a number of cells or rows will usually make + // the table too complex to be handled with chart. + // Thus we tell the charts to use their own data provider and forget about this table + rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this ); + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + SwSelBoxes aSelBoxes(rBoxes); + ExpandSelection( aSelBoxes ); + + // Find Lines for the Layout update + FndBox_ aFndBox( nullptr, nullptr ); + aFndBox.SetTableLines( aSelBoxes, *this ); + aFndBox.DelFrames( *this ); + + CpyTabFrames aFrameArr; + std::vector<SwTableBoxFormat*> aLastBoxArr; + for (size_t n = 0; n < aSelBoxes.size(); ++n) + { + SwTableBox* pSelBox = aSelBoxes[n]; + OSL_ENSURE( pSelBox, "Box is not in the table" ); + + // We don't want to split small table cells into very very small cells + if( pSelBox->GetFrameFormat()->GetFrameSize().GetWidth()/( nCnt + 1 ) < 10 ) + continue; + + // Then split the nCnt Box up into nCnt Boxes + SwTableLine* pInsLine = pSelBox->GetUpper(); + sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox ); + + // Find the Frame Format in the Frame Format Array + SwTableBoxFormat* pLastBoxFormat; + CpyTabFrame aFindFrame( static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat()) ); + CpyTabFrames::const_iterator itFind = aFrameArr.lower_bound( aFindFrame ); + const size_t nFndPos = itFind - aFrameArr.begin(); + if( itFind == aFrameArr.end() || !(*itFind == aFindFrame) ) + { + // Change the FrameFormat + aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat()); + SwTwips nBoxSz = aFindFrame.pNewFrameFormat->GetFrameSize().GetWidth(); + SwTwips nNewBoxSz = nBoxSz / ( nCnt + 1 ); + aFindFrame.pNewFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, + nNewBoxSz, 0 ) ); + aFrameArr.insert( aFindFrame ); + + pLastBoxFormat = aFindFrame.pNewFrameFormat; + if( nBoxSz != ( nNewBoxSz * (nCnt + 1))) + { + // We have a remainder, so we need to define an own Format + // for the last Box. + pLastBoxFormat = new SwTableBoxFormat( *aFindFrame.pNewFrameFormat ); + pLastBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, + nBoxSz - ( nNewBoxSz * nCnt ), 0 ) ); + } + aLastBoxArr.insert( aLastBoxArr.begin() + nFndPos, pLastBoxFormat ); + } + else + { + aFindFrame = aFrameArr[ nFndPos ]; + pSelBox->ChgFrameFormat( aFindFrame.pNewFrameFormat ); + pLastBoxFormat = aLastBoxArr[ nFndPos ]; + } + + // Insert the Boxes at the Position + for( sal_uInt16 i = 1; i < nCnt; ++i ) + ::InsTableBox( rDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat, + pSelBox, nBoxPos + i ); // insert after + + ::InsTableBox( rDoc, pTableNd, pInsLine, pLastBoxFormat, + pSelBox, nBoxPos + nCnt ); // insert after + + // Special treatment for the Border: + const SvxBoxItem& aSelBoxItem = aFindFrame.pNewFrameFormat->GetBox(); + if( aSelBoxItem.GetRight() ) + { + pInsLine->GetTabBoxes()[ nBoxPos + nCnt ]->ClaimFrameFormat(); + + SvxBoxItem aTmp( aSelBoxItem ); + aTmp.SetLine( nullptr, SvxBoxItemLine::RIGHT ); + aFindFrame.pNewFrameFormat->SetFormatAttr( aTmp ); + + // Remove the Format from the "cache" + for( auto i = aFrameArr.size(); i; ) + { + const CpyTabFrame& rCTF = aFrameArr[ --i ]; + if( rCTF.pNewFrameFormat == aFindFrame.pNewFrameFormat || + rCTF.pFrameFormat == aFindFrame.pNewFrameFormat ) + { + aFrameArr.erase( aFrameArr.begin() + i ); + aLastBoxArr.erase( aLastBoxArr.begin() + i ); + } + } + } + } + + // Update Layout + aFndBox.MakeFrames( *this ); + + CHECKBOXWIDTH + CHECKTABLELAYOUT + return true; +} + +/* + * >> MERGE << + * Algorithm: + * If we only have one Line in the FndBox_, take this Line and test + * the Box count: + * If we have more than one Box, we merge on Box level, meaning + * the new Box will be as wide as the old ones. + * All Lines that are above/under the Area, are inserted into + * the Box as Line + Box. + * All Lines that come before/after the Area, are inserted into + * the Boxes Left/Right. + * + * >> MERGE << + */ +static void lcl_CpyLines( sal_uInt16 nStt, sal_uInt16 nEnd, + SwTableLines& rLines, + SwTableBox* pInsBox, + sal_uInt16 nPos = USHRT_MAX ) +{ + for( sal_uInt16 n = nStt; n < nEnd; ++n ) + rLines[n]->SetUpper( pInsBox ); + if( USHRT_MAX == nPos ) + nPos = pInsBox->GetTabLines().size(); + pInsBox->GetTabLines().insert( pInsBox->GetTabLines().begin() + nPos, + rLines.begin() + nStt, rLines.begin() + nEnd ); + rLines.erase( rLines.begin() + nStt, rLines.begin() + nEnd ); +} + +static void lcl_CpyBoxes( sal_uInt16 nStt, sal_uInt16 nEnd, + SwTableBoxes& rBoxes, + SwTableLine* pInsLine ) +{ + for( sal_uInt16 n = nStt; n < nEnd; ++n ) + rBoxes[n]->SetUpper( pInsLine ); + sal_uInt16 nPos = pInsLine->GetTabBoxes().size(); + pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + nPos, + rBoxes.begin() + nStt, rBoxes.begin() + nEnd ); + rBoxes.erase( rBoxes.begin() + nStt, rBoxes.begin() + nEnd ); +} + +static void lcl_CalcWidth( SwTableBox* pBox ) +{ + // Assertion: Every Line in the Box is as large + SwFrameFormat* pFormat = pBox->ClaimFrameFormat(); + OSL_ENSURE( pBox->GetTabLines().size(), "Box does not have any Lines" ); + + SwTableLine* pLine = pBox->GetTabLines()[0]; + OSL_ENSURE( pLine, "Box is not within a Line" ); + + tools::Long nWidth = 0; + for( auto pTabBox : pLine->GetTabBoxes() ) + nWidth += pTabBox->GetFrameFormat()->GetFrameSize().GetWidth(); + + pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 )); + + // Boxes with Lines can only have Size/Fillorder + pFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 ); + pFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 ); +} + +namespace { + +struct InsULPara +{ + SwTableNode* pTableNd; + SwTableLine* pInsLine; + SwTableBox* pInsBox; + bool bUL_LR : 1; // Upper-Lower(true) or Left-Right(false) ? + bool bUL : 1; // Upper-Left(true) or Lower-Right(false) ? + + SwTableBox* pLeftBox; + + InsULPara( SwTableNode* pTNd, + SwTableBox* pLeft, + SwTableLine* pLine ) + : pTableNd( pTNd ), pInsLine( pLine ), pInsBox( nullptr ), + pLeftBox( pLeft ) + { bUL_LR = true; bUL = true; } + + void SetLeft( SwTableBox* pBox ) + { bUL_LR = false; bUL = true; if( pBox ) pInsBox = pBox; } + void SetRight( SwTableBox* pBox ) + { bUL_LR = false; bUL = false; if( pBox ) pInsBox = pBox; } + void SetLower( SwTableLine* pLine ) + { bUL_LR = true; bUL = false; if( pLine ) pInsLine = pLine; } +}; + +} + +static void lcl_Merge_MoveLine(FndLine_ & rFndLine, InsULPara *const pULPara); + +static void lcl_Merge_MoveBox(FndBox_ & rFndBox, InsULPara *const pULPara) +{ + SwTableBoxes* pBoxes; + + sal_uInt16 nStt = 0, nEnd = rFndBox.GetLines().size(); + sal_uInt16 nInsPos = USHRT_MAX; + if( !pULPara->bUL_LR ) // Left/Right + { + sal_uInt16 nPos; + SwTableBox* pFndTableBox = rFndBox.GetBox(); + pBoxes = &pFndTableBox->GetUpper()->GetTabBoxes(); + if( pULPara->bUL ) // Left ? + { + // if there are Boxes before it, move them + nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox ); + if( 0 != nPos ) + lcl_CpyBoxes( 0, nPos, *pBoxes, pULPara->pInsLine ); + } + else // Right + { + // if there are Boxes behind it, move them + nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox ); + if( nPos +1 < o3tl::narrowing<sal_uInt16>(pBoxes->size()) ) + { + nInsPos = pULPara->pInsLine->GetTabBoxes().size(); + lcl_CpyBoxes( nPos+1, pBoxes->size(), + *pBoxes, pULPara->pInsLine ); + } + } + } + // Upper/Lower and still deeper? + else if (!rFndBox.GetLines().empty()) + { + // Only search the Line from which we need to move + nStt = pULPara->bUL ? 0 : rFndBox.GetLines().size()-1; + nEnd = nStt+1; + } + + pBoxes = &pULPara->pInsLine->GetTabBoxes(); + + // Is there still a level to step down to? + if (rFndBox.GetBox()->GetTabLines().empty()) + return; + + SwTableBox* pBox = new SwTableBox( + static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat()), + 0, pULPara->pInsLine ); + InsULPara aPara( *pULPara ); + aPara.pInsBox = pBox; + for (FndLines_t::iterator it = rFndBox.GetLines().begin() + nStt; + it != rFndBox.GetLines().begin() + nEnd; ++it ) + { + lcl_Merge_MoveLine(**it, &aPara); + } + if( !pBox->GetTabLines().empty() ) + { + if( USHRT_MAX == nInsPos ) + nInsPos = pBoxes->size(); + pBoxes->insert( pBoxes->begin() + nInsPos, pBox ); + lcl_CalcWidth( pBox ); // calculate the Box's width + } + else + delete pBox; +} + +static void lcl_Merge_MoveLine(FndLine_& rFndLine, InsULPara *const pULPara) +{ + SwTableLines* pLines; + + sal_uInt16 nStt = 0, nEnd = rFndLine.GetBoxes().size(); + sal_uInt16 nInsPos = USHRT_MAX; + if( pULPara->bUL_LR ) // UpperLower ? + { + sal_uInt16 nPos; + SwTableLine* pFndLn = rFndLine.GetLine(); + pLines = pFndLn->GetUpper() ? + &pFndLn->GetUpper()->GetTabLines() : + &pULPara->pTableNd->GetTable().GetTabLines(); + + SwTableBox* pLBx = rFndLine.GetBoxes().front()->GetBox(); + SwTableBox* pRBx = rFndLine.GetBoxes().back()->GetBox(); + sal_uInt16 nLeft = pFndLn->GetBoxPos( pLBx ); + sal_uInt16 nRight = pFndLn->GetBoxPos( pRBx ); + + if( !nLeft || nRight == pFndLn->GetTabBoxes().size() ) + { + if( pULPara->bUL ) // Upper ? + { + // If there are Lines before it, move them + nPos = pLines->GetPos( pFndLn ); + if( 0 != nPos ) + lcl_CpyLines( 0, nPos, *pLines, pULPara->pInsBox ); + } + else + // If there are Lines after it, move them + if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) ) + { + nInsPos = pULPara->pInsBox->GetTabLines().size(); + lcl_CpyLines( nPos+1, pLines->size(), *pLines, + pULPara->pInsBox ); + } + } + else + { + // There are still Boxes on the left side, so put the Left- + // and Merge-Box into one Box and Line, insert before/after + // a Line with a Box, into which the upper/lower Lines are + // inserted + SwTableLine* pInsLine = pULPara->pLeftBox->GetUpper(); + SwTableBox* pLMBox = new SwTableBox( + static_cast<SwTableBoxFormat*>(pULPara->pLeftBox->GetFrameFormat()), 0, pInsLine ); + SwTableLine* pLMLn = new SwTableLine( + static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 2, pLMBox ); + pLMLn->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE ); + + pLMBox->GetTabLines().insert( pLMBox->GetTabLines().begin(), pLMLn ); + + lcl_CpyBoxes( 0, 2, pInsLine->GetTabBoxes(), pLMLn ); + + pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLMBox ); + + if( pULPara->bUL ) // Upper ? + { + // If there are Lines before it, move them + nPos = pLines->GetPos( pFndLn ); + if( 0 != nPos ) + lcl_CpyLines( 0, nPos, *pLines, pLMBox, 0 ); + } + else + // If there are Lines after it, move them + if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) ) + lcl_CpyLines( nPos+1, pLines->size(), *pLines, + pLMBox ); + lcl_CalcWidth( pLMBox ); // calculate the Box's width + } + } + // Left/Right + else + { + // Find only the Line from which we need to move + nStt = pULPara->bUL ? 0 : rFndLine.GetBoxes().size()-1; + nEnd = nStt+1; + } + pLines = &pULPara->pInsBox->GetTabLines(); + + SwTableLine* pNewLine = new SwTableLine( + static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()), 0, pULPara->pInsBox ); + InsULPara aPara( *pULPara ); // copying + aPara.pInsLine = pNewLine; + FndBoxes_t & rLineBoxes = rFndLine.GetBoxes(); + for (FndBoxes_t::iterator it = rLineBoxes.begin() + nStt; + it != rLineBoxes.begin() + nEnd; ++it) + { + lcl_Merge_MoveBox(**it, &aPara); + } + + if( !pNewLine->GetTabBoxes().empty() ) + { + if( USHRT_MAX == nInsPos ) + nInsPos = pLines->size(); + pLines->insert( pLines->begin() + nInsPos, pNewLine ); + } + else + delete pNewLine; +} + +static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox ); + +bool SwTable::OldMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes, + SwTableBox* pMergeBox, SwUndoTableMerge* pUndo ) +{ + OSL_ENSURE( !rBoxes.empty() && pMergeBox, "no valid values" ); + SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode()); + if( !pTableNd ) + return false; + + // Find all Boxes/Lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( rBoxes, &aFndBox ); + ForEach_FndLineCopyCol( GetTabLines(), &aPara ); + } + if( aFndBox.GetLines().empty() ) + return false; + + // TL_CHART2: splitting/merging of a number of cells or rows will usually make + // the table too complex to be handled with chart. + // Thus we tell the charts to use their own data provider and forget about this table + pDoc->getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this ); + + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + if( pUndo ) + pUndo->SetSelBoxes( rBoxes ); + + // Find Lines for the Layout update + aFndBox.SetTableLines( *this ); + aFndBox.DelFrames( *this ); + + FndBox_* pFndBox = &aFndBox; + while( 1 == pFndBox->GetLines().size() && + 1 == pFndBox->GetLines().front()->GetBoxes().size() ) + { + pFndBox = pFndBox->GetLines().front()->GetBoxes().front().get(); + } + + SwTableLine* pInsLine = new SwTableLine( + static_cast<SwTableLineFormat*>(pFndBox->GetLines().front()->GetLine()->GetFrameFormat()), 0, + !pFndBox->GetUpper() ? nullptr : pFndBox->GetBox() ); + pInsLine->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE ); + + // Add the new Line + SwTableLines* pLines = pFndBox->GetUpper() ? + &pFndBox->GetBox()->GetTabLines() : &GetTabLines(); + + SwTableLine* pNewLine = pFndBox->GetLines().front()->GetLine(); + sal_uInt16 nInsPos = pLines->GetPos( pNewLine ); + pLines->insert( pLines->begin() + nInsPos, pInsLine ); + + SwTableBox* pLeftBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine ); + SwTableBox* pRightBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine ); + pMergeBox->SetUpper( pInsLine ); + pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLeftBox ); + pLeftBox->ClaimFrameFormat(); + pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 1, pMergeBox); + pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 2, pRightBox ); + pRightBox->ClaimFrameFormat(); + + // This contains all Lines that are above the selected Area, + // thus they form a Upper/Lower Line + InsULPara aPara( pTableNd, pLeftBox, pInsLine ); + + // Move the overlapping upper/lower Lines of the selected Area + for (auto & it : pFndBox->GetLines().front()->GetBoxes()) + { + lcl_Merge_MoveBox(*it, &aPara); + } + aPara.SetLower( pInsLine ); + const auto nEnd = pFndBox->GetLines().size()-1; + for (auto & it : pFndBox->GetLines()[nEnd]->GetBoxes()) + { + lcl_Merge_MoveBox(*it, &aPara); + } + + // Move the Boxes extending into the selected Area from left/right + aPara.SetLeft( pLeftBox ); + for (auto & rpFndLine : pFndBox->GetLines()) + { + lcl_Merge_MoveLine( *rpFndLine, &aPara ); + } + + aPara.SetRight( pRightBox ); + for (auto & rpFndLine : pFndBox->GetLines()) + { + lcl_Merge_MoveLine( *rpFndLine, &aPara ); + } + + if( pLeftBox->GetTabLines().empty() ) + DeleteBox_( *this, pLeftBox, nullptr, false, false ); + else + { + lcl_CalcWidth( pLeftBox ); // calculate the Box's width + if( pUndo && pLeftBox->GetSttNd() ) + pUndo->AddNewBox( pLeftBox->GetSttIdx() ); + } + if( pRightBox->GetTabLines().empty() ) + DeleteBox_( *this, pRightBox, nullptr, false, false ); + else + { + lcl_CalcWidth( pRightBox ); // calculate the Box's width + if( pUndo && pRightBox->GetSttNd() ) + pUndo->AddNewBox( pRightBox->GetSttIdx() ); + } + + DeleteSel( pDoc, rBoxes, nullptr, nullptr, false, false ); + + // Clean up this Line's structure once again, generally all of them + GCLines(); + + for( const auto& rpBox : GetTabLines()[0]->GetTabBoxes() ) + lcl_BoxSetHeadCondColl(rpBox); + + aFndBox.MakeFrames( *this ); + + CHECKBOXWIDTH + CHECKTABLELAYOUT + + return true; +} + +static void lcl_CheckRowSpan( SwTable &rTable ) +{ + const tools::Long nLineCount = static_cast<tools::Long>(rTable.GetTabLines().size()); + tools::Long nMaxSpan = nLineCount; + tools::Long nMinSpan = 1; + while( nMaxSpan ) + { + SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ]; + for( auto pBox : pLine->GetTabBoxes() ) + { + sal_Int32 nRowSpan = pBox->getRowSpan(); + if( nRowSpan > nMaxSpan ) + pBox->setRowSpan( nMaxSpan ); + else if( nRowSpan < nMinSpan ) + pBox->setRowSpan( nMinSpan > 0 ? nMaxSpan : nMinSpan ); + } + --nMaxSpan; + nMinSpan = -nMaxSpan; + } +} + +static sal_uInt16 lcl_GetBoxOffset( const FndBox_& rBox ) +{ + // Find the first Box + const FndBox_* pFirstBox = &rBox; + while (!pFirstBox->GetLines().empty()) + { + pFirstBox = pFirstBox->GetLines().front()->GetBoxes().front().get(); + } + + sal_uInt16 nRet = 0; + // Calculate the position relative to above via the Lines + const SwTableBox* pBox = pFirstBox->GetBox(); + do { + const SwTableBoxes& rBoxes = pBox->GetUpper()->GetTabBoxes(); + for( auto pCmp : rBoxes ) + { + if (pBox==pCmp) + break; + nRet = nRet + o3tl::narrowing<sal_uInt16>(pCmp->GetFrameFormat()->GetFrameSize().GetWidth()); + } + pBox = pBox->GetUpper()->GetUpper(); + } while( pBox ); + return nRet; +} + +static sal_uInt16 lcl_GetLineWidth( const FndLine_& rLine ) +{ + sal_uInt16 nRet = 0; + for( auto n = rLine.GetBoxes().size(); n; ) + { + nRet = nRet + o3tl::narrowing<sal_uInt16>(rLine.GetBoxes()[--n]->GetBox() + ->GetFrameFormat()->GetFrameSize().GetWidth()); + } + return nRet; +} + +static void lcl_CalcNewWidths(const FndLines_t& rFndLines, CpyPara& rPara) +{ + rPara.pWidths.reset(); + const size_t nLineCount = rFndLines.size(); + if( nLineCount ) + { + rPara.pWidths = std::make_shared< std::vector< std::vector< sal_uLong > > > + ( nLineCount ); + // First we collect information about the left/right borders of all + // selected cells + for( size_t nLine = 0; nLine < nLineCount; ++nLine ) + { + std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ]; + const FndLine_ *pFndLine = rFndLines[ nLine ].get(); + if( pFndLine && !pFndLine->GetBoxes().empty() ) + { + const SwTableLine *pLine = pFndLine->GetLine(); + if( pLine && !pLine->GetTabBoxes().empty() ) + { + size_t nBoxCount = pLine->GetTabBoxes().size(); + sal_uLong nPos = 0; + // The first selected box... + const SwTableBox *const pSel = + pFndLine->GetBoxes().front()->GetBox(); + size_t nBox = 0; + // Sum up the width of all boxes before the first selected box + while( nBox < nBoxCount ) + { + SwTableBox* pBox = pLine->GetTabBoxes()[nBox++]; + if( pBox != pSel ) + nPos += pBox->GetFrameFormat()->GetFrameSize().GetWidth(); + else + break; + } + // nPos is now the left border of the first selected box + if( rPara.nMinLeft > nPos ) + rPara.nMinLeft = nPos; + nBoxCount = pFndLine->GetBoxes().size(); + rWidth = std::vector< sal_uLong >( nBoxCount+2 ); + rWidth[ 0 ] = nPos; + // Add now the widths of all selected boxes and store + // the positions in the vector + for( nBox = 0; nBox < nBoxCount; ) + { + nPos += pFndLine->GetBoxes()[nBox] + ->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth(); + rWidth[ ++nBox ] = nPos; + } + // nPos: The right border of the last selected box + if( rPara.nMaxRight < nPos ) + rPara.nMaxRight = nPos; + if( nPos <= rWidth[ 0 ] ) + rWidth.clear(); + } + } + } + } + // Second step: calculate the new widths for the copied cells + sal_uLong nSelSize = rPara.nMaxRight - rPara.nMinLeft; + if( !nSelSize ) + return; + + for( size_t nLine = 0; nLine < nLineCount; ++nLine ) + { + std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ]; + const size_t nCount = rWidth.size(); + if( nCount > 2 ) + { + rWidth[ nCount - 1 ] = rPara.nMaxRight; + sal_uLong nLastPos = 0; + for( size_t nBox = 0; nBox < nCount; ++nBox ) + { + sal_uInt64 nNextPos = rWidth[ nBox ]; + nNextPos -= rPara.nMinLeft; + nNextPos *= rPara.nNewSize; + nNextPos /= nSelSize; + rWidth[ nBox ] = static_cast<sal_uLong>(nNextPos - nLastPos); + nLastPos = static_cast<sal_uLong>(nNextPos); + } + } + } +} + +static void +lcl_CopyLineToDoc(FndLine_ const& rpFndLn, CpyPara *const pCpyPara); + +static void lcl_CopyBoxToDoc(FndBox_ const& rFndBox, CpyPara *const pCpyPara) +{ + // Calculation of new size + sal_uLong nRealSize; + sal_uLong nDummy1 = 0; + sal_uLong nDummy2 = 0; + if( pCpyPara->pTableNd->GetTable().IsNewModel() ) + { + if( pCpyPara->nBoxIdx == 1 ) + nDummy1 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][0]; + nRealSize = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx++]; + if( pCpyPara->nBoxIdx == (*pCpyPara->pWidths)[pCpyPara->nLnIdx].size()-1 ) + nDummy2 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx]; + } + else + { + nRealSize = pCpyPara->nNewSize; + nRealSize *= rFndBox.GetBox()->GetFrameFormat()->GetFrameSize().GetWidth(); + if (pCpyPara->nOldSize == 0) + throw o3tl::divide_by_zero(); + nRealSize /= pCpyPara->nOldSize; + } + + sal_uLong nSize; + bool bDummy = nDummy1 > 0; + if( bDummy ) + nSize = nDummy1; + else + { + nSize = nRealSize; + nRealSize = 0; + } + do + { + // Find the Frame Format in the list of all Frame Formats + CpyTabFrame aFindFrame(static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat())); + + std::shared_ptr<SwFormatFrameSize> aFrameSz(std::make_shared<SwFormatFrameSize>()); + CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame ); + const CpyTabFrames::size_type nFndPos = itFind - pCpyPara->rTabFrameArr.begin(); + + // It *is* sometimes cool to have multiple tests/if's and assignments + // in a single statement, and it is technically possible. But it is definitely + // not simply readable - where from my POV reading code is done 1000 times + // more often than writing it. Thus I dismantled the expression in smaller + // chunks to keep it handy/understandable/changeable (hopefully without error) + // The original for reference: + // if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) || + // ( aFrameSz = ( aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]).pNewFrameFormat-> + // GetFrameSize()).GetWidth() != static_cast<SwTwips>(nSize) ) + + bool DoCopyIt(itFind == pCpyPara->rTabFrameArr.end()); + + if(!DoCopyIt) + { + DoCopyIt = !(*itFind == aFindFrame); + } + + if(!DoCopyIt) + { + aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]; + aFrameSz.reset(aFindFrame.pNewFrameFormat->GetFrameSize().Clone()); + DoCopyIt = aFrameSz->GetWidth() != static_cast<SwTwips>(nSize); + } + + if(DoCopyIt) + { + // It doesn't exist yet, so copy it + aFindFrame.pNewFrameFormat = pCpyPara->rDoc.MakeTableBoxFormat(); + aFindFrame.pNewFrameFormat->CopyAttrs( *rFndBox.GetBox()->GetFrameFormat() ); + if( !pCpyPara->bCpyContent ) + aFindFrame.pNewFrameFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE ); + aFrameSz->SetWidth( nSize ); + aFindFrame.pNewFrameFormat->SetFormatAttr( *aFrameSz ); + pCpyPara->rTabFrameArr.insert( aFindFrame ); + } + + SwTableBox* pBox; + if (!rFndBox.GetLines().empty()) + { + pBox = new SwTableBox( aFindFrame.pNewFrameFormat, + rFndBox.GetLines().size(), pCpyPara->pInsLine ); + pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox ); + CpyPara aPara( *pCpyPara, pBox ); + aPara.nNewSize = nSize; // get the size + for (auto const& rpFndLine : rFndBox.GetLines()) + { + lcl_CopyLineToDoc( *rpFndLine, &aPara ); + } + } + else + { + // Create an empty Box + pCpyPara->rDoc.GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine, + aFindFrame.pNewFrameFormat, + pCpyPara->rDoc.GetDfltTextFormatColl(), + nullptr, pCpyPara->nInsPos ); + pBox = pCpyPara->pInsLine->GetTabBoxes()[ pCpyPara->nInsPos ]; + if( bDummy ) + pBox->setDummyFlag( true ); + else if( pCpyPara->bCpyContent ) + { + // Copy the content into this empty Box + pBox->setRowSpan(rFndBox.GetBox()->getRowSpan()); + + // We can also copy formulas and values, if we copy the content + { + SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyPara->rDoc.GetAttrPool() ); + aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet()); + if( aBoxAttrSet.Count() ) + { + const SwTableBoxNumFormat* pItem; + SvNumberFormatter* pN = pCpyPara->rDoc.GetNumberFormatter( false ); + if( pN && pN->HasMergeFormatTable() && (pItem = aBoxAttrSet. + GetItemIfSet( RES_BOXATR_FORMAT, false )) ) + { + sal_uLong nOldIdx = pItem->GetValue(); + sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx ); + if( nNewIdx != nOldIdx ) + aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx )); + } + pBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet ); + } + } + SwDoc* pFromDoc = rFndBox.GetBox()->GetFrameFormat()->GetDoc(); + SwNodeRange aCpyRg( *rFndBox.GetBox()->GetSttNd(), SwNodeOffset(1), + *rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() ); + SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 ); + + pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx, nullptr, false); + // Delete the initial TextNode + pCpyPara->rDoc.GetNodes().Delete( aInsIdx ); + } + ++pCpyPara->nInsPos; + } + if( nRealSize ) + { + bDummy = false; + nSize = nRealSize; + nRealSize = 0; + } + else + { + bDummy = true; + nSize = nDummy2; + nDummy2 = 0; + } + } + while( nSize ); +} + +static void +lcl_CopyLineToDoc(const FndLine_& rFndLine, CpyPara *const pCpyPara) +{ + // Find the Frame Format in the list of all Frame Formats + CpyTabFrame aFindFrame( rFndLine.GetLine()->GetFrameFormat() ); + CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame ); + if( itFind == pCpyPara->rTabFrameArr.end() ) + { + // It doesn't exist yet, so copy it + aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pCpyPara->rDoc.MakeTableLineFormat()); + aFindFrame.pNewFrameFormat->CopyAttrs( *rFndLine.GetLine()->GetFrameFormat() ); + pCpyPara->rTabFrameArr.insert( aFindFrame ); + } + else + aFindFrame = *itFind; + + SwTableLine* pNewLine = new SwTableLine( reinterpret_cast<SwTableLineFormat*>(aFindFrame.pNewFrameFormat), + rFndLine.GetBoxes().size(), pCpyPara->pInsBox ); + if( pCpyPara->pInsBox ) + { + SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines(); + rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine ); + } + else + { + SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines(); + rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine); + } + + CpyPara aPara( *pCpyPara, pNewLine ); + + if( pCpyPara->pTableNd->GetTable().IsNewModel() ) + { + aPara.nOldSize = 0; // will not be used + aPara.nBoxIdx = 1; + } + else if( rFndLine.GetBoxes().size() == + rFndLine.GetLine()->GetTabBoxes().size() ) + { + // Get the Parent's size + const SwFrameFormat* pFormat; + + if( rFndLine.GetLine()->GetUpper() ) + pFormat = rFndLine.GetLine()->GetUpper()->GetFrameFormat(); + else + pFormat = pCpyPara->pTableNd->GetTable().GetFrameFormat(); + aPara.nOldSize = pFormat->GetFrameSize().GetWidth(); + } + else + // Calculate it + for (auto &rpBox : rFndLine.GetBoxes()) + { + aPara.nOldSize += rpBox->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth(); + } + + const FndBoxes_t& rBoxes = rFndLine.GetBoxes(); + for (auto const& it : rBoxes) + { + lcl_CopyBoxToDoc(*it, &aPara); + } + if( pCpyPara->pTableNd->GetTable().IsNewModel() ) + ++pCpyPara->nLnIdx; +} + +void SwTable::CopyHeadlineIntoTable( SwTableNode& rTableNd ) +{ + // Find all Boxes/Lines + SwSelBoxes aSelBoxes; + SwTableBox* pBox = GetTabSortBoxes()[ 0 ]; + pBox = GetTableBox( pBox->GetSttNd()->StartOfSectionNode()->GetIndex() + 1 ); + SelLineFromBox( pBox, aSelBoxes ); + + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( aSelBoxes, &aFndBox ); + ForEach_FndLineCopyCol( GetTabLines(), &aPara ); + } + if( aFndBox.GetLines().empty() ) + return; + + { + // Convert Table formulas to their relative representation + SwTableFormulaUpdate aMsgHint( this ); + aMsgHint.m_eFlags = TBL_RELBOXNAME; + GetFrameFormat()->GetDoc()->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + } + + CpyTabFrames aCpyFormat; + CpyPara aPara( &rTableNd, 1, aCpyFormat ); + aPara.nNewSize = aPara.nOldSize = rTableNd.GetTable().GetFrameFormat()->GetFrameSize().GetWidth(); + // Copy + if( IsNewModel() ) + lcl_CalcNewWidths( aFndBox.GetLines(), aPara ); + for (const auto & rpFndLine : aFndBox.GetLines()) + { + lcl_CopyLineToDoc( *rpFndLine, &aPara ); + } + if( rTableNd.GetTable().IsNewModel() ) + { // The copied line must not contain any row span attributes > 1 + SwTableLine* pLine = rTableNd.GetTable().GetTabLines()[0]; + OSL_ENSURE( !pLine->GetTabBoxes().empty(), "Empty Table Line" ); + for( auto pTableBox : pLine->GetTabBoxes() ) + { + OSL_ENSURE( pTableBox, "Missing Table Box" ); + pTableBox->setRowSpan( 1 ); + } + } +} + +bool SwTable::MakeCopy( SwDoc& rInsDoc, const SwPosition& rPos, + const SwSelBoxes& rSelBoxes, + bool bCpyName, const OUString& rStyleName ) const +{ + // Find all Boxes/Lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( rSelBoxes, &aFndBox ); + ForEach_FndLineCopyCol( const_cast<SwTableLines&>(GetTabLines()), &aPara ); + } + if( aFndBox.GetLines().empty() ) + return false; + + // First copy the PoolTemplates for the Table, so that the Tables are + // actually copied and have valid values. + SwDoc* pSrcDoc = GetFrameFormat()->GetDoc(); + if( pSrcDoc != &rInsDoc ) + { + rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) ); + rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) ); + } + + SwTable* pNewTable = const_cast<SwTable*>(rInsDoc.InsertTable( + SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ), + rPos, 1, 1, GetFrameFormat()->GetHoriOrient().GetHoriOrient(), + nullptr, nullptr, false, IsNewModel() )); + if( !pNewTable ) + return false; + + SwNodeIndex aIdx( rPos.nNode, -1 ); + SwTableNode* pTableNd = aIdx.GetNode().FindTableNode(); + ++aIdx; + OSL_ENSURE( pTableNd, "Where is the TableNode now?" ); + + pTableNd->GetTable().SetRowsToRepeat( GetRowsToRepeat() ); + + pNewTable->SetTableStyleName(pTableNd->GetTable().GetTableStyleName()); + + pTableNd->GetTable().SetTableStyleName(rStyleName); + if( auto pSwDDETable = dynamic_cast<const SwDDETable*>(this) ) + { + // A DDE-Table is being copied + // Does the new Document actually have it's FieldType? + SwFieldType* pFieldType = rInsDoc.getIDocumentFieldsAccess().InsertFieldType( + *pSwDDETable->GetDDEFieldType() ); + OSL_ENSURE( pFieldType, "unknown FieldType" ); + + // Change the Table Pointer at the Node + pNewTable = new SwDDETable( *pNewTable, + static_cast<SwDDEFieldType*>(pFieldType) ); + pTableNd->SetNewTable( std::unique_ptr<SwTable>(pNewTable), false ); + } + + pNewTable->GetFrameFormat()->CopyAttrs( *GetFrameFormat() ); + pNewTable->SetTableChgMode( GetTableChgMode() ); + + // Destroy the already created Frames + pTableNd->DelFrames(); + + { + // Convert the Table formulas to their relative representation + SwTableFormulaUpdate aMsgHint( this ); + aMsgHint.m_eFlags = TBL_RELBOXNAME; + pSrcDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint ); + } + + SwTableNumFormatMerge aTNFM(*pSrcDoc, rInsDoc); + + // Also copy Names or enforce a new unique one + if( bCpyName ) + pNewTable->GetFrameFormat()->SetName( GetFrameFormat()->GetName() ); + + CpyTabFrames aCpyFormat; + CpyPara aPara( pTableNd, 1, aCpyFormat ); + aPara.nNewSize = aPara.nOldSize = GetFrameFormat()->GetFrameSize().GetWidth(); + + if( IsNewModel() ) + lcl_CalcNewWidths( aFndBox.GetLines(), aPara ); + // Copy + for (const auto & rpFndLine : aFndBox.GetLines()) + { + lcl_CopyLineToDoc( *rpFndLine, &aPara ); + } + + // Set the "right" margin above/below + { + FndLine_* pFndLn = aFndBox.GetLines().front().get(); + SwTableLine* pLn = pFndLn->GetLine(); + const SwTableLine* pTmp = pLn; + sal_uInt16 nLnPos = GetTabLines().GetPos( pTmp ); + if( USHRT_MAX != nLnPos && nLnPos ) + { + // There is a Line before it + SwCollectTableLineBoxes aLnPara( false, SplitTable_HeadlineOption::BorderCopy ); + + pLn = GetTabLines()[ nLnPos - 1 ]; + for( const auto& rpBox : pLn->GetTabBoxes() ) + sw_Box_CollectBox( rpBox, &aLnPara ); + + if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ), + lcl_GetLineWidth( *pFndLn )) ) + { + aLnPara.SetValues( true ); + pLn = pNewTable->GetTabLines()[ 0 ]; + for( const auto& rpBox : pLn->GetTabBoxes() ) + sw_BoxSetSplitBoxFormats(rpBox, &aLnPara ); + } + } + + pFndLn = aFndBox.GetLines().back().get(); + pLn = pFndLn->GetLine(); + pTmp = pLn; + nLnPos = GetTabLines().GetPos( pTmp ); + if( nLnPos < GetTabLines().size() - 1 ) + { + // There is a Line following it + SwCollectTableLineBoxes aLnPara( true, SplitTable_HeadlineOption::BorderCopy ); + + pLn = GetTabLines()[ nLnPos + 1 ]; + for( const auto& rpBox : pLn->GetTabBoxes() ) + sw_Box_CollectBox( rpBox, &aLnPara ); + + if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ), + lcl_GetLineWidth( *pFndLn )) ) + { + aLnPara.SetValues( false ); + pLn = pNewTable->GetTabLines().back(); + for( const auto& rpBox : pLn->GetTabBoxes() ) + sw_BoxSetSplitBoxFormats(rpBox, &aLnPara ); + } + } + } + + // We need to delete the initial Box + DeleteBox_( *pNewTable, pNewTable->GetTabLines().back()->GetTabBoxes()[0], + nullptr, false, false ); + + if( pNewTable->IsNewModel() ) + lcl_CheckRowSpan( *pNewTable ); + // Clean up + pNewTable->GCLines(); + + pTableNd->MakeOwnFrames( &aIdx ); // re-generate the Frames + + CHECKTABLELAYOUT + + return true; +} + +// Find the next Box with content from this Line +SwTableBox* SwTableLine::FindNextBox( const SwTable& rTable, + const SwTableBox* pSrchBox, bool bOvrTableLns ) const +{ + const SwTableLine* pLine = this; // for M800 + SwTableBox* pBox; + sal_uInt16 nFndPos; + if( !GetTabBoxes().empty() && pSrchBox ) + { + nFndPos = GetBoxPos( pSrchBox ); + if( USHRT_MAX != nFndPos && + nFndPos + 1 != o3tl::narrowing<sal_uInt16>(GetTabBoxes().size()) ) + { + pBox = GetTabBoxes()[ nFndPos + 1 ]; + while( !pBox->GetTabLines().empty() ) + pBox = pBox->GetTabLines().front()->GetTabBoxes()[0]; + return pBox; + } + } + + if( GetUpper() ) + { + nFndPos = GetUpper()->GetTabLines().GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" ); + // Is there another Line? + if( nFndPos+1 >= o3tl::narrowing<sal_uInt16>(GetUpper()->GetTabLines().size()) ) + return GetUpper()->GetUpper()->FindNextBox( rTable, GetUpper(), bOvrTableLns ); + pLine = GetUpper()->GetTabLines()[nFndPos+1]; + } + else if( bOvrTableLns ) // Over a Table's the "BaseLines"?? + { + // Search for the next Line in the Table + nFndPos = rTable.GetTabLines().GetPos( pLine ); + if( nFndPos + 1 >= o3tl::narrowing<sal_uInt16>(rTable.GetTabLines().size()) ) + return nullptr; // there are no more Boxes + + pLine = rTable.GetTabLines()[ nFndPos+1 ]; + } + else + return nullptr; + + if( !pLine->GetTabBoxes().empty() ) + { + pBox = pLine->GetTabBoxes().front(); + while( !pBox->GetTabLines().empty() ) + pBox = pBox->GetTabLines().front()->GetTabBoxes().front(); + return pBox; + } + return pLine->FindNextBox( rTable, nullptr, bOvrTableLns ); +} + +// Find the previous Box from this Line +SwTableBox* SwTableLine::FindPreviousBox( const SwTable& rTable, + const SwTableBox* pSrchBox, bool bOvrTableLns ) const +{ + const SwTableLine* pLine = this; // for M800 + SwTableBox* pBox; + sal_uInt16 nFndPos; + if( !GetTabBoxes().empty() && pSrchBox ) + { + nFndPos = GetBoxPos( pSrchBox ); + if( USHRT_MAX != nFndPos && nFndPos ) + { + pBox = GetTabBoxes()[ nFndPos - 1 ]; + while( !pBox->GetTabLines().empty() ) + { + pLine = pBox->GetTabLines().back(); + pBox = pLine->GetTabBoxes().back(); + } + return pBox; + } + } + + if( GetUpper() ) + { + nFndPos = GetUpper()->GetTabLines().GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" ); + // Is there another Line? + if( !nFndPos ) + return GetUpper()->GetUpper()->FindPreviousBox( rTable, GetUpper(), bOvrTableLns ); + pLine = GetUpper()->GetTabLines()[nFndPos-1]; + } + else if( bOvrTableLns ) // Over a Table's the "BaseLines"?? + { + // Search for the next Line in the Table + nFndPos = rTable.GetTabLines().GetPos( pLine ); + if( !nFndPos ) + return nullptr; // there are no more Boxes + + pLine = rTable.GetTabLines()[ nFndPos-1 ]; + } + else + return nullptr; + + if( !pLine->GetTabBoxes().empty() ) + { + pBox = pLine->GetTabBoxes().back(); + while( !pBox->GetTabLines().empty() ) + { + pLine = pBox->GetTabLines().back(); + pBox = pLine->GetTabBoxes().back(); + } + return pBox; + } + return pLine->FindPreviousBox( rTable, nullptr, bOvrTableLns ); +} + +// Find the next Box with content from this Line +SwTableBox* SwTableBox::FindNextBox( const SwTable& rTable, + const SwTableBox* pSrchBox, bool bOvrTableLns ) const +{ + if( !pSrchBox && GetTabLines().empty() ) + return const_cast<SwTableBox*>(this); + return GetUpper()->FindNextBox( rTable, pSrchBox ? pSrchBox : this, + bOvrTableLns ); + +} + +// Find the next Box with content from this Line +SwTableBox* SwTableBox::FindPreviousBox( const SwTable& rTable, + const SwTableBox* pSrchBox ) const +{ + if( !pSrchBox && GetTabLines().empty() ) + return const_cast<SwTableBox*>(this); + return GetUpper()->FindPreviousBox( rTable, pSrchBox ? pSrchBox : this ); +} + +static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox ) +{ + // We need to adapt the paragraphs with conditional templates in the HeadLine + const SwStartNode* pSttNd = pBox->GetSttNd(); + if( pSttNd ) + pSttNd->CheckSectionCondColl(); + else + for( const SwTableLine* pLine : pBox->GetTabLines() ) + sw_LineSetHeadCondColl( pLine ); +} + +void sw_LineSetHeadCondColl( const SwTableLine* pLine ) +{ + for( const SwTableBox* pBox : pLine->GetTabBoxes() ) + lcl_BoxSetHeadCondColl(pBox); +} + +static SwTwips lcl_GetDistance( SwTableBox* pBox, bool bLeft ) +{ + bool bFirst = true; + SwTwips nRet = 0; + SwTableLine* pLine; + while( pBox ) + { + pLine = pBox->GetUpper(); + if( !pLine ) + break; + sal_uInt16 nStt = 0, nPos = pLine->GetBoxPos( pBox ); + + if( bFirst && !bLeft ) + ++nPos; + bFirst = false; + + while( nStt < nPos ) + nRet += pLine->GetTabBoxes()[ nStt++ ]->GetFrameFormat() + ->GetFrameSize().GetWidth(); + pBox = pLine->GetUpper(); + } + return nRet; +} + +static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam, + SwTwips nDist, bool bCheck ) +{ + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + for( auto pBox : rBoxes ) + { + SwFrameFormat* pFormat = pBox->GetFrameFormat(); + const SwFormatFrameSize& rSz = pFormat->GetFrameSize(); + SwTwips nWidth = rSz.GetWidth(); + bool bGreaterBox = false; + + if( bCheck ) + { + for( auto pLn : pBox->GetTabLines() ) + if( !::lcl_SetSelBoxWidth( pLn, rParam, nDist, true )) + return false; + + // Collect all "ContentBoxes" + bGreaterBox = (TableChgMode::FixedWidthChangeAbs != rParam.nMode) + && ((nDist + (rParam.bLeft ? 0 : nWidth)) >= rParam.nSide); + if (bGreaterBox + || (!rParam.bBigger + && (std::abs(nDist + ((rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth) - rParam.nSide) < COLFUZZY))) + { + SwTwips nLowerDiff; + if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode ) + { + // The "other Boxes" have been adapted, so change by this value + nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide; + nLowerDiff *= rParam.nDiff; + nLowerDiff /= rParam.nMaxSize; + nLowerDiff = rParam.nDiff - nLowerDiff; + } + else + nLowerDiff = rParam.nDiff; + + if( nWidth < nLowerDiff || nWidth - nLowerDiff < MINLAY ) + return false; + } + } + else + { + SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff; + for( auto pLn : pBox->GetTabLines() ) + { + rParam.nLowerDiff = 0; + lcl_SetSelBoxWidth( pLn, rParam, nDist, false ); + + if( nLowerDiff < rParam.nLowerDiff ) + nLowerDiff = rParam.nLowerDiff; + } + rParam.nLowerDiff = nOldLower; + + if( nLowerDiff || + (bGreaterBox = !nOldLower && TableChgMode::FixedWidthChangeAbs != rParam.nMode && + ( nDist + ( rParam.bLeft ? 0 : nWidth ) ) >= rParam.nSide) || + ( std::abs( nDist + ( (rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth ) + - rParam.nSide ) < COLFUZZY )) + { + // This column contains the Cursor - so decrease/increase + SwFormatFrameSize aNew( rSz ); + + if( !nLowerDiff ) + { + if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode ) + { + // The "other Boxes" have been adapted, so change by this value + nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide; + nLowerDiff *= rParam.nDiff; + nLowerDiff /= rParam.nMaxSize; + nLowerDiff = rParam.nDiff - nLowerDiff; + } + else + nLowerDiff = rParam.nDiff; + } + + rParam.nLowerDiff += nLowerDiff; + + if( rParam.bBigger ) + aNew.SetWidth( nWidth + nLowerDiff ); + else + aNew.SetWidth( nWidth - nLowerDiff ); + rParam.aShareFormats.SetSize( *pBox, aNew ); + break; + } + } + + if( rParam.bLeft && rParam.nMode != TableChgMode::FixedWidthChangeAbs && nDist >= rParam.nSide ) + break; + + nDist += nWidth; + + // If it gets bigger, then that's it + if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || !rParam.bLeft ) && + nDist >= rParam.nSide ) + break; + } + return true; +} + +static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam, + SwTwips nDist, bool bCheck ) +{ + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + for( auto pBox : rBoxes ) + { + SwFrameFormat* pFormat = pBox->GetFrameFormat(); + const SwFormatFrameSize& rSz = pFormat->GetFrameSize(); + SwTwips nWidth = rSz.GetWidth(); + + if( bCheck ) + { + for( auto pLn : pBox->GetTabLines() ) + if( !::lcl_SetOtherBoxWidth( pLn, rParam, nDist, true )) + return false; + + if( rParam.bBigger && ( TableChgMode::FixedWidthChangeAbs == rParam.nMode + ? std::abs( nDist - rParam.nSide ) < COLFUZZY + : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY + : nDist >= rParam.nSide - COLFUZZY )) ) + { + SwTwips nDiff; + if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional + { + // calculate relative + nDiff = nWidth; + nDiff *= rParam.nDiff; + nDiff /= rParam.nMaxSize; + } + else + nDiff = rParam.nDiff; + + if( nWidth < nDiff || nWidth - nDiff < MINLAY ) + return false; + } + } + else + { + SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff; + for( auto pLn : pBox->GetTabLines() ) + { + rParam.nLowerDiff = 0; + lcl_SetOtherBoxWidth( pLn, rParam, nDist, false ); + + if( nLowerDiff < rParam.nLowerDiff ) + nLowerDiff = rParam.nLowerDiff; + } + rParam.nLowerDiff = nOldLower; + + if( nLowerDiff || + ( TableChgMode::FixedWidthChangeAbs == rParam.nMode + ? std::abs( nDist - rParam.nSide ) < COLFUZZY + : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY + : nDist >= rParam.nSide - COLFUZZY) + ) ) + { + SwFormatFrameSize aNew( rSz ); + + if( !nLowerDiff ) + { + if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional + { + // calculate relative + nLowerDiff = nWidth; + nLowerDiff *= rParam.nDiff; + nLowerDiff /= rParam.nMaxSize; + } + else + nLowerDiff = rParam.nDiff; + } + + rParam.nLowerDiff += nLowerDiff; + + if( rParam.bBigger ) + aNew.SetWidth( nWidth - nLowerDiff ); + else + aNew.SetWidth( nWidth + nLowerDiff ); + + rParam.aShareFormats.SetSize( *pBox, aNew ); + } + } + + nDist += nWidth; + if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || rParam.bLeft ) && + nDist > rParam.nSide ) + break; + } + return true; +} + +static void lcl_AjustLines( SwTableLine* pLine, CR_SetBoxWidth& rParam ) +{ + SwTableBoxes& rBoxes = pLine->GetTabBoxes(); + for( auto pBox : rBoxes ) + { + SwFormatFrameSize aSz( pBox->GetFrameFormat()->GetFrameSize() ); + SwTwips nWidth = aSz.GetWidth(); + nWidth *= rParam.nDiff; + nWidth /= rParam.nMaxSize; + aSz.SetWidth( nWidth ); + rParam.aShareFormats.SetSize( *pBox, aSz ); + + for( auto pLn : pBox->GetTabLines() ) + ::lcl_AjustLines( pLn, rParam ); + } +} + +#ifdef DBG_UTIL +void CheckBoxWidth( const SwTableLine& rLine, SwTwips nSize ) +{ + const SwTableBoxes& rBoxes = rLine.GetTabBoxes(); + + SwTwips nCurrentSize = 0; + // See if the tables have a correct width + for (const SwTableBox* pBox : rBoxes) + { + const SwTwips nBoxW = pBox->GetFrameFormat()->GetFrameSize().GetWidth(); + nCurrentSize += nBoxW; + + for( auto pLn : pBox->GetTabLines() ) + CheckBoxWidth( *pLn, nBoxW ); + } + + if (sal::static_int_cast< tools::ULong >(std::abs(nCurrentSize - nSize)) > + (COLFUZZY * rBoxes.size())) + { + OSL_FAIL( "Line's Boxes are too small or too large" ); + } +} +#endif + +bool SwTable::SetColWidth( SwTableBox& rCurrentBox, TableChgWidthHeightType eType, + SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo ) +{ + SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout + + const SwFormatFrameSize& rSz = GetFrameFormat()->GetFrameSize(); + const SvxLRSpaceItem& rLR = GetFrameFormat()->GetLRSpace(); + + bool bBigger, + bRet = false, + bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) || + TableChgWidthHeightType::CellLeft == extractPosition( eType ); + + // Get the current Box's edge + // Only needed for manipulating the width + const SwTwips nDist = ::lcl_GetDistance( &rCurrentBox, bLeft ); + SwTwips nDistStt = 0; + CR_SetBoxWidth aParam( eType, nRelDiff, nDist, + bLeft ? nDist : rSz.GetWidth() - nDist, + const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) ); + bBigger = aParam.bBigger; + + FN_lcl_SetBoxWidth fnSelBox, fnOtherBox; + fnSelBox = lcl_SetSelBoxWidth; + fnOtherBox = lcl_SetOtherBoxWidth; + + switch( extractPosition(eType) ) + { + case TableChgWidthHeightType::ColRight: + case TableChgWidthHeightType::ColLeft: + if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode ) + { + // First test if we have room at all + bool bChgLRSpace = true; + if( bBigger ) + { + if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) && + !rSz.GetWidthPercent() ) + { + // silence -Wsign-compare on Android with the static cast + bRet = rSz.GetWidth() < static_cast<unsigned short>(USHRT_MAX) - nRelDiff; + bChgLRSpace = bLeft ? rLR.GetLeft() >= nAbsDiff + : rLR.GetRight() >= nAbsDiff; + } + else + bRet = bLeft ? rLR.GetLeft() >= nAbsDiff + : rLR.GetRight() >= nAbsDiff; + + if( !bRet ) + { + // Then call itself recursively; only with another mode (proportional) + TableChgMode eOld = m_eTableChgMode; + m_eTableChgMode = TableChgMode::FixedWidthChangeProp; + + bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff, + ppUndo ); + m_eTableChgMode = eOld; + return bRet; + } + } + else + { + bRet = true; + for( auto const & n: m_aLines ) + { + aParam.LoopClear(); + if( !(*fnSelBox)( n, aParam, nDistStt, true )) + { + bRet = false; + break; + } + } + } + + if( bRet ) + { + if( ppUndo ) + ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true )); + + tools::Long nFrameWidth = LONG_MAX; + LockModify(); + SwFormatFrameSize aSz( rSz ); + SvxLRSpaceItem aLR( rLR ); + if( bBigger ) + { + // If the Table does not have any room to grow, we need to create some! + // silence -Wsign-compare on Android with the static cast + if( aSz.GetWidth() + nRelDiff > static_cast<unsigned short>(USHRT_MAX) ) + { + // Break down to USHRT_MAX / 2 + CR_SetBoxWidth aTmpPara( TableChgWidthHeightType::ColLeft, aSz.GetWidth() / 2, + 0, aSz.GetWidth(), aParam.pTableNd ); + for( size_t nLn = 0; nLn < m_aLines.size(); ++nLn ) + ::lcl_AjustLines( m_aLines[ nLn ], aTmpPara ); + aSz.SetWidth( aSz.GetWidth() / 2 ); + aParam.nDiff = nRelDiff /= 2; + aParam.nSide /= 2; + aParam.nMaxSize /= 2; + } + + if( bLeft ) + aLR.SetLeft( sal_uInt16( aLR.GetLeft() - nAbsDiff ) ); + else + aLR.SetRight( sal_uInt16( aLR.GetRight() - nAbsDiff ) ); + } + else if( bLeft ) + aLR.SetLeft( sal_uInt16( aLR.GetLeft() + nAbsDiff ) ); + else + aLR.SetRight( sal_uInt16( aLR.GetRight() + nAbsDiff ) ); + + if( bChgLRSpace ) + GetFrameFormat()->SetFormatAttr( aLR ); + const SwFormatHoriOrient& rHOri = GetFrameFormat()->GetHoriOrient(); + if( text::HoriOrientation::FULL == rHOri.GetHoriOrient() || + (text::HoriOrientation::LEFT == rHOri.GetHoriOrient() && aLR.GetLeft()) || + (text::HoriOrientation::RIGHT == rHOri.GetHoriOrient() && aLR.GetRight())) + { + SwFormatHoriOrient aHOri( rHOri ); + aHOri.SetHoriOrient( text::HoriOrientation::NONE ); + GetFrameFormat()->SetFormatAttr( aHOri ); + + // If the Table happens to contain relative values (USHORT_MAX), + // we need to convert them to absolute ones now. + // Bug 61494 + if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) && + !rSz.GetWidthPercent() ) + { + SwTabFrame* pTabFrame = SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First(); + if( pTabFrame && + pTabFrame->getFramePrintArea().Width() != rSz.GetWidth() ) + { + nFrameWidth = pTabFrame->getFramePrintArea().Width(); + if( bBigger ) + nFrameWidth += nAbsDiff; + else + nFrameWidth -= nAbsDiff; + } + } + } + + if( bBigger ) + aSz.SetWidth( aSz.GetWidth() + nRelDiff ); + else + aSz.SetWidth( aSz.GetWidth() - nRelDiff ); + + if( rSz.GetWidthPercent() ) + aSz.SetWidthPercent( static_cast<sal_uInt8>(( aSz.GetWidth() * 100 ) / + ( aSz.GetWidth() + aLR.GetRight() + aLR.GetLeft()))); + + GetFrameFormat()->SetFormatAttr( aSz ); + + UnlockModify(); + + for( sal_uInt16 n = m_aLines.size(); n; ) + { + --n; + aParam.LoopClear(); + (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false ); + } + + // If the Table happens to contain relative values (USHORT_MAX), + // we need to convert them to absolute ones now. + // Bug 61494 + if( LONG_MAX != nFrameWidth ) + { + SwFormatFrameSize aAbsSz( aSz ); + aAbsSz.SetWidth( nFrameWidth ); + GetFrameFormat()->SetFormatAttr( aAbsSz ); + } + } + } + else if( bLeft ? nDist != 0 : std::abs( rSz.GetWidth() - nDist ) > COLFUZZY ) + { + bRet = true; + if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode ) + aParam.bBigger = !bBigger; + + // First test if we have room at all + if( aParam.bBigger ) + { + for( auto const & n: m_aLines ) + { + aParam.LoopClear(); + if( !(*fnOtherBox)( n, aParam, 0, true )) + { + bRet = false; + break; + } + } + } + else + { + for( auto const & n: m_aLines ) + { + aParam.LoopClear(); + if( !(*fnSelBox)( n, aParam, nDistStt, true )) + { + bRet = false; + break; + } + } + } + + // If true, set it + if( bRet ) + { + CR_SetBoxWidth aParam1( aParam ); + if( ppUndo ) + ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true )); + + if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft ) + { + for( sal_uInt16 n = m_aLines.size(); n; ) + { + --n; + aParam.LoopClear(); + aParam1.LoopClear(); + (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false ); + (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false ); + } + } + else + { + for( sal_uInt16 n = m_aLines.size(); n; ) + { + --n; + aParam.LoopClear(); + aParam1.LoopClear(); + (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false ); + (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false ); + } + } + } + } + break; + + case TableChgWidthHeightType::CellRight: + case TableChgWidthHeightType::CellLeft: + if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode ) + { + // Then call itself recursively; only with another mode (proportional) + TableChgMode eOld = m_eTableChgMode; + m_eTableChgMode = TableChgMode::FixedWidthChangeAbs; + + bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff, + ppUndo ); + m_eTableChgMode = eOld; + return bRet; + } + else if( bLeft ? nDist != 0 : (rSz.GetWidth() - nDist) > COLFUZZY ) + { + if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode ) + aParam.bBigger = !bBigger; + + // First, see if there is enough room at all + SwTableBox* pBox = &rCurrentBox; + SwTableLine* pLine = rCurrentBox.GetUpper(); + while( pLine->GetUpper() ) + { + const SwTableBoxes::size_type nPos = pLine->GetBoxPos( pBox ); + if( bLeft ? nPos != 0 : nPos + 1 != pLine->GetTabBoxes().size() ) + break; + + pBox = pLine->GetUpper(); + pLine = pBox->GetUpper(); + } + + if( pLine->GetUpper() ) + { + // We need to correct the distance once again! + aParam.nSide -= ::lcl_GetDistance( pLine->GetUpper(), true ); + + if( bLeft ) + aParam.nMaxSize = aParam.nSide; + else + aParam.nMaxSize = pLine->GetUpper()->GetFrameFormat()-> + GetFrameSize().GetWidth() - aParam.nSide; + } + + // First, see if there is enough room at all + FN_lcl_SetBoxWidth fnTmp = aParam.bBigger ? fnOtherBox : fnSelBox; + bRet = (*fnTmp)( pLine, aParam, nDistStt, true ); + + // If true, set it + if( bRet ) + { + CR_SetBoxWidth aParam1( aParam ); + if( ppUndo ) + ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true )); + + if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft ) + { + (*fnSelBox)( pLine, aParam, nDistStt, false ); + (*fnOtherBox)( pLine, aParam1, nDistStt, false ); + } + else + { + (*fnOtherBox)( pLine, aParam1, nDistStt, false ); + (*fnSelBox)( pLine, aParam, nDistStt, false ); + } + } + } + break; + default: break; + } + +#if defined DBG_UTIL + if( bRet ) + { + CHECKBOXWIDTH + CHECKTABLELAYOUT + } +#endif + + return bRet; +} + +static void SetLineHeight( SwTableLine& rLine, SwTwips nOldHeight, SwTwips nNewHeight, + bool bMinSize ) +{ + SwLayoutFrame* pLineFrame = GetRowFrame( rLine ); + OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" ); + + SwFrameFormat* pFormat = rLine.ClaimFrameFormat(); + + SwTwips nMyNewH, nMyOldH = pLineFrame->getFrameArea().Height(); + if( !nOldHeight ) // the BaseLine and absolute + nMyNewH = nMyOldH + nNewHeight; + else + { + // Calculate as exactly as possible + Fraction aTmp( nMyOldH ); + aTmp *= Fraction( nNewHeight, nOldHeight ); + aTmp += Fraction( 1, 2 ); // round up if needed + nMyNewH = tools::Long(aTmp); + } + + SwFrameSize eSize = SwFrameSize::Minimum; + if( !bMinSize && + ( nMyOldH - nMyNewH ) > ( CalcRowRstHeight( pLineFrame ) + ROWFUZZY )) + eSize = SwFrameSize::Fixed; + + pFormat->SetFormatAttr( SwFormatFrameSize( eSize, 0, nMyNewH ) ); + + // First adapt all internal ones + for( auto pBox : rLine.GetTabBoxes() ) + { + for( auto pLine : pBox->GetTabLines() ) + SetLineHeight( *pLine, nMyOldH, nMyNewH, bMinSize ); + } +} + +static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam, + SwTwips nDist, bool bCheck ) +{ + bool bRet = true; + if( !bCheck ) + { + // Set line height + SetLineHeight( *pLine, 0, rParam.bBigger ? nDist : -nDist, + rParam.bBigger ); + } + else if( !rParam.bBigger ) + { + // Calculate the new relative size by means of the old one + SwLayoutFrame* pLineFrame = GetRowFrame( *pLine ); + OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" ); + SwTwips nRstHeight = CalcRowRstHeight( pLineFrame ); + if( (nRstHeight + ROWFUZZY) < nDist ) + bRet = false; + } + return bRet; +} + +static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam, + SwTwips nDist, bool bCheck ) +{ + bool bRet = true; + if( bCheck ) + { + if( rParam.bBigger ) + { + // Calculate the new relative size by means of the old one + SwLayoutFrame* pLineFrame = GetRowFrame( *pLine ); + OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" ); + + if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) + { + nDist *= pLineFrame->getFrameArea().Height(); + nDist /= rParam.nMaxHeight; + } + bRet = nDist <= CalcRowRstHeight( pLineFrame ); + } + } + else + { + // Set line height + // pLine is the following/preceding, thus adjust it + if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) + { + SwLayoutFrame* pLineFrame = GetRowFrame( *pLine ); + OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" ); + + // Calculate the new relative size by means of the old one + // If the selected Box get bigger, adjust via the max space else + // via the max height. + if( (true) /*!rParam.bBigger*/ ) + { + nDist *= pLineFrame->getFrameArea().Height(); + nDist /= rParam.nMaxHeight; + } + else + { + // Calculate the new relative size by means of the old one + nDist *= CalcRowRstHeight( pLineFrame ); + nDist /= rParam.nMaxSpace; + } + } + SetLineHeight( *pLine, 0, rParam.bBigger ? -nDist : nDist, + !rParam.bBigger ); + } + return bRet; +} + +bool SwTable::SetRowHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType, + SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo ) +{ + SwTableLine* pLine = rCurrentBox.GetUpper(); + + SwTableLine* pBaseLine = pLine; + while( pBaseLine->GetUpper() ) + pBaseLine = pBaseLine->GetUpper()->GetUpper(); + + bool bBigger, + bRet = false, + bTop = TableChgWidthHeightType::CellTop == extractPosition( eType ); + sal_uInt16 nBaseLinePos = GetTabLines().GetPos( pBaseLine ); + + CR_SetLineHeight aParam( eType, + const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) ); + bBigger = aParam.bBigger; + + SwTableLines* pLines = &m_aLines; + + // How do we get to the height? + switch( extractPosition(eType) ) + { + case TableChgWidthHeightType::CellTop: + case TableChgWidthHeightType::CellBottom: + if( pLine == pBaseLine ) + break; // it doesn't work then! + + // Is a nested Line (Box!) + pLines = &pLine->GetUpper()->GetTabLines(); + nBaseLinePos = pLines->GetPos( pLine ); + [[fallthrough]]; + + case TableChgWidthHeightType::RowBottom: + { + if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode ) + { + // First test if we have room at all + if( bBigger ) + bRet = true; + else + bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam, + nAbsDiff, true ); + + if( bRet ) + { + if( ppUndo ) + ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true )); + + lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam, + nAbsDiff, false ); + } + } + else + { + bRet = true; + SwTableLines::size_type nStt; + SwTableLines::size_type nEnd; + if( bTop ) + { + nStt = 0; + nEnd = nBaseLinePos; + } + else + { + nStt = nBaseLinePos + 1; + nEnd = pLines->size(); + } + + // Get the current Lines' height + if( TableChgMode::FixedWidthChangeProp == m_eTableChgMode ) + { + for( auto n = nStt; n < nEnd; ++n ) + { + SwLayoutFrame* pLineFrame = GetRowFrame( *(*pLines)[ n ] ); + OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" ); + aParam.nMaxSpace += CalcRowRstHeight( pLineFrame ); + aParam.nMaxHeight += pLineFrame->getFrameArea().Height(); + } + if( bBigger && aParam.nMaxSpace < nAbsDiff ) + bRet = false; + } + else + { + if( bTop ? nEnd != 0 : nStt < nEnd ) + { + if( bTop ) + nStt = nEnd - 1; + else + nEnd = nStt + 1; + } + else + bRet = false; + } + + if( bRet ) + { + if( bBigger ) + { + for( auto n = nStt; n < nEnd; ++n ) + { + if( !lcl_SetOtherLineHeight( (*pLines)[ n ], aParam, + nAbsDiff, true )) + { + bRet = false; + break; + } + } + } + else + bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam, + nAbsDiff, true ); + } + + if( bRet ) + { + // Adjust + if( ppUndo ) + ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true )); + + CR_SetLineHeight aParam1( aParam ); + + if( bTop ) + { + lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam, + nAbsDiff, false ); + for( auto n = nStt; n < nEnd; ++n ) + lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1, + nAbsDiff, false ); + } + else + { + for( auto n = nStt; n < nEnd; ++n ) + lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1, + nAbsDiff, false ); + lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam, + nAbsDiff, false ); + } + } + else + { + // Then call itself recursively; only with another mode (proportional) + TableChgMode eOld = m_eTableChgMode; + m_eTableChgMode = TableChgMode::VarWidthChangeAbs; + + bRet = SetRowHeight( rCurrentBox, eType, nAbsDiff, + nRelDiff, ppUndo ); + + m_eTableChgMode = eOld; + } + } + } + break; + default: break; + } + + CHECKTABLELAYOUT + + return bRet; +} + +SwFrameFormat* SwShareBoxFormat::GetFormat( tools::Long nWidth ) const +{ + SwFrameFormat *pRet = nullptr, *pTmp; + for( auto n = m_aNewFormats.size(); n; ) + if( ( pTmp = m_aNewFormats[ --n ])->GetFrameSize().GetWidth() + == nWidth ) + { + pRet = pTmp; + break; + } + return pRet; +} + +SwFrameFormat* SwShareBoxFormat::GetFormat( const SfxPoolItem& rItem ) const +{ + const SfxPoolItem* pItem; + sal_uInt16 nWhich = rItem.Which(); + SwFrameFormat *pRet = nullptr, *pTmp; + const SfxPoolItem& rFrameSz = m_pOldFormat->GetFormatAttr( RES_FRM_SIZE, false ); + for( auto n = m_aNewFormats.size(); n; ) + if( SfxItemState::SET == ( pTmp = m_aNewFormats[ --n ])-> + GetItemState( nWhich, false, &pItem ) && *pItem == rItem && + pTmp->GetFormatAttr( RES_FRM_SIZE, false ) == rFrameSz ) + { + pRet = pTmp; + break; + } + return pRet; +} + +void SwShareBoxFormat::AddFormat( SwFrameFormat& rNew ) +{ + m_aNewFormats.push_back( &rNew ); +} + +bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat ) +{ + // returns true, if we can delete + if( m_pOldFormat == &rFormat ) + return true; + + std::vector<SwFrameFormat*>::iterator it = std::find( m_aNewFormats.begin(), m_aNewFormats.end(), &rFormat ); + if( m_aNewFormats.end() != it ) + m_aNewFormats.erase( it ); + return m_aNewFormats.empty(); +} + +SwShareBoxFormats::~SwShareBoxFormats() +{ +} + +SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, tools::Long nWidth ) const +{ + sal_uInt16 nPos; + return Seek_Entry( rFormat, &nPos ) + ? m_ShareArr[ nPos ].GetFormat(nWidth) + : nullptr; +} +SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, + const SfxPoolItem& rItem ) const +{ + sal_uInt16 nPos; + return Seek_Entry( rFormat, &nPos ) + ? m_ShareArr[ nPos ].GetFormat(rItem) + : nullptr; +} + +void SwShareBoxFormats::AddFormat( const SwFrameFormat& rOld, SwFrameFormat& rNew ) +{ + sal_uInt16 nPos; + if( !Seek_Entry( rOld, &nPos )) + { + SwShareBoxFormat aEntry(rOld); + aEntry.AddFormat( rNew ); + m_ShareArr.insert(m_ShareArr.begin() + nPos, aEntry); + } + else + m_ShareArr[ nPos ].AddFormat(rNew); +} + +void SwShareBoxFormats::ChangeFrameFormat( SwTableBox* pBox, SwTableLine* pLn, + SwFrameFormat& rFormat ) +{ + SwClient aCl; + SwFrameFormat* pOld = nullptr; + if( pBox ) + { + pOld = pBox->GetFrameFormat(); + pOld->Add( &aCl ); + pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(&rFormat) ); + } + else if( pLn ) + { + pOld = pLn->GetFrameFormat(); + pOld->Add( &aCl ); + pLn->ChgFrameFormat( static_cast<SwTableLineFormat*>(&rFormat) ); + } + if( pOld && pOld->HasOnlyOneListener() ) + { + RemoveFormat( *pOld ); + delete pOld; + } +} + +void SwShareBoxFormats::SetSize( SwTableBox& rBox, const SwFormatFrameSize& rSz ) +{ + SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(), + *pRet = GetFormat( *pBoxFormat, rSz.GetWidth() ); + if( pRet ) + ChangeFrameFormat( &rBox, nullptr, *pRet ); + else + { + pRet = rBox.ClaimFrameFormat(); + pRet->SetFormatAttr( rSz ); + AddFormat( *pBoxFormat, *pRet ); + } +} + +void SwShareBoxFormats::SetAttr( SwTableBox& rBox, const SfxPoolItem& rItem ) +{ + SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(), + *pRet = GetFormat( *pBoxFormat, rItem ); + if( pRet ) + ChangeFrameFormat( &rBox, nullptr, *pRet ); + else + { + pRet = rBox.ClaimFrameFormat(); + pRet->SetFormatAttr( rItem ); + AddFormat( *pBoxFormat, *pRet ); + } +} + +void SwShareBoxFormats::SetAttr( SwTableLine& rLine, const SfxPoolItem& rItem ) +{ + SwFrameFormat *pLineFormat = rLine.GetFrameFormat(), + *pRet = GetFormat( *pLineFormat, rItem ); + if( pRet ) + ChangeFrameFormat( nullptr, &rLine, *pRet ); + else + { + pRet = rLine.ClaimFrameFormat(); + pRet->SetFormatAttr( rItem ); + AddFormat( *pLineFormat, *pRet ); + } +} + +void SwShareBoxFormats::RemoveFormat( const SwFrameFormat& rFormat ) +{ + for (auto i = m_ShareArr.size(); i; ) + { + if (m_ShareArr[ --i ].RemoveFormat(rFormat)) + { + m_ShareArr.erase( m_ShareArr.begin() + i ); + } + } +} + +bool SwShareBoxFormats::Seek_Entry( const SwFrameFormat& rFormat, sal_uInt16* pPos ) const +{ + sal_uIntPtr nIdx = reinterpret_cast<sal_uIntPtr>(&rFormat); + auto nO = m_ShareArr.size(); + decltype(nO) nU = 0; + if( nO > 0 ) + { + nO--; + while( nU <= nO ) + { + const auto nM = nU + ( nO - nU ) / 2; + sal_uIntPtr nFormat = reinterpret_cast<sal_uIntPtr>(&m_ShareArr[ nM ].GetOldFormat()); + if( nFormat == nIdx ) + { + if( pPos ) + *pPos = nM; + return true; + } + else if( nFormat < nIdx ) + nU = nM + 1; + else if( nM == 0 ) + { + if( pPos ) + *pPos = nU; + return false; + } + else + nO = nM - 1; + } + } + if( pPos ) + *pPos = nU; + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |