/* -*- 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 <hintids.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/protitem.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 <cntfrm.hxx>
#include <docsh.hxx>
#include <fesh.hxx>
#include <tabfrm.hxx>
#include <frmtool.hxx>
#include <pam.hxx>
#include <swtable.hxx>
#include <ndtxt.hxx>
#include <tblafmt.hxx>
#include <tblsel.hxx>
#include <fldbas.hxx>
#include <swundo.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
// 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
{
union {
SwFrameFormat* pFrameFormat; // for CopyCol
SwTwips nSize; // for DelCol
} Value;
SwTableBoxFormat *pNewFrameFormat;
explicit CpyTabFrame(SwFrameFormat* pCurrentFrameFormat) : pNewFrameFormat( nullptr )
{ Value.pFrameFormat = pCurrentFrameFormat; }
bool operator==( const CpyTabFrame& rCpyTabFrame ) const
{ return static_cast<sal_uLong>(Value.nSize) == static_cast<sal_uLong>(rCpyTabFrame.Value.nSize); }
bool operator<( const CpyTabFrame& rCpyTabFrame ) const
{ return static_cast<sal_uLong>(Value.nSize) < static_cast<sal_uLong>(rCpyTabFrame.Value.nSize); }
};
struct CR_SetBoxWidth
{
SwSelBoxes m_Boxes;
SwShareBoxFormats aShareFormats;
SwTableNode* pTableNd;
SwUndoTableNdsChg* pUndo;
SwTwips nDiff, nSide, nMaxSize, nLowerDiff;
TableChgMode nMode;
sal_uInt16 nTableWidth, nRemainWidth, nBoxWidth;
bool bBigger, bLeft, bSplittBox, bAnyBoxFnd;
CR_SetBoxWidth( TableChgWidthHeightType eType, SwTwips nDif, SwTwips nSid, SwTwips nTableW,
SwTwips nMax, SwTableNode* pTNd )
: pTableNd( pTNd ), pUndo( nullptr ),
nDiff( nDif ), nSide( nSid ), nMaxSize( nMax ), nLowerDiff( 0 ),
nTableWidth( static_cast<sal_uInt16>(nTableW) ), nRemainWidth( 0 ), nBoxWidth( 0 ),
bSplittBox( false ), bAnyBoxFnd( false )
{
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 ),
pUndo( rCpy.pUndo ),
nDiff( rCpy.nDiff ), nSide( rCpy.nSide ),
nMaxSize( rCpy.nMaxSize ), nLowerDiff( 0 ),
nMode( rCpy.nMode ), nTableWidth( rCpy.nTableWidth ),
nRemainWidth( rCpy.nRemainWidth ), nBoxWidth( rCpy.nBoxWidth ),
bBigger( rCpy.bBigger ), bLeft( rCpy.bLeft ),
bSplittBox( rCpy.bSplittBox ), bAnyBoxFnd( rCpy.bAnyBoxFnd )
{
}
SwUndoTableNdsChg* CreateUndo( SwUndoId eUndoType )
{
return pUndo = new SwUndoTableNdsChg( eUndoType, m_Boxes, *pTableNd );
}
void LoopClear()
{
nLowerDiff = 0; nRemainWidth = 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 );
static bool lcl_InsSelBox( SwTableLine* pLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck );
static bool lcl_InsOtherBox( SwTableLine* pLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck );
static bool lcl_DelSelBox( SwTableLine* pLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck );
static bool lcl_DelOtherBox( 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
struct CR_SetLineHeight
{
SwSelBoxes m_Boxes;
SwShareBoxFormats aShareFormats;
SwTableNode* pTableNd;
SwUndoTableNdsChg* pUndo;
SwTwips nMaxSpace, nMaxHeight;
TableChgMode nMode;
bool bBigger, bTop;
CR_SetLineHeight( TableChgWidthHeightType eType, SwTableNode* pTNd )
: pTableNd( pTNd ), pUndo( nullptr ),
nMaxSpace( 0 ), nMaxHeight( 0 )
{
bTop = TableChgWidthHeightType::CellTop == extractPosition( eType );
bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
if( eType & TableChgWidthHeightType::InsertDeleteMode )
bBigger = !bBigger;
nMode = pTableNd->GetTable().GetTableChgMode();
}
CR_SetLineHeight( const CR_SetLineHeight& rCpy )
: pTableNd( rCpy.pTableNd ), pUndo( rCpy.pUndo ),
nMaxSpace( rCpy.nMaxSpace ), nMaxHeight( rCpy.nMaxHeight ),
nMode( rCpy.nMode ),
bBigger( rCpy.bBigger ), bTop( rCpy.bTop )
{}
SwUndoTableNdsChg* CreateUndo( SwUndoId nUndoType )
{
return pUndo = new SwUndoTableNdsChg( nUndoType, m_Boxes, *pTableNd );
}
};
static bool lcl_SetSelLineHeight( SwTableLine* pLine, CR_SetLineHeight& rParam,
SwTwips nDist, bool bCheck );
static bool lcl_SetOtherLineHeight( SwTableLine* pLine, CR_SetLineHeight& rParam,
SwTwips nDist, bool bCheck );
static bool lcl_InsDelSelLine( SwTableLine* pLine, CR_SetLineHeight& rParam,
SwTwips nDist, bool bCheck );
typedef bool (*FN_lcl_SetLineHeight)(SwTableLine*, CR_SetLineHeight&, SwTwips, bool );
typedef o3tl::sorted_vector<CpyTabFrame> CpyTabFrames;
struct CpyPara
{
std::shared_ptr< std::vector< std::vector< sal_uLong > > > pWidths;
SwDoc* pDoc;
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 )
: pDoc( 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 ), pDoc(rPara.pDoc), 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 ), pDoc(rPara.pDoc), 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 void 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());
sal_uInt16 nFndPos;
if( pCpyPara->nCpyCnt )
{
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().size() )
{
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();
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.Value.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->pDoc, 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.Value.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 void 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;
}
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);
}
}
}
}
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* pDoc, 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( pDoc, 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 = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
if (pPCD && nCnt)
pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
pDoc->UpdateCharts( GetFrameFormat()->GetName() );
pDoc->GetDocShell()->GetFEShell()->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() &&
1 == (pFndLine = pFndBox->GetLines()[0].get())->GetBoxes().size())
{
// 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())
lcl_CopyRow( *rpFndLine, &aCpyPara );
}
// 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() );
pDoc->GetDocShell()->GetFEShell()->UpdateTableStyleFormatting( pTableNd );
return true;
}
static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const long nOffset,
bool bFirst, SwShareBoxFormats& rShareFormats );
static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const long nOffset,
bool bFirst, SwShareBoxFormats& rShareFormats )
{
for ( auto pLine : rLines )
::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats );
}
static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const 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 < static_cast<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 < static_cast<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
if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() )
rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableCellRedline( rTable.GetFrameFormat()->GetDoc(), *(rTableBoxes[nDelPos]), true, USHRT_MAX );
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
if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() )
rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, USHRT_MAX );
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
if ( rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().HasExtraRedlineTable() )
rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, USHRT_MAX );
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 < static_cast<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 < static_cast<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() )
{
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* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
bool bSameHeight )
{
OSL_ENSURE( pDoc && !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
pDoc->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
long* pRowHeights = nullptr;
if ( bSameHeight )
{
pRowHeights = new 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 && ATT_VAR_SIZE == aFSz.GetHeightSizeType() )
aFSz.SetHeightSizeType( ATT_MIN_SIZE );
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;
{
sal_uLong nSttNd = pLastBox->GetSttIdx() + 1,
nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex();
while( nSttNd < nEndNd )
if( !pDoc->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( pDoc, 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()+2 != pEndNd->GetIndex() )
{
// Move TextNodes
SwNodeRange aRg( *pLastBox->GetSttNd(), +2, *pEndNd );
pLastBox = pNewLine->GetTabBoxes()[0]; // reset
SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 );
pDoc->GetNodes().MoveNodes(aRg, pDoc->GetNodes(), aInsPos, false);
pDoc->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 );
}
delete[] pRowHeights;
GCLines();
aFndBox.MakeFrames( *this );
CHECKBOXWIDTH
CHECKTABLELAYOUT
return true;
}
bool SwTable::SplitCol( SwDoc* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
{
OSL_ENSURE( pDoc && !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
pDoc->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( ATT_VAR_SIZE,
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( ATT_VAR_SIZE,
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( pDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat,
pSelBox, nBoxPos + i ); // insert after
::InsTableBox( pDoc, 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.Value.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" );
long nWidth = 0;
for( auto pTabBox : pLine->GetTabBoxes() )
nWidth += pTabBox->GetFrameFormat()->GetFrameSize().GetWidth();
pFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, 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 );
}
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
if( 0 != ( nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox ) ) )
lcl_CpyBoxes( 0, nPos, *pBoxes, pULPara->pInsLine );
}
else // Right
// if there are Boxes behind it, move them
if( (nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox )) +1 < static_cast<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().size())
{
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().size() )
{
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
if( 0 != ( nPos = pLines->GetPos( pFndLn )) )
lcl_CpyLines( 0, nPos, *pLines, pULPara->pInsBox );
}
else
// If there are Lines after it, move them
if( (nPos = pLines->GetPos( pFndLn )) + 1 < static_cast<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
if( 0 != ( nPos = pLines->GetPos( pFndLn )) )
lcl_CpyLines( 0, nPos, *pLines, pLMBox, 0 );
}
else
// If there are Lines after it, move them
if( (nPos = pLines->GetPos( pFndLn )) + 1 < static_cast<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( SwTableBoxes::iterator it = GetTabLines()[0]->GetTabBoxes().begin();
it != GetTabLines()[0]->GetTabBoxes().end(); ++it)
lcl_BoxSetHeadCondColl(*it);
aFndBox.MakeFrames( *this );
CHECKBOXWIDTH
CHECKTABLELAYOUT
return true;
}
static void lcl_CheckRowSpan( SwTable &rTable )
{
const long nLineCount = static_cast<long>(rTable.GetTabLines().size());
long nMaxSpan = nLineCount;
long nMinSpan = 1;
while( nMaxSpan )
{
SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ];
for( auto pBox : pLine->GetTabBoxes() )
{
long 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 + static_cast<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 + static_cast<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.get())[ nLine ];
const FndLine_ *pFndLine = rFndLines[ nLine ].get();
if( pFndLine && pFndLine->GetBoxes().size() )
{
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 )
{
for( size_t nLine = 0; nLine < nLineCount; ++nLine )
{
std::vector< sal_uLong > &rWidth = (*rPara.pWidths.get())[ 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.get())[pCpyPara->nLnIdx][0];
nRealSize = (*pCpyPara->pWidths.get())[pCpyPara->nLnIdx][pCpyPara->nBoxIdx++];
if( pCpyPara->nBoxIdx == (*pCpyPara->pWidths.get())[pCpyPara->nLnIdx].size()-1 )
nDummy2 = (*pCpyPara->pWidths.get())[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()));
SwFormatFrameSize aFrameSz;
CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
const CpyTabFrames::size_type nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) ||
( aFrameSz = ( aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]).pNewFrameFormat->
GetFrameSize()).GetWidth() != static_cast<SwTwips>(nSize) )
{
// It doesn't exist yet, so copy it
aFindFrame.pNewFrameFormat = pCpyPara->pDoc->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->pDoc->GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine,
aFindFrame.pNewFrameFormat,
pCpyPara->pDoc->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
{
SfxItemSet aBoxAttrSet( pCpyPara->pDoc->GetAttrPool(),
svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} );
aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet());
if( aBoxAttrSet.Count() )
{
const SfxPoolItem* pItem;
SvNumberFormatter* pN = pCpyPara->pDoc->GetNumberFormatter( false );
if( pN && pN->HasMergeFormatTable() && SfxItemState::SET == aBoxAttrSet.
GetItemState( RES_BOXATR_FORMAT, false, &pItem ) )
{
sal_uLong nOldIdx = static_cast<const SwTableBoxNumFormat*>(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(), 1,
*rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() );
SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 );
pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly( aCpyRg, 0, aInsIdx, nullptr, false );
// Delete the initial TextNode
pCpyPara->pDoc->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->pDoc->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 (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* pInsDoc, const SwPosition& rPos,
const SwSelBoxes& rSelBoxes,
bool bCpyName ) 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 != pInsDoc )
{
pInsDoc->CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) );
pInsDoc->CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) );
}
SwTable* pNewTable = const_cast<SwTable*>(pInsDoc->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());
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 = pInsDoc->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();
{
// Conver the Table formulas to their relative representation
SwTableFormulaUpdate aMsgHint( this );
aMsgHint.m_eFlags = TBL_RELBOXNAME;
pSrcDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
}
SwTableNumFormatMerge aTNFM( *pSrcDoc, *pInsDoc );
// 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 (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( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_Box_CollectBox( *it, &aLnPara );
if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
lcl_GetLineWidth( *pFndLn )) )
{
aLnPara.SetValues( true );
pLn = pNewTable->GetTabLines()[ 0 ];
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_BoxSetSplitBoxFormats(*it, &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( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_Box_CollectBox( *it, &aLnPara );
if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
lcl_GetLineWidth( *pFndLn )) )
{
aLnPara.SetValues( false );
pLn = pNewTable->GetTabLines().back();
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_BoxSetSplitBoxFormats(*it, &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->MakeFrames( &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 &&
USHRT_MAX != ( nFndPos = GetBoxPos( pSrchBox )) &&
nFndPos + 1 != static_cast<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 >= static_cast<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 >= static_cast<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 &&
USHRT_MAX != ( nFndPos = GetBoxPos( pSrchBox )) &&
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 && nullptr != ( pLine = pBox->GetUpper() ) )
{
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)))
{
rParam.bAnyBoxFnd = true;
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 )) )
{
rParam.bAnyBoxFnd = true;
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 bool lcl_InsSelBox( SwTableLine* pLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck )
{
SwTableBoxes& rBoxes = pLine->GetTabBoxes();
for( size_t n = 0; n < rBoxes.size(); ++n )
{
SwTableBox* pBox = rBoxes[ n ];
SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
SwTwips nWidth = rSz.GetWidth();
int nCmp {0};
if( bCheck )
{
for( size_t i = 0; i < pBox->GetTabLines().size(); ++i )
if( !::lcl_InsSelBox( pBox->GetTabLines()[ i ], rParam,
nDist, true ))
return false;
// Collect all "ContentBoxes"
if( std::abs( nDist + ( rParam.bLeft ? 0 : nWidth )
- rParam.nSide ) < COLFUZZY )
nCmp = 1;
else if( nDist + ( rParam.bLeft ? 0 : nWidth/2 ) > rParam.nSide )
nCmp = 2;
if( nCmp )
{
rParam.bAnyBoxFnd = true;
if( pFormat->GetProtect().IsContentProtected() )
return false;
if( rParam.bSplittBox &&
nWidth - rParam.nDiff <= COLFUZZY +
( 567 / 2 /*leave room for at least 0.5 cm*/) )
return false;
if( pBox->GetSttNd() )
{
rParam.m_Boxes.insert(pBox);
}
break;
}
}
else
{
SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
for( auto pLn : pBox->GetTabLines() )
{
rParam.nLowerDiff = 0;
lcl_InsSelBox( pLn, rParam, nDist, false );
if( nLowerDiff < rParam.nLowerDiff )
nLowerDiff = rParam.nLowerDiff;
}
rParam.nLowerDiff = nOldLower;
if( nLowerDiff )
nCmp = 1;
else if( std::abs( nDist + ( rParam.bLeft ? 0 : nWidth )
- rParam.nSide ) < COLFUZZY )
nCmp = 2;
else if( nDist + nWidth / 2 > rParam.nSide )
nCmp = 3;
if( nCmp )
{
// This column contains the Cursor - so decrease/increase
if( 1 == nCmp )
{
if( !rParam.bSplittBox )
{
// the current Box on
SwFormatFrameSize aNew( rSz );
aNew.SetWidth( nWidth + rParam.nDiff );
rParam.aShareFormats.SetSize( *pBox, aNew );
}
}
else
{
OSL_ENSURE( pBox->GetSttNd(), "This must be an EndBox!");
if( !rParam.bLeft && 3 != nCmp )
++n;
::InsTableBox( pFormat->GetDoc(), rParam.pTableNd,
pLine, pFormat, pBox, n );
SwTableBox* pNewBox = rBoxes[ n ];
SwFormatFrameSize aNew( rSz );
aNew.SetWidth( rParam.nDiff );
rParam.aShareFormats.SetSize( *pNewBox, aNew );
// Special case: There is no space in the other Boxes, but in the Cell
if( rParam.bSplittBox )
{
// the current Box on
SwFormatFrameSize aNewSize( rSz );
aNewSize.SetWidth( nWidth - rParam.nDiff );
rParam.aShareFormats.SetSize( *pBox, aNewSize );
}
// Special treatment for the Border
// The right one needs to be removed
{
const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
if( rBoxItem.GetRight() )
{
SvxBoxItem aTmp( rBoxItem );
aTmp.SetLine( nullptr, SvxBoxItemLine::RIGHT );
rParam.aShareFormats.SetAttr( rParam.bLeft
? *pNewBox
: *pBox, aTmp );
}
}
}
rParam.nLowerDiff = rParam.nDiff;
break;
}
}
if( rParam.bLeft && rParam.nMode != TableChgMode::FixedWidthChangeAbs && nDist >= rParam.nSide )
break;
nDist += nWidth;
}
return true;
}
static bool lcl_InsOtherBox( SwTableLine* pLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck )
{
// Special case: There is no space in the other Boxes, but in the cell
if( rParam.bSplittBox )
return true;
SwTableBoxes& rBoxes = pLine->GetTabBoxes();
// Table fixed, proportional
if( !rParam.nRemainWidth && TableChgMode::FixedWidthChangeProp == rParam.nMode )
{
// Find the right width to which the relative width adjustment
// corresponds to
SwTwips nTmpDist = nDist;
for( auto pBox : rBoxes )
{
SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
if( (nTmpDist + nWidth / 2 ) > rParam.nSide )
{
rParam.nRemainWidth = rParam.bLeft
? sal_uInt16(nTmpDist)
: sal_uInt16(rParam.nTableWidth - nTmpDist);
break;
}
nTmpDist += nWidth;
}
}
for( SwTableBoxes::size_type n = 0; n < rBoxes.size(); ++n )
{
SwTableBox* pBox = rBoxes[ n ];
SwFrameFormat* pFormat = pBox->GetFrameFormat();
const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
SwTwips nWidth = rSz.GetWidth();
if( bCheck )
{
for( auto pLn : pBox->GetTabLines() )
if( !::lcl_InsOtherBox( pLn, rParam, nDist, true ))
return false;
if(
rParam.bLeft ? ((nDist + nWidth / 2 ) <= rParam.nSide &&
(TableChgMode::FixedWidthChangeAbs != rParam.nMode ||
(n < rBoxes.size() &&
(nDist + nWidth + rBoxes[ n+1 ]->
GetFrameFormat()->GetFrameSize().GetWidth() / 2)
> rParam.nSide) ))
: (nDist + nWidth / 2 ) > rParam.nSide
)
{
rParam.bAnyBoxFnd = true;
SwTwips nDiff;
if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
{
// calculate relatively
nDiff = nWidth;
nDiff *= rParam.nDiff;
nDiff /= rParam.nRemainWidth;
if( nWidth < nDiff || nWidth - nDiff < MINLAY )
return false;
}
else
{
nDiff = rParam.nDiff;
// See if the left or right Box is big enough to give up space.
// We're inserting a Box before or after.
SwTwips nTmpWidth = nWidth;
if( rParam.bLeft && pBox->GetUpper()->GetUpper() )
{
const SwTableBox* pTmpBox = pBox;
sal_uInt16 nBoxPos = n;
while( !nBoxPos && pTmpBox->GetUpper()->GetUpper() )
{
pTmpBox = pTmpBox->GetUpper()->GetUpper();
nBoxPos = pTmpBox->GetUpper()->GetBoxPos( pTmpBox );
}
nTmpWidth = pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
}
if( nTmpWidth < nDiff || nTmpWidth - nDiff < MINLAY )
return false;
break;
}
}
}
else
{
SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
for( auto pLn : pBox->GetTabLines() )
{
rParam.nLowerDiff = 0;
lcl_InsOtherBox( pLn, rParam, nDist, false );
if( nLowerDiff < rParam.nLowerDiff )
nLowerDiff = rParam.nLowerDiff;
}
rParam.nLowerDiff = nOldLower;
if( nLowerDiff ||
(rParam.bLeft ? ((nDist + nWidth / 2 ) <= rParam.nSide &&
(TableChgMode::FixedWidthChangeAbs != rParam.nMode ||
(n < rBoxes.size() &&
(nDist + nWidth + rBoxes[ n+1 ]->
GetFrameFormat()->GetFrameSize().GetWidth() / 2)
> rParam.nSide) ))
: (nDist + nWidth / 2 ) > rParam.nSide ))
{
if( !nLowerDiff )
{
if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
{
// Calculate relatively
nLowerDiff = nWidth;
nLowerDiff *= rParam.nDiff;
nLowerDiff /= rParam.nRemainWidth;
}
else
nLowerDiff = rParam.nDiff;
}
SwFormatFrameSize aNew( rSz );
rParam.nLowerDiff += nLowerDiff;
if( rParam.bBigger )
aNew.SetWidth( nWidth - nLowerDiff );
else
aNew.SetWidth( nWidth + nLowerDiff );
rParam.aShareFormats.SetSize( *pBox, aNew );
if( TableChgMode::FixedWidthChangeAbs == rParam.nMode )
break;
}
}
nDist += nWidth;
}
return true;
}
// The position comparison's result
// SwComparePosition::Before, // Box comes before
// SwComparePosition::Behind, // Box comes after
// SwComparePosition::Inside, // Box is completely within start/end
// SwComparePosition::Outside, // Box overlaps start/end completely
// SwComparePosition::Equal, // Box and start/end are the same
// SwComparePosition::OverlapBefore, // Box overlapps the start
// SwComparePosition::OverlapBehind // Box overlapps the end
SwComparePosition CheckBoxInRange( sal_uInt16 nStt, sal_uInt16 nEnd,
sal_uInt16 nBoxStt, sal_uInt16 nBoxEnd )
{
// Still treat COLFUZZY!
SwComparePosition nRet;
if( nBoxStt + COLFUZZY < nStt )
{
if( nBoxEnd > nStt + COLFUZZY )
{
if( nBoxEnd >= nEnd + COLFUZZY )
nRet = SwComparePosition::Outside;
else
nRet = SwComparePosition::OverlapBefore;
}
else
nRet = SwComparePosition::Before;
}
else if( nEnd > nBoxStt + COLFUZZY )
{
if( nEnd + COLFUZZY >= nBoxEnd )
{
if( COLFUZZY > std::abs( long(nEnd) - long(nBoxEnd) ) &&
COLFUZZY > std::abs( long(nStt) - long(nBoxStt) ) )
nRet = SwComparePosition::Equal;
else
nRet = SwComparePosition::Inside;
}
else
nRet = SwComparePosition::OverlapBehind;
}
else
nRet = SwComparePosition::Behind;
return nRet;
}
static void lcl_DelSelBox_CorrLowers( SwTableLine& rLine, CR_SetBoxWidth& rParam,
SwTwips nWidth )
{
// 1. step: Calculate own width
SwTableBoxes& rBoxes = rLine.GetTabBoxes();
SwTwips nBoxWidth = 0;
for( auto n = rBoxes.size(); n; )
nBoxWidth += rBoxes[ --n ]->GetFrameFormat()->GetFrameSize().GetWidth();
if( COLFUZZY < std::abs( nWidth - nBoxWidth ))
{
// Thus, they need to be adjusted
for( auto n = rBoxes.size(); n; )
{
SwTableBox* pBox = rBoxes[ --n ];
SwFormatFrameSize aNew( pBox->GetFrameFormat()->GetFrameSize() );
long nDiff = aNew.GetWidth();
nDiff *= nWidth;
nDiff /= nBoxWidth;
aNew.SetWidth( nDiff );
rParam.aShareFormats.SetSize( *pBox, aNew );
if( !pBox->GetSttNd() )
{
// Has Lower itself, so also adjust that
for( auto i = pBox->GetTabLines().size(); i; )
::lcl_DelSelBox_CorrLowers( *pBox->GetTabLines()[ --i ],
rParam, nDiff );
}
}
}
}
static void lcl_ChgBoxSize( SwTableBox& rBox, CR_SetBoxWidth& rParam,
const SwFormatFrameSize& rOldSz,
sal_uInt16& rDelWidth, SwTwips nDist )
{
long nDiff = 0;
bool bSetSize = false;
switch( rParam.nMode )
{
case TableChgMode::FixedWidthChangeAbs: // Fixed width table, change neighbor
nDiff = rDelWidth + rParam.nLowerDiff;
bSetSize = true;
break;
case TableChgMode::FixedWidthChangeProp: // Fixed width table, change all neighbors
if( !rParam.nRemainWidth )
{
// Calculate
if( rParam.bLeft )
rParam.nRemainWidth = sal_uInt16(nDist);
else
rParam.nRemainWidth = sal_uInt16(rParam.nTableWidth - nDist);
}
// Calculate relatively
nDiff = rOldSz.GetWidth();
nDiff *= rDelWidth + rParam.nLowerDiff;
nDiff /= rParam.nRemainWidth;
bSetSize = true;
break;
case TableChgMode::VarWidthChangeAbs: // Variable table, change all neighbors
if( COLFUZZY < std::abs( rParam.nBoxWidth -
( rDelWidth + rParam.nLowerDiff )))
{
nDiff = rDelWidth + rParam.nLowerDiff - rParam.nBoxWidth;
if( 0 < nDiff )
rDelWidth = rDelWidth - sal_uInt16(nDiff);
else
rDelWidth = rDelWidth + sal_uInt16(-nDiff);
bSetSize = true;
}
break;
}
if( bSetSize )
{
SwFormatFrameSize aNew( rOldSz );
aNew.SetWidth( aNew.GetWidth() + nDiff );
rParam.aShareFormats.SetSize( rBox, aNew );
// Change the Lower once again
for( auto i = rBox.GetTabLines().size(); i; )
::lcl_DelSelBox_CorrLowers( *rBox.GetTabLines()[ --i ], rParam,
aNew.GetWidth() );
}
}
static bool lcl_DeleteBox_Recursive( CR_SetBoxWidth& rParam, SwTableBox& rBox,
bool bCheck )
{
bool bRet = true;
if( rBox.GetSttNd() )
{
if( bCheck )
{
rParam.bAnyBoxFnd = true;
if( rBox.GetFrameFormat()->GetProtect().IsContentProtected() )
bRet = false;
else
{
SwTableBox* pBox = &rBox;
rParam.m_Boxes.insert(pBox);
}
}
else
::DeleteBox_( rParam.pTableNd->GetTable(), &rBox,
rParam.pUndo, false, true, &rParam.aShareFormats );
}
else
{
// We need to delete these sequentially via the ContentBoxes
for( auto i = rBox.GetTabLines().size(); i; )
{
SwTableLine& rLine = *rBox.GetTabLines()[ --i ];
for( auto n = rLine.GetTabBoxes().size(); n; )
{
if (!::lcl_DeleteBox_Recursive( rParam,
*rLine.GetTabBoxes()[ --n ], bCheck ))
{
return false;
}
}
}
}
return bRet;
}
static bool lcl_DelSelBox( SwTableLine* pTabLine, CR_SetBoxWidth& rParam,
SwTwips nDist, bool bCheck )
{
SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
SwTableBoxes::size_type n;
SwTableBoxes::size_type nCntEnd;
sal_uInt16 nBoxChkStt, nBoxChkEnd, nDelWidth = 0;
if( rParam.bLeft )
{
n = rBoxes.size();
nCntEnd = 0;
nBoxChkStt = static_cast<sal_uInt16>(rParam.nSide);
nBoxChkEnd = static_cast<sal_uInt16>(rParam.nSide + rParam.nBoxWidth);
}
else
{
n = 0;
nCntEnd = rBoxes.size();
nBoxChkStt = static_cast<sal_uInt16>(rParam.nSide - rParam.nBoxWidth);
nBoxChkEnd = static_cast<sal_uInt16>(rParam.nSide);
}
while( n != nCntEnd )
{
SwTableBox* pBox;
if( rParam.bLeft )
pBox = rBoxes[ --n ];
else
pBox = rBoxes[ n++ ];
SwFrameFormat* pFormat = pBox->GetFrameFormat();
const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
long nWidth = rSz.GetWidth();
bool bDelBox = false, bChgLowers = false;
// Test the Box width and react accordingly
SwComparePosition ePosType = ::CheckBoxInRange(
nBoxChkStt, nBoxChkEnd,
sal_uInt16(rParam.bLeft ? nDist - nWidth : nDist),
sal_uInt16(rParam.bLeft ? nDist : nDist + nWidth));
switch( ePosType )
{
case SwComparePosition::Before:
if( bCheck )
{
if( rParam.bLeft )
return true;
}
else if( rParam.bLeft )
{
::lcl_ChgBoxSize( *pBox, rParam, rSz, nDelWidth, nDist );
if( TableChgMode::FixedWidthChangeAbs == rParam.nMode )
n = nCntEnd;
}
break;
case SwComparePosition::Behind:
if( bCheck )
{
if( !rParam.bLeft )
return true;
}
else if( !rParam.bLeft )
{
::lcl_ChgBoxSize( *pBox, rParam, rSz, nDelWidth, nDist );
if( TableChgMode::FixedWidthChangeAbs == rParam.nMode )
n = nCntEnd;
}
break;
case SwComparePosition::Outside: // Box fully overlaps start/end
case SwComparePosition::Inside: // Box is completely within start/end
case SwComparePosition::Equal: // Box and start/end are the same
bDelBox = true;
break;
case SwComparePosition::OverlapBefore: // Box overlaps the start
if( nBoxChkStt <= ( nDist + (rParam.bLeft ? - nWidth / 2
: nWidth / 2 )))
{
if( !pBox->GetSttNd() )
bChgLowers = true;
else
bDelBox = true;
}
else if( !bCheck && rParam.bLeft )
{
if( !pBox->GetSttNd() )
bChgLowers = true;
else
{
::lcl_ChgBoxSize( *pBox, rParam, rSz, nDelWidth, nDist );
if( TableChgMode::FixedWidthChangeAbs == rParam.nMode )
n = nCntEnd;
}
}
break;
case SwComparePosition::OverlapBehind: // Box overlaps the end
// JP 10.02.99:
// Delete generally or (like in OVERLAP_BEFORE) only delete the one who reaches up to the half into the delete Box?
if( !pBox->GetSttNd() )
bChgLowers = true;
else
bDelBox = true;
break;
default: break;
}
if( bDelBox )
{
nDelWidth = nDelWidth + sal_uInt16(nWidth);
if( bCheck )
{
// The last/first Box can only be deleted for the variable Table,
// if it's as large as the change in the Table.
if( (( TableChgMode::VarWidthChangeAbs != rParam.nMode ||
nDelWidth != rParam.nBoxWidth ) &&
COLFUZZY > std::abs( rParam.bLeft
? nWidth - nDist
: (nDist + nWidth - rParam.nTableWidth )))
|| !::lcl_DeleteBox_Recursive(rParam, *pBox, bCheck))
{
return false;
}
if( pFormat->GetProtect().IsContentProtected() )
return false;
}
else
{
::lcl_DeleteBox_Recursive(rParam, *pBox, bCheck);
if( !rParam.bLeft )
{
--n;
--nCntEnd;
}
}
}
else if( bChgLowers )
{
bool bFirst = true, bCorrLowers = false;
long nLowerDiff = 0;
long nOldLower = rParam.nLowerDiff;
sal_uInt16 nOldRemain = rParam.nRemainWidth;
for( auto i = pBox->GetTabLines().size(); i; )
{
rParam.nLowerDiff = nDelWidth + nOldLower;
rParam.nRemainWidth = nOldRemain;
SwTableLine* pLine = pBox->GetTabLines()[ --i ];
if( !::lcl_DelSelBox( pLine, rParam, nDist, bCheck ))
return false;
// Do the Box and its Lines still exist?
if( n < rBoxes.size() &&
pBox == rBoxes[ rParam.bLeft ? n : n-1 ] &&
i < pBox->GetTabLines().size() &&
pLine == pBox->GetTabLines()[ i ] )
{
if( !bFirst && !bCorrLowers &&
COLFUZZY < std::abs( nLowerDiff - rParam.nLowerDiff ) )
bCorrLowers = true;
// The largest deletion width counts, but only if we don't
// delete the whole Line
if( nLowerDiff < rParam.nLowerDiff )
nLowerDiff = rParam.nLowerDiff;
bFirst = false;
}
}
rParam.nLowerDiff = nOldLower;
rParam.nRemainWidth = nOldRemain;
// Did we delete all Boxes? Then the deletion width = the Box width, of course
if( !nLowerDiff )
nLowerDiff = nWidth;
// Adjust deletion width!
nDelWidth = nDelWidth + sal_uInt16(nLowerDiff);
if( !bCheck )
{
// Has the Box already been removed?
if( n > rBoxes.size() ||
pBox != rBoxes[ ( rParam.bLeft ? n : n-1 ) ] )
{
// Then change the loop variable when deleting to the right
if( !rParam.bLeft )
{
--n;
--nCntEnd;
}
}
else
{
// Or else we need to adapt the Box's size
SwFormatFrameSize aNew( rSz );
bool bCorrRel = false;
if( TableChgMode::VarWidthChangeAbs != rParam.nMode )
{
switch( ePosType )
{
case SwComparePosition::OverlapBefore: // Box overlaps the start
if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
bCorrRel = rParam.bLeft;
else if( rParam.bLeft ) // TableChgMode::FixedWidthChangeAbs
{
nLowerDiff = nLowerDiff - nDelWidth;
bCorrLowers = true;
n = nCntEnd;
}
break;
case SwComparePosition::OverlapBehind: // Box overlaps the end
if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
bCorrRel = !rParam.bLeft;
else if( !rParam.bLeft ) // TableChgMode::FixedWidthChangeAbs
{
nLowerDiff = nLowerDiff - nDelWidth;
bCorrLowers = true;
n = nCntEnd;
}
break;
default:
OSL_ENSURE( !pBox, "we should never reach this!" );
break;
}
}
if( bCorrRel )
{
if( !rParam.nRemainWidth )
{
// Calculate
if( rParam.bLeft )
rParam.nRemainWidth = sal_uInt16(nDist - nLowerDiff);
else
rParam.nRemainWidth = sal_uInt16(rParam.nTableWidth - nDist
- nLowerDiff );
}
long nDiff = aNew.GetWidth() - nLowerDiff;
nDiff *= nDelWidth + rParam.nLowerDiff;
nDiff /= rParam.nRemainWidth;
aNew.SetWidth( aNew.GetWidth() - nLowerDiff + nDiff );
}
else
aNew.SetWidth( aNew.GetWidth() - nLowerDiff );
rParam.aShareFormats.SetSize( *pBox, aNew );
if( bCorrLowers )
{
// Adapt the Lower once again
for( auto i = pBox->GetTabLines().size(); i; )
::lcl_DelSelBox_CorrLowers( *pBox->
GetTabLines()[ --i ], rParam, aNew.GetWidth() );
}
}
}
}
if( rParam.bLeft )
nDist -= nWidth;
else
nDist += nWidth;
}
rParam.nLowerDiff = nDelWidth;
return true;
}
// Dummy function for the method SetColWidth
static bool lcl_DelOtherBox( SwTableLine* , CR_SetBoxWidth& , SwTwips , bool )
{
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 (SwTableBoxes::const_iterator i(rBoxes.begin()); i != rBoxes.end(); ++i)
{
const SwTableBox* pBox = *i;
const SwTwips nBoxW = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
nCurrentSize += nBoxW;
for( auto pLn : pBox->GetTabLines() )
CheckBoxWidth( *pLn, nBoxW );
}
if (sal::static_int_cast< unsigned long >(std::abs(nCurrentSize - nSize)) >
(COLFUZZY * rBoxes.size()))
{
OSL_FAIL( "Line's Boxes are too small or too large" );
}
}
#endif
static FndBox_* lcl_SaveInsDelData( CR_SetBoxWidth& rParam, SwUndo** ppUndo,
SwTableSortBoxes& rTmpLst, SwTwips nDistStt )
{
// Find all Boxes/Lines
SwTable& rTable = rParam.pTableNd->GetTable();
if (rParam.m_Boxes.empty())
{
// Get the Boxes
if( rParam.bBigger )
for( auto pLn : rTable.GetTabLines() )
::lcl_DelSelBox( pLn, rParam, nDistStt, true );
else
for( auto pLn : rTable.GetTabLines() )
::lcl_InsSelBox( pLn, rParam, nDistStt, true );
}
// Prevent deleting the whole Table
if (rParam.bBigger
&& rParam.m_Boxes.size() == rTable.GetTabSortBoxes().size())
{
return nullptr;
}
FndBox_* pFndBox = new FndBox_( nullptr, nullptr );
if( rParam.bBigger )
pFndBox->SetTableLines( rParam.m_Boxes, rTable );
else
{
FndPara aPara(rParam.m_Boxes, pFndBox);
ForEach_FndLineCopyCol( rTable.GetTabLines(), &aPara );
OSL_ENSURE( pFndBox->GetLines().size(), "Where are the Boxes" );
pFndBox->SetTableLines( rTable );
if( ppUndo )
rTmpLst.insert( rTable.GetTabSortBoxes() );
}
// Find Lines for the Layout update
pFndBox->DelFrames( rTable );
// TL_CHART2: this function get called from SetColWidth exclusively,
// thus it is currently speculated that nothing needs to be done here.
// Note: that SetColWidth is currently not completely understood though :-(
return pFndBox;
}
bool SwTable::SetColWidth( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
SwTwips nAbsDiff, SwTwips nRelDiff, SwUndo** ppUndo )
{
SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
const SwFormatFrameSize& rSz = GetFrameFormat()->GetFrameSize();
const SvxLRSpaceItem& rLR = GetFrameFormat()->GetLRSpace();
std::unique_ptr<FndBox_> xFndBox; // for insertion/deletion
SwTableSortBoxes aTmpLst; // for Undo
bool bBigger,
bRet = false,
bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
TableChgWidthHeightType::CellLeft == extractPosition( eType ),
bInsDel = bool(eType & TableChgWidthHeightType::InsertDeleteMode );
sal_uLong nBoxIdx = rCurrentBox.GetSttIdx();
// 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, rSz.GetWidth(),
bLeft ? nDist : rSz.GetWidth() - nDist,
const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
bBigger = aParam.bBigger;
FN_lcl_SetBoxWidth fnSelBox, fnOtherBox;
if( bInsDel )
{
if( bBigger )
{
fnSelBox = lcl_DelSelBox;
fnOtherBox = lcl_DelOtherBox;
aParam.nBoxWidth = static_cast<sal_uInt16>(rCurrentBox.GetFrameFormat()->GetFrameSize().GetWidth());
if( bLeft )
nDistStt = rSz.GetWidth();
}
else
{
fnSelBox = lcl_InsSelBox;
fnOtherBox = lcl_InsOtherBox;
}
}
else
{
fnSelBox = lcl_SetSelBoxWidth;
fnOtherBox = lcl_SetOtherBoxWidth;
}
switch( extractPosition(eType) )
{
case TableChgWidthHeightType::ColRight:
case TableChgWidthHeightType::ColLeft:
if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
{
if( bInsDel )
bBigger = !bBigger;
// 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 && bInsDel &&
// Is the room on the other side?
( bLeft ? rLR.GetRight() >= nAbsDiff
: rLR.GetLeft() >= nAbsDiff ))
{
bRet = true; bLeft = !bLeft;
}
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( bInsDel )
{
xFndBox.reset(::lcl_SaveInsDelData( aParam, ppUndo,
aTmpLst, nDistStt));
if (aParam.bBigger &&
aParam.m_Boxes.size() == m_TabSortContentBoxes.size())
{
// This whole Table is to be deleted!
GetFrameFormat()->GetDoc()->DeleteRowCol(aParam.m_Boxes);
return false;
}
if( ppUndo )
*ppUndo = aParam.CreateUndo(
aParam.bBigger ? SwUndoId::COL_DELETE
: SwUndoId::TABLE_INSCOL );
}
else if( ppUndo )
*ppUndo = new SwUndoAttrTable( *aParam.pTableNd, true );
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(), 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 );
aParam.nTableWidth = sal_uInt16( aSz.GetWidth() );
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( bInsDel ||
( bLeft ? nDist != 0 : std::abs( rSz.GetWidth() - nDist ) > COLFUZZY ) )
{
bRet = true;
if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode && !bInsDel )
aParam.bBigger = !bBigger;
// First test if we have room at all
if( bInsDel )
{
if( aParam.bBigger )
{
for( auto const & n: m_aLines )
{
aParam.LoopClear();
if( !(*fnSelBox)( n, aParam, nDistStt, true ))
{
bRet = false;
break;
}
}
}
else
{
bRet = bLeft ? nDist != 0 : ( rSz.GetWidth() - nDist ) > COLFUZZY;
if( bRet )
{
for( auto const & n: m_aLines )
{
aParam.LoopClear();
if( !(*fnOtherBox)( n, aParam, 0, true ))
{
bRet = false;
break;
}
}
if( bRet && !aParam.bAnyBoxFnd )
bRet = false;
}
if( !bRet && rCurrentBox.GetFrameFormat()->GetFrameSize().GetWidth()
- nRelDiff > COLFUZZY +
( 567 / 2 /*leave room for at least 0.5 cm*/) )
{
// Consume the space from the current Cell
aParam.bSplittBox = true;
// We also need to test this!
bRet = true;
for( auto const & n: m_aLines )
{
aParam.LoopClear();
if( !(*fnSelBox)( n, aParam, nDistStt, true ))
{
bRet = false;
break;
}
}
}
}
}
else 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( bInsDel )
{
aParam1.bBigger = !aParam.bBigger;
xFndBox.reset(::lcl_SaveInsDelData(aParam, ppUndo, aTmpLst, nDistStt));
if( ppUndo )
*ppUndo = aParam.CreateUndo(
aParam.bBigger ? SwUndoId::TABLE_DELBOX
: SwUndoId::TABLE_INSCOL );
}
else if( ppUndo )
*ppUndo = new SwUndoAttrTable( *aParam.pTableNd, true );
if( bInsDel
? ( TableChgMode::FixedWidthChangeAbs == m_eTableChgMode ? (bBigger && bLeft) : bLeft )
: ( 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( bInsDel || ( bLeft ? nDist != 0
: (rSz.GetWidth() - nDist) > COLFUZZY ))
{
if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode && !bInsDel )
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
if( bInsDel )
{
bRet = bLeft ? nDist != 0 : ( rSz.GetWidth() - nDist ) > COLFUZZY;
if( bRet && !aParam.bBigger )
{
bRet = (*fnOtherBox)( pLine, aParam, 0, true );
if( bRet && !aParam.bAnyBoxFnd )
bRet = false;
}
if( !bRet && !aParam.bBigger && rCurrentBox.GetFrameFormat()->
GetFrameSize().GetWidth() - nRelDiff > COLFUZZY +
( 567 / 2 /*leave room for at least 0.5 cm*/) )
{
// Consume the room from the current Cell
aParam.bSplittBox = true;
bRet = true;
}
}
else
{
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( bInsDel )
{
aParam1.bBigger = !aParam.bBigger;
xFndBox.reset(::lcl_SaveInsDelData(aParam, ppUndo, aTmpLst, nDistStt));
if( ppUndo )
*ppUndo = aParam.CreateUndo(
aParam.bBigger ? SwUndoId::TABLE_DELBOX
: SwUndoId::TABLE_INSCOL );
}
else if( ppUndo )
*ppUndo = new SwUndoAttrTable( *aParam.pTableNd, true );
if( bInsDel
? ( TableChgMode::FixedWidthChangeAbs == m_eTableChgMode ? (bBigger && bLeft) : bLeft )
: ( 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( xFndBox )
{
// Clean up the structure of all Lines
GCLines();
// Update Layout
if( !bBigger || xFndBox->AreLinesToRestore( *this ) )
xFndBox->MakeFrames( *this );
// TL_CHART2: it is currently unclear if sth has to be done here.
// The function name hints that nothing needs to be done, on the other
// hand there is a case where sth gets deleted. :-(
xFndBox.reset();
if (ppUndo && *ppUndo && aParam.pUndo)
{
aParam.pUndo->SetColWidthParam( nBoxIdx, static_cast<sal_uInt16>(m_eTableChgMode), eType,
nAbsDiff, nRelDiff );
if( !aParam.bBigger )
aParam.pUndo->SaveNewBoxes( *aParam.pTableNd, aTmpLst );
}
}
#if defined DBG_UTIL
if( bRet )
{
CHECKBOXWIDTH
CHECKTABLELAYOUT
}
#endif
return bRet;
}
static FndBox_* lcl_SaveInsDelData( CR_SetLineHeight& rParam, SwUndo** ppUndo,
SwTableSortBoxes& rTmpLst )
{
// Find all Boxes/Lines
SwTable& rTable = rParam.pTableNd->GetTable();
OSL_ENSURE( !rParam.m_Boxes.empty(), "We can't go on without Boxes!" );
// Prevent deleting the whole Table
if (!rParam.bBigger
&& rParam.m_Boxes.size() == rTable.GetTabSortBoxes().size())
{
return nullptr;
}
FndBox_* pFndBox = new FndBox_( nullptr, nullptr );
if( !rParam.bBigger )
pFndBox->SetTableLines( rParam.m_Boxes, rTable );
else
{
FndPara aPara(rParam.m_Boxes, pFndBox);
ForEach_FndLineCopyCol( rTable.GetTabLines(), &aPara );
OSL_ENSURE( pFndBox->GetLines().size(), "Where are the Boxes?" );
pFndBox->SetTableLines( rTable );
if( ppUndo )
rTmpLst.insert( rTable.GetTabSortBoxes() );
}
// Find Lines for the Layout update
pFndBox->DelFrames( rTable );
// TL_CHART2: it is currently unclear if sth has to be done here.
return pFndBox;
}
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 = long(aTmp);
}
SwFrameSize eSize = ATT_MIN_SIZE;
if( !bMinSize &&
( nMyOldH - nMyNewH ) > ( CalcRowRstHeight( pLineFrame ) + ROWFUZZY ))
eSize = ATT_FIX_SIZE;
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, 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, 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;
}
static bool lcl_InsDelSelLine( SwTableLine* pLine, CR_SetLineHeight& rParam,
SwTwips nDist, bool bCheck )
{
if( !bCheck )
{
SwTableBoxes& rBoxes = pLine->GetTabBoxes();
SwDoc* pDoc = pLine->GetFrameFormat()->GetDoc();
if( !rParam.bBigger )
{
for (size_t n = rBoxes.size(); n; )
{
::lcl_SaveUpperLowerBorder( rParam.pTableNd->GetTable(),
*rBoxes[ --n ],
rParam.aShareFormats );
}
for (size_t n = rBoxes.size(); n; )
{
::DeleteBox_( rParam.pTableNd->GetTable(),
rBoxes[ --n ], rParam.pUndo, false,
false, &rParam.aShareFormats );
}
}
else
{
// Insert Line
SwTableLine* pNewLine = new SwTableLine( static_cast<SwTableLineFormat*>(pLine->GetFrameFormat()),
rBoxes.size(), pLine->GetUpper() );
SwTableLines* pLines;
if( pLine->GetUpper() )
pLines = &pLine->GetUpper()->GetTabLines();
else
pLines = &rParam.pTableNd->GetTable().GetTabLines();
sal_uInt16 nPos = pLines->GetPos( pLine );
if( !rParam.bTop )
++nPos;
pLines->insert( pLines->begin() + nPos, pNewLine );
SwFrameFormat* pNewFormat = pNewLine->ClaimFrameFormat();
pNewFormat->SetFormatAttr( SwFormatFrameSize( ATT_MIN_SIZE, 0, nDist ) );
// And once again calculate the Box count
SwTableBoxes& rNewBoxes = pNewLine->GetTabBoxes();
for( SwTableBoxes::size_type n = 0; n < rBoxes.size(); ++n )
{
SwTwips nWidth = 0;
SwTableBox* pOld = rBoxes[ n ];
if( !pOld->GetSttNd() )
{
// Not a normal content Box, so fall back to the 1st next Box
nWidth = pOld->GetFrameFormat()->GetFrameSize().GetWidth();
while( !pOld->GetSttNd() )
pOld = pOld->GetTabLines()[ 0 ]->GetTabBoxes()[ 0 ];
}
::InsTableBox( pDoc, rParam.pTableNd, pNewLine,
static_cast<SwTableBoxFormat*>(pOld->GetFrameFormat()), pOld, n );
// Special treatment for the border:
// The top one needs to be removed
const SvxBoxItem& rBoxItem = pOld->GetFrameFormat()->GetBox();
if( rBoxItem.GetTop() )
{
SvxBoxItem aTmp( rBoxItem );
aTmp.SetLine( nullptr, SvxBoxItemLine::TOP );
rParam.aShareFormats.SetAttr( rParam.bTop
? *pOld
: *rNewBoxes[ n ], aTmp );
}
if( nWidth )
rParam.aShareFormats.SetAttr( *rNewBoxes[ n ],
SwFormatFrameSize( ATT_FIX_SIZE, nWidth, 0 ) );
}
}
}
else
{
// Collect Boxes!
SwTableBoxes& rBoxes = pLine->GetTabBoxes();
for( auto n = rBoxes.size(); n; )
{
SwTableBox* pBox = rBoxes[ --n ];
if( pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
return false;
if( pBox->GetSttNd() )
{
rParam.m_Boxes.insert(pBox);
}
else
{
for( auto i = pBox->GetTabLines().size(); i; )
lcl_InsDelSelLine( pBox->GetTabLines()[ --i ],
rParam, 0, true );
}
}
}
return true;
}
bool SwTable::SetRowHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
SwTwips nAbsDiff, SwTwips nRelDiff,SwUndo** ppUndo )
{
SwTableLine* pLine = rCurrentBox.GetUpper();
SwTableLine* pBaseLine = pLine;
while( pBaseLine->GetUpper() )
pBaseLine = pBaseLine->GetUpper()->GetUpper();
std::unique_ptr<FndBox_> xFndBox; // for insertion/deletion
SwTableSortBoxes aTmpLst; // for Undo
bool bBigger,
bRet = false,
bTop = TableChgWidthHeightType::CellTop == extractPosition( eType ),
bInsDel = bool(eType & TableChgWidthHeightType::InsertDeleteMode );
sal_uInt16 nBaseLinePos = GetTabLines().GetPos( pBaseLine );
sal_uLong nBoxIdx = rCurrentBox.GetSttIdx();
CR_SetLineHeight aParam( eType,
const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
bBigger = aParam.bBigger;
FN_lcl_SetLineHeight fnSelLine, fnOtherLine = lcl_SetOtherLineHeight;
if( bInsDel )
fnSelLine = lcl_InsDelSelLine;
else
fnSelLine = lcl_SetSelLineHeight;
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 );
pBaseLine = pLine;
SAL_FALLTHROUGH;
case TableChgWidthHeightType::RowBottom:
{
if( bInsDel && !bBigger ) // By how much does it get higher?
{
nAbsDiff = GetRowFrame( *pBaseLine )->getFrameArea().Height();
}
if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
{
// First test if we have room at all
if( bBigger )
bRet = true;
else
bRet = (*fnSelLine)( (*pLines)[ nBaseLinePos ], aParam,
nAbsDiff, true );
if( bRet )
{
if( bInsDel )
{
if (aParam.m_Boxes.empty())
{
::lcl_InsDelSelLine( (*pLines)[ nBaseLinePos ],
aParam, 0, true );
}
xFndBox.reset(::lcl_SaveInsDelData( aParam, ppUndo, aTmpLst ));
// delete complete table when last row is deleted
if( !bBigger &&
aParam.m_Boxes.size() == m_TabSortContentBoxes.size())
{
GetFrameFormat()->GetDoc()->DeleteRowCol(aParam.m_Boxes);
return false;
}
if( ppUndo )
*ppUndo = aParam.CreateUndo(
bBigger ? SwUndoId::TABLE_INSROW
: SwUndoId::ROW_DELETE );
}
else if( ppUndo )
*ppUndo = new SwUndoAttrTable( *aParam.pTableNd, true );
(*fnSelLine)( (*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( !(*fnOtherLine)( (*pLines)[ n ], aParam,
nAbsDiff, true ))
{
bRet = false;
break;
}
}
}
else
bRet = (*fnSelLine)( (*pLines)[ nBaseLinePos ], aParam,
nAbsDiff, true );
}
if( bRet )
{
// Adjust
if( bInsDel )
{
if (aParam.m_Boxes.empty())
{
::lcl_InsDelSelLine( (*pLines)[ nBaseLinePos ],
aParam, 0, true );
}
xFndBox.reset(::lcl_SaveInsDelData( aParam, ppUndo, aTmpLst ));
if( ppUndo )
*ppUndo = aParam.CreateUndo(
bBigger ? SwUndoId::TABLE_INSROW
: SwUndoId::ROW_DELETE );
}
else if( ppUndo )
*ppUndo = new SwUndoAttrTable( *aParam.pTableNd, true );
CR_SetLineHeight aParam1( aParam );
if( bTop )
{
(*fnSelLine)( (*pLines)[ nBaseLinePos ], aParam,
nAbsDiff, false );
for( auto n = nStt; n < nEnd; ++n )
(*fnOtherLine)( (*pLines)[ n ], aParam1,
nAbsDiff, false );
}
else
{
for( auto n = nStt; n < nEnd; ++n )
(*fnOtherLine)( (*pLines)[ n ], aParam1,
nAbsDiff, false );
(*fnSelLine)( (*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;
xFndBox.reset();
}
}
}
break;
default: break;
}
if( xFndBox )
{
// then clean up the structure of all Lines
GCLines();
// Update Layout
if( bBigger || xFndBox->AreLinesToRestore( *this ) )
xFndBox->MakeFrames( *this );
// TL_CHART2: it is currently unclear if sth has to be done here.
xFndBox.reset();
if (ppUndo && *ppUndo && aParam.pUndo)
{
aParam.pUndo->SetColWidthParam( nBoxIdx, static_cast<sal_uInt16>(m_eTableChgMode), eType,
nAbsDiff, nRelDiff );
if( bBigger )
aParam.pUndo->SaveNewBoxes( *aParam.pTableNd, aTmpLst );
}
}
CHECKTABLELAYOUT
return bRet;
}
SwFrameFormat* SwShareBoxFormat::GetFormat( long nWidth ) const
{
SwFrameFormat *pRet = nullptr, *pTmp;
for( auto n = aNewFormats.size(); n; )
if( ( pTmp = 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 = pOldFormat->GetFormatAttr( RES_FRM_SIZE, false );
for( auto n = aNewFormats.size(); n; )
if( SfxItemState::SET == ( pTmp = 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 )
{
aNewFormats.push_back( &rNew );
}
bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat )
{
// returns true, if we can delete
if( pOldFormat == &rFormat )
return true;
std::vector<SwFrameFormat*>::iterator it = std::find( aNewFormats.begin(), aNewFormats.end(), &rFormat );
if( aNewFormats.end() != it )
aNewFormats.erase( it );
return aNewFormats.empty();
}
SwShareBoxFormats::~SwShareBoxFormats()
{
}
SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, 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;
SwShareBoxFormat* pEntry;
if( !Seek_Entry( rOld, &nPos ))
{
pEntry = new SwShareBoxFormat( rOld );
m_ShareArr.insert(m_ShareArr.begin() + nPos, std::unique_ptr<SwShareBoxFormat>(pEntry));
}
else
pEntry = m_ShareArr[ nPos ].get();
pEntry->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: */
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V690 The 'CR_SetBoxWidth' class implements a copy constructor, but lacks the '=' operator. It is dangerous to use such a class.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V690 The 'CR_SetLineHeight' class implements a copy constructor, but lacks the '=' operator. It is dangerous to use such a class.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.