/* -*- 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 .
*/
// 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;
staticvoid 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 = pBox->ClaimFrameFormat();
// Find the selected Boxes in the Line:
FndLine_ const* pCmpLine = nullptr;
SwFormatFrameSize aFrameSz( pNewFormat->GetFrameSize() );
if( 1 == pCpyPara->nDelBorderFlag ||
8 == pCpyPara->nDelBorderFlag )
{ // For all Boxes that delete TopBorderLine, we copy after that
pBox = pCpyPara->pInsLine->GetTabBoxes()[
pCpyPara->nInsPos - 1 ];
}
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 constbool 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.
}
// 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 );
}
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() );
}
// 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();
// 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 = rDoc.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();
// 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 = 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 );
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;
}
}
for( sal_uInt16 i = 0; i <= nCnt; ++i )
{ // Create a new Line in the new Box
SwTableLine* pNewLine = new SwTableLine(
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 );
// 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 );
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
/* * >> 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 <<
*/ staticvoid 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 );
}
staticvoid 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" );
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(
pULPara->pLeftBox->GetFrameFormat(), 0, pInsLine );
SwTableLine* pLMLn = new SwTableLine(
pInsLine->GetFrameFormat(), 2, pLMBox );
pLMLn->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );
// 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( pUndo )
pUndo->SetSelBoxes( rBoxes );
// Find Lines for the Layout update
aFndBox.SetTableLines( *this );
aFndBox.DelFrames( *this );
// 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 ); constauto 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 );
}