/* -*- 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 <fesh.hxx>
#include <hintids.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/protitem.hxx>
#include <editeng/boxitem.hxx>
#include <svl/stritem.hxx>
#include <editeng/shaditem.hxx>
#include <fmtfsize.hxx>
#include <fmtornt.hxx>
#include <fmtfordr.hxx>
#include <fmtpdsc.hxx>
#include <fmtanchr.hxx>
#include <fmtlsplt.hxx>
#include <frmatr.hxx>
#include <charatr.hxx>
#include <cellfrm.hxx>
#include <pagefrm.hxx>
#include <tabcol.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <UndoManager.hxx>
#include <DocumentSettingManager.hxx>
#include <IDocumentChartDataProviderAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <IDocumentState.hxx>
#include <cntfrm.hxx>
#include <pam.hxx>
#include <swcrsr.hxx>
#include <viscrs.hxx>
#include <swtable.hxx>
#include <swundo.hxx>
#include <tblsel.hxx>
#include <fldbas.hxx>
#include <poolfmt.hxx>
#include <tabfrm.hxx>
#include <UndoCore.hxx>
#include <UndoRedline.hxx>
#include <UndoDelete.hxx>
#include <UndoNumbering.hxx>
#include <UndoTable.hxx>
#include <hints.hxx>
#include <tblafmt.hxx>
#include <swcache.hxx>
#include <ddefld.hxx>
#include <frminf.hxx>
#include <cellatr.hxx>
#include <swtblfmt.hxx>
#include <swddetbl.hxx>
#include <mvsave.hxx>
#include <docary.hxx>
#include <redline.hxx>
#include <rolbck.hxx>
#include <tblrwcl.hxx>
#include <editsh.hxx>
#include <txtfrm.hxx>
#include <ftnfrm.hxx>
#include <section.hxx>
#include <frmtool.hxx>
#include <node2lay.hxx>
#include <strings.hrc>
#include <docsh.hxx>
#include <unochart.hxx>
#include <node.hxx>
#include <ndtxt.hxx>
#include <cstdlib>
#include <map>
#include <algorithm>
#include <rootfrm.hxx>
#include <fldupde.hxx>
#include <calbck.hxx>
#include <o3tl/numeric.hxx>
#include <tools/datetimeutils.hxx>
#include <sal/log.hxx>
#ifdef DBG_UTIL
#define CHECK_TABLE(t) (t).CheckConsistency();
#else
#define CHECK_TABLE(t)
#endif
using ::editeng::SvxBorderLine;
using namespace ::com::sun::star;
const sal_Unicode T2T_PARA = 0x0a;
static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
{
bool bTop = false, bBottom = false, bLeft = false, bRight = false;
switch ( nId )
{
case 0: bTop = bBottom = bLeft = true; break;
case 1: bTop = bBottom = bLeft = bRight = true; break;
case 2: bBottom = bLeft = true; break;
case 3: bBottom = bLeft = bRight = true; break;
}
const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
Color aCol( bHTML ? COL_GRAY : COL_BLACK );
SvxBorderLine aLine( &aCol, DEF_LINE_WIDTH_0 );
if ( bHTML )
{
aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
aLine.SetWidth( DEF_LINE_WIDTH_0 );
}
SvxBoxItem aBox(RES_BOX);
aBox.SetAllDistances(55);
if ( bTop )
aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
if ( bBottom )
aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
if ( bLeft )
aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
if ( bRight )
aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
rFormat.SetFormatAttr( aBox );
}
typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;
static void
lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
{
DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
if (!pMap)
{
pMap = new DfltBoxAttrMap_t;
rBoxFormatArr[ nId ] = pMap;
}
SwTableBoxFormat* pNewTableBoxFormat = nullptr;
SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
if (pMap->end() != iter)
{
pNewTableBoxFormat = iter->second;
}
else
{
SwDoc* pDoc = pBoxFrameFormat->GetDoc();
// format does not exist, so create it
pNewTableBoxFormat = pDoc->MakeTableBoxFormat();
pNewTableBoxFormat->SetFormatAttr( pBoxFrameFormat->GetAttrSet().Get( RES_FRM_SIZE ) );
if( pAutoFormat )
pAutoFormat->UpdateToSet( nId, const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
SwTableAutoFormat::UPDATE_BOX,
pDoc->GetNumberFormatter() );
else
::lcl_SetDfltBoxAttr( *pNewTableBoxFormat, nId );
(*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
}
rBox.ChgFrameFormat( pNewTableBoxFormat );
}
static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
sal_uInt16 nCols, sal_uInt8 nId )
{
if ( !rBoxFormatArr[nId] )
{
SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
if( USHRT_MAX != nCols )
pBoxFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE,
USHRT_MAX / nCols, 0 ));
::lcl_SetDfltBoxAttr( *pBoxFormat, nId );
rBoxFormatArr[ nId ] = pBoxFormat;
}
return rBoxFormatArr[nId];
}
static SwTableBoxFormat *lcl_CreateAFormatBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
const SwTableAutoFormat& rAutoFormat,
sal_uInt16 nCols, sal_uInt8 nId )
{
if( !rBoxFormatArr[nId] )
{
SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
rAutoFormat.UpdateToSet( nId, const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
SwTableAutoFormat::UPDATE_BOX,
rDoc.GetNumberFormatter( ) );
if( USHRT_MAX != nCols )
pBoxFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE,
USHRT_MAX / nCols, 0 ));
rBoxFormatArr[ nId ] = pBoxFormat;
}
return rBoxFormatArr[nId];
}
SwTableNode* SwDoc::IsIdxInTable(const SwNodeIndex& rIdx)
{
SwTableNode* pTableNd = nullptr;
sal_uLong nIndex = rIdx.GetIndex();
do {
SwNode* pNd = static_cast<SwNode*>(GetNodes()[ nIndex ]->StartOfSectionNode());
if( nullptr != ( pTableNd = pNd->GetTableNode() ) )
break;
nIndex = pNd->GetIndex();
} while ( nIndex );
return pTableNd;
}
/**
* Insert a new Box before the InsPos
*/
bool SwNodes::InsBoxen( SwTableNode* pTableNd,
SwTableLine* pLine,
SwTableBoxFormat* pBoxFormat,
SwTextFormatColl* pTextColl,
const SfxItemSet* pAutoAttr,
sal_uInt16 nInsPos,
sal_uInt16 nCnt )
{
if( !nCnt )
return false;
OSL_ENSURE( pLine, "No valid Line" );
// Move Index after the Line's last Box
sal_uLong nIdxPos = 0;
SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
if( !pLine->GetTabBoxes().empty() )
{
if( nInsPos < pLine->GetTabBoxes().size() )
{
if( nullptr == (pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
pLine->GetTabBoxes()[ nInsPos ] )))
pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
}
else
{
if( nullptr == (pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
pLine->GetTabBoxes().back() )))
pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
}
}
else if( nullptr == ( pNxtBox = pLine->FindNextBox( pTableNd->GetTable() )))
pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
if( !pPrvBox && !pNxtBox )
{
bool bSetIdxPos = true;
if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
{
const SwTableLine* pTableLn = pLine;
while( pTableLn->GetUpper() )
pTableLn = pTableLn->GetUpper()->GetUpper();
if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
{
// Before the Table's first Box
while( !( pNxtBox = pLine->GetTabBoxes()[0])->GetTabLines().empty() )
pLine = pNxtBox->GetTabLines()[0];
nIdxPos = pNxtBox->GetSttIdx();
bSetIdxPos = false;
}
}
if( bSetIdxPos )
// Tables without content or at the end; move before the End
nIdxPos = pTableNd->EndOfSectionIndex();
}
else if( pNxtBox ) // There is a successor
nIdxPos = pNxtBox->GetSttIdx();
else // There is a predecessor
nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;
SwNodeIndex aEndIdx( *this, nIdxPos );
for( sal_uInt16 n = 0; n < nCnt; ++n )
{
SwStartNode* pSttNd = new SwStartNode( aEndIdx, SwNodeType::Start,
SwTableBoxStartNode );
pSttNd->m_pStartOfSection = pTableNd;
new SwEndNode( aEndIdx, *pSttNd );
pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
sal_uInt16 nRealInsPos = nInsPos + n;
if (nRealInsPos > rTabBoxes.size())
nRealInsPos = rTabBoxes.size();
rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );
if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
//FEATURE::CONDCOLL
&& RES_CONDTXTFMTCOLL != pTextColl->Which()
//FEATURE::CONDCOLL
)
new SwTextNode( SwNodeIndex( *pSttNd->EndOfSectionNode() ),
pTextColl, pAutoAttr );
else
{
// Handle Outline numbering correctly!
SwTextNode* pTNd = new SwTextNode(
SwNodeIndex( *pSttNd->EndOfSectionNode() ),
GetDoc()->GetDfltTextFormatColl(),
pAutoAttr );
pTNd->ChgFormatColl( pTextColl );
}
}
return true;
}
/**
* Insert a new Table
*/
const SwTable* SwDoc::InsertTable( const SwInsertTableOptions& rInsTableOpts,
const SwPosition& rPos, sal_uInt16 nRows,
sal_uInt16 nCols, sal_Int16 eAdjust,
const SwTableAutoFormat* pTAFormat,
const std::vector<sal_uInt16> *pColArr,
bool bCalledFromShell,
bool bNewModel )
{
assert(nRows && "Table without line?");
assert(nCols && "Table without rows?");
{
// Do not copy into Footnotes!
if( rPos.nNode < GetNodes().GetEndOfInserts().GetIndex() &&
rPos.nNode >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
return nullptr;
// If the ColumnArray has a wrong count, ignore it!
if( pColArr &&
static_cast<size_t>(nCols + ( text::HoriOrientation::NONE == eAdjust ? 2 : 1 )) != pColArr->size() )
pColArr = nullptr;
}
OUString aTableName = GetUniqueTableName();
if( GetIDocumentUndoRedo().DoesUndo() )
{
GetIDocumentUndoRedo().AppendUndo(
new SwUndoInsTable( rPos, nCols, nRows, static_cast<sal_uInt16>(eAdjust),
rInsTableOpts, pTAFormat, pColArr,
aTableName));
}
// Start with inserting the Nodes and get the AutoFormat for the Table
SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
*pHeadColl = pBodyColl;
bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );
if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );
const sal_uInt16 nRowsToRepeat =
SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
rInsTableOpts.mnRowsToRepeat :
0;
/* Save content node to extract FRAMEDIR from. */
const SwContentNode * pContentNd = rPos.nNode.GetNode().GetContentNode();
/* If we are called from a shell pass the attrset from
pContentNd (aka the node the table is inserted at) thus causing
SwNodes::InsertTable to propagate an adjust item if
necessary. */
SwTableNode *pTableNd = SwNodes::InsertTable(
rPos.nNode,
nCols,
pBodyColl,
nRows,
nRowsToRepeat,
pHeadColl,
bCalledFromShell ? &pContentNd->GetSwAttrSet() : nullptr );
// Create the Box/Line/Table construct
SwTableLineFormat* pLineFormat = MakeTableLineFormat();
SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );
/* If the node to insert the table at is a context node and has a
non-default FRAMEDIR propagate it to the table. */
if (pContentNd)
{
const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
const SfxPoolItem *pItem = nullptr;
if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem )
&& pItem != nullptr)
{
pTableFormat->SetFormatAttr( *pItem );
}
}
// Set Orientation at the Table's Format
pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
// All lines use the left-to-right Fill-Order!
pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
// Set USHRT_MAX as the Table's default SSize
SwTwips nWidth = USHRT_MAX;
if( pColArr )
{
sal_uInt16 nSttPos = pColArr->front();
sal_uInt16 nLastPos = pColArr->back();
if( text::HoriOrientation::NONE == eAdjust )
{
sal_uInt16 nFrameWidth = nLastPos;
nLastPos = (*pColArr)[ pColArr->size()-2 ];
pTableFormat->SetFormatAttr( SvxLRSpaceItem( nSttPos, nFrameWidth - nLastPos, 0, 0, RES_LR_SPACE ) );
}
nWidth = nLastPos - nSttPos;
}
else
{
nWidth /= nCols;
nWidth *= nCols; // to avoid rounding problems
}
pTableFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nWidth ));
if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
// Move the hard PageDesc/PageBreak Attributes if needed
SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
->GetContentNode();
if( pNextNd && pNextNd->HasSwAttrSet() )
{
const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
const SfxPoolItem *pItem;
if( SfxItemState::SET == pNdSet->GetItemState( RES_PAGEDESC, false,
&pItem ) )
{
pTableFormat->SetFormatAttr( *pItem );
pNextNd->ResetAttr( RES_PAGEDESC );
pNdSet = pNextNd->GetpSwAttrSet();
}
if( pNdSet && SfxItemState::SET == pNdSet->GetItemState( RES_BREAK, false,
&pItem ) )
{
pTableFormat->SetFormatAttr( *pItem );
pNextNd->ResetAttr( RES_BREAK );
}
}
SwTable& rNdTable = pTableNd->GetTable();
rNdTable.RegisterToFormat( *pTableFormat );
rNdTable.SetRowsToRepeat( nRowsToRepeat );
rNdTable.SetTableModel( bNewModel );
std::vector<SwTableBoxFormat*> aBoxFormatArr;
SwTableBoxFormat* pBoxFormat = nullptr;
if( !bDfltBorders && !pTAFormat )
{
pBoxFormat = MakeTableBoxFormat();
pBoxFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, USHRT_MAX / nCols, 0 ));
}
else
{
const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
aBoxFormatArr.resize( nBoxArrLen, nullptr );
}
SfxItemSet aCharSet( GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{} );
SwNodeIndex aNdIdx( *pTableNd, 1 ); // Set to StartNode of first Box
SwTableLines& rLines = rNdTable.GetTabLines();
for( sal_uInt16 n = 0; n < nRows; ++n )
{
SwTableLine* pLine = new SwTableLine( pLineFormat, nCols, nullptr );
rLines.insert( rLines.begin() + n, pLine );
SwTableBoxes& rBoxes = pLine->GetTabBoxes();
for( sal_uInt16 i = 0; i < nCols; ++i )
{
SwTableBoxFormat *pBoxF;
if( pTAFormat )
{
sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
nCols, nId );
// Set the Paragraph/Character Attributes if needed
if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
{
aCharSet.ClearItem();
pTAFormat->UpdateToSet( nId, aCharSet,
SwTableAutoFormat::UPDATE_CHAR, nullptr );
if( aCharSet.Count() )
GetNodes()[ aNdIdx.GetIndex()+1 ]->GetContentNode()->
SetAttr( aCharSet );
}
}
else if( bDfltBorders )
{
sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
}
else
pBoxF = pBoxFormat;
// For AutoFormat on input: the columns are set when inserting the Table
// The Array contains the columns positions and not their widths!
if( pColArr )
{
nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
if( pBoxF->GetFrameSize().GetWidth() != nWidth )
{
if( pBoxF->HasWriterListeners() ) // Create new Format
{
SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
*pNewFormat = *pBoxF;
pBoxF = pNewFormat;
}
pBoxF->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nWidth ));
}
}
SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
rBoxes.insert( rBoxes.begin() + i, pBox );
aNdIdx += 3; // StartNode, TextNode, EndNode == 3 Nodes
}
}
// Insert Frames
GetNodes().GoNext( &aNdIdx ); // Go to the next ContentNode
pTableNd->MakeFrames( &aNdIdx );
// To-Do - add 'SwExtraRedlineTable' also ?
if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
{
SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, 1 );
if( getIDocumentRedlineAccess().IsRedlineOn() )
getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( nsRedlineType_t::REDLINE_INSERT, aPam ), true);
else
getIDocumentRedlineAccess().SplitRedline( aPam );
}
getIDocumentState().SetModified();
CHECK_TABLE(rNdTable);
return &rNdTable;
}
SwTableNode* SwNodes::InsertTable( const SwNodeIndex& rNdIdx,
sal_uInt16 nBoxes,
SwTextFormatColl* pContentTextColl,
sal_uInt16 nLines,
sal_uInt16 nRepeat,
SwTextFormatColl* pHeadlineTextColl,
const SwAttrSet * pAttrSet)
{
if( !nBoxes )
return nullptr;
// If Lines is given, create the Matrix from Lines and Boxes
if( !pHeadlineTextColl || !nLines )
pHeadlineTextColl = pContentTextColl;
SwTableNode * pTableNd = new SwTableNode( rNdIdx );
SwEndNode* pEndNd = new SwEndNode( rNdIdx, *pTableNd );
if( !nLines ) // For the for loop
++nLines;
SwNodeIndex aIdx( *pEndNd );
SwTextFormatColl* pTextColl = pHeadlineTextColl;
for( sal_uInt16 nL = 0; nL < nLines; ++nL )
{
for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
{
SwStartNode* pSttNd = new SwStartNode( aIdx, SwNodeType::Start,
SwTableBoxStartNode );
pSttNd->m_pStartOfSection = pTableNd;
SwTextNode * pTmpNd = new SwTextNode( aIdx, pTextColl );
// #i60422# Propagate some more attributes.
const SfxPoolItem* pItem = nullptr;
if ( nullptr != pAttrSet )
{
static const sal_uInt16 aPropagateItems[] = {
RES_PARATR_ADJUST,
RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, 0 };
const sal_uInt16* pIdx = aPropagateItems;
while ( *pIdx != 0 )
{
if ( SfxItemState::SET != pTmpNd->GetSwAttrSet().GetItemState( *pIdx ) &&
SfxItemState::SET == pAttrSet->GetItemState( *pIdx, true, &pItem ) )
static_cast<SwContentNode *>(pTmpNd)->SetAttr(*pItem);
++pIdx;
}
}
new SwEndNode( aIdx, *pSttNd );
}
if ( nL + 1 >= nRepeat )
pTextColl = pContentTextColl;
}
return pTableNd;
}
/**
* Text to Table
*/
const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
const SwPaM& rRange, sal_Unicode cCh,
sal_Int16 eAdjust,
const SwTableAutoFormat* pTAFormat )
{
// See if the selection contains a Table
const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
{
sal_uLong nCnt = pStt->nNode.GetIndex();
for( ; nCnt <= pEnd->nNode.GetIndex(); ++nCnt )
if( !GetNodes()[ nCnt ]->IsTextNode() )
return nullptr;
}
// Save first node in the selection if it is a context node
SwContentNode * pSttContentNd = pStt->nNode.GetNode().GetContentNode();
SwPaM aOriginal( *pStt, *pEnd );
pStt = aOriginal.GetMark();
pEnd = aOriginal.GetPoint();
SwUndoTextToTable* pUndo = nullptr;
if( GetIDocumentUndoRedo().DoesUndo() )
{
GetIDocumentUndoRedo().StartUndo( SwUndoId::TEXTTOTABLE, nullptr );
pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
static_cast<sal_uInt16>(eAdjust), pTAFormat );
GetIDocumentUndoRedo().AppendUndo( pUndo );
// Do not add splitting the TextNode to the Undo history
GetIDocumentUndoRedo().DoUndo( false );
}
::PaMCorrAbs( aOriginal, *pEnd );
// Make sure that the range is on Node Edges
SwNodeRange aRg( pStt->nNode, pEnd->nNode );
if( pStt->nContent.GetIndex() )
getIDocumentContentOperations().SplitNode( *pStt, false );
bool bEndContent = 0 != pEnd->nContent.GetIndex();
// Do not split at the End of a Line (except at the End of the Doc)
if( bEndContent )
{
if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
|| pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
{
getIDocumentContentOperations().SplitNode( *pEnd, false );
--const_cast<SwNodeIndex&>(pEnd->nNode);
const_cast<SwIndex&>(pEnd->nContent).Assign(
pEnd->nNode.GetNode().GetContentNode(), 0 );
// A Node and at the End?
if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
--aRg.aStart;
}
else
++aRg.aEnd;
}
if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
{
OSL_FAIL( "empty range" );
++aRg.aEnd;
}
// We always use Upper to insert the Table
SwNode2Layout aNode2Layout( aRg.aStart.GetNode() );
GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );
// Create the Box/Line/Table construct
SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
SwTableLineFormat* pLineFormat = MakeTableLineFormat();
SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
// All Lines have a left-to-right Fill Order
pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
// The Table's SSize is USHRT_MAX
pTableFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, USHRT_MAX ));
if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
/* If the first node in the selection is a context node and if it
has an item FRAMEDIR set (no default) propagate the item to the
replacing table. */
if (pSttContentNd)
{
const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
const SfxPoolItem *pItem = nullptr;
if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem )
&& pItem != nullptr)
{
pTableFormat->SetFormatAttr( *pItem );
}
}
//Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
//until after RegisterToFormat is completed
bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
getIDocumentState().SetEnableSetModified(false);
SwTableNode* pTableNd = GetNodes().TextToTable(
aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );
SwTable& rNdTable = pTableNd->GetTable();
const sal_uInt16 nRowsToRepeat =
SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
rInsTableOpts.mnRowsToRepeat :
0;
rNdTable.SetRowsToRepeat(nRowsToRepeat);
bool bUseBoxFormat = false;
if( !pBoxFormat->HasWriterListeners() )
{
// The Box's Formats already have the right size, we must only set
// the right Border/AutoFormat.
bUseBoxFormat = true;
pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
delete pBoxFormat;
eAdjust = text::HoriOrientation::NONE;
}
// Set Orientation in the Table's Format
pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
rNdTable.RegisterToFormat(*pTableFormat);
if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
{
sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
std::unique_ptr< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
if( bUseBoxFormat )
{
aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
}
else
{
aBoxFormatArr2.reset(new std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr ));
}
SfxItemSet aCharSet( GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{} );
SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
SwTableBoxFormat *pBoxF = nullptr;
SwTableLines& rLines = rNdTable.GetTabLines();
const SwTableLines::size_type nRows = rLines.size();
for( SwTableLines::size_type n = 0; n < nRows; ++n )
{
SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
const SwTableBoxes::size_type nCols = rBoxes.size();
for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
{
SwTableBox* pBox = rBoxes[ i ];
bool bChgSz = false;
if( pTAFormat )
{
sal_uInt8 nId = static_cast<sal_uInt8>(!n ? 0 : (( n+1 == nRows )
? 12 : (4 * (1 + ((n-1) & 1 )))));
nId = nId + static_cast<sal_uInt8>(!i ? 0 :
( i+1 == nCols ? 3 : (1 + ((i-1) & 1))));
if( bUseBoxFormat )
::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId, pTAFormat );
else
{
bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
*pTAFormat, USHRT_MAX, nId );
}
// Set Paragraph/Character Attributes if needed
if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
{
aCharSet.ClearItem();
pTAFormat->UpdateToSet( nId, aCharSet,
SwTableAutoFormat::UPDATE_CHAR, nullptr );
if( aCharSet.Count() )
{
sal_uLong nSttNd = pBox->GetSttIdx()+1;
sal_uLong nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
for( ; nSttNd < nEndNd; ++nSttNd )
{
SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
if( pNd )
{
if( pHistory )
{
SwRegHistory aReg( pNd, *pNd, pHistory );
pNd->SetAttr( aCharSet );
}
else
pNd->SetAttr( aCharSet );
}
}
}
}
}
else
{
sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
if( bUseBoxFormat )
::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
else
{
bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
USHRT_MAX, nId );
}
}
if( !bUseBoxFormat )
{
if( bChgSz )
pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
pBox->ChgFrameFormat( pBoxF );
}
}
}
if( bUseBoxFormat )
{
for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
{
delete (*aBoxFormatArr1)[ i ];
}
}
}
// Check the boxes for numbers
if( IsInsTableFormatNum() )
{
for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
{
ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
}
}
sal_uLong nIdx = pTableNd->GetIndex();
aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
{
SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
rTmp.DeleteMark();
rTmp.GetPoint()->nNode = *pTableNd;
SwContentNode* pCNd = GetNodes().GoNext( &rTmp.GetPoint()->nNode );
rTmp.GetPoint()->nContent.Assign( pCNd, 0 );
}
if( pUndo )
{
GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
}
getIDocumentState().SetEnableSetModified(bEnableSetModified);
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, 0);
return &rNdTable;
}
static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
{
// delete old layout frames, new ones need to be created...
rNode.DelFrames();
if (!rNode.IsTextNode())
{
return;
}
SwTextNode & rTextNode = *rNode.GetTextNode();
// remove PageBreaks/PageDesc/ColBreak
SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
if (pSet)
{
const SfxPoolItem* pItem;
if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false, &pItem))
{
if (pTableFormat)
{
pTableFormat->SetFormatAttr(*pItem);
}
rTextNode.ResetAttr(RES_BREAK);
pSet = rTextNode.GetpSwAttrSet();
}
if (pSet
&& (SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false, &pItem))
&& static_cast<SwFormatPageDesc const*>(pItem)->GetPageDesc())
{
if (pTableFormat)
{
pTableFormat->SetFormatAttr(*pItem);
}
rTextNode.ResetAttr(RES_PAGEDESC);
}
}
}
/**
* balance lines in table, insert empty boxes so all lines have the size
*/
static void
lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
{
for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
{
SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
size_t const nBoxes = pCurrLine->GetTabBoxes().size();
if (nMaxBoxes != nBoxes)
{
rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
nullptr, nBoxes, nMaxBoxes - nBoxes);
if (pUndo)
{
for (size_t i = nBoxes; i < nMaxBoxes; ++i)
{
pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
}
}
// if the first line is missing boxes, the width array is useless!
if (!n && pPositions)
{
pPositions->clear();
}
}
}
}
static void
lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
std::vector<sal_uInt16> *const pPositions)
{
if (pPositions && !pPositions->empty())
{
SwTableLines& rLns = rTable.GetTabLines();
sal_uInt16 nLastPos = 0;
for (size_t n = 0; n < pPositions->size(); ++n)
{
SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
pNewFormat->SetFormatAttr(
SwFormatFrameSize(ATT_VAR_SIZE, (*pPositions)[n] - nLastPos));
for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
{
// Have to do an Add here, because the BoxFormat
// is still needed by the caller
pNewFormat->Add( rLns[ nTmpLine ]->GetTabBoxes()[ n ] );
}
nLastPos = (*pPositions)[ n ];
}
// propagate size upwards from format, so the table gets the right size
SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
"who is still registered in the format?");
rBoxFormat.SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nLastPos ));
}
else
{
size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
rBoxFormat.SetFormatAttr(SwFormatFrameSize(ATT_VAR_SIZE, nWidth));
}
}
SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
SwTableFormat* pTableFormat,
SwTableLineFormat* pLineFormat,
SwTableBoxFormat* pBoxFormat,
SwTextFormatColl* pTextColl,
SwUndoTextToTable* pUndo )
{
if( rRange.aStart >= rRange.aEnd )
return nullptr;
SwTableNode * pTableNd = new SwTableNode( rRange.aStart );
new SwEndNode( rRange.aEnd, *pTableNd );
SwDoc* pDoc = GetDoc();
std::vector<sal_uInt16> aPosArr;
SwTable& rTable = pTableNd->GetTable();
SwTableBox* pBox;
sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
SwNodeIndex aSttIdx( *pTableNd, 1 );
SwNodeIndex aEndIdx( rRange.aEnd, -1 );
for( nLines = 0, nBoxes = 0;
aSttIdx.GetIndex() < aEndIdx.GetIndex();
aSttIdx += 2, nLines++, nBoxes = 0 )
{
SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );
if( !nLines && 0x0b == cCh )
{
cCh = 0x09;
// Get the separator's position from the first Node, in order for the Boxes to be set accordingly
SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() )) );
if( aFInfo.IsOneLine() ) // only makes sense in this case
{
OUString const& rText(pTextNd->GetText());
for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
{
if (rText[nChPos] == cCh)
{
// sw_redlinehide: no idea if this makes any sense...
TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
aPosArr.push_back( static_cast<sal_uInt16>(
aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
}
}
aPosArr.push_back(
static_cast<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
aFInfo.GetFrame()->getFramePrintArea().Bottom() :
aFInfo.GetFrame()->getFramePrintArea().Right()) );
}
}
lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);
// Set the TableNode as StartNode for all TextNodes in the Table
pTextNd->m_pStartOfSection = pTableNd;
SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
SwStartNode* pSttNd;
SwPosition aCntPos( aSttIdx, SwIndex( pTextNd ));
const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
pContentStore->Save( pDoc, aSttIdx.GetIndex(), pTextNd->GetText().getLength() );
if( T2T_PARA != cCh )
{
for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
{
if (pTextNd->GetText()[nChPos] == cCh)
{
aCntPos.nContent = nChPos;
SwContentNode* pNewNd = pTextNd->SplitContentNode( aCntPos );
if( !pContentStore->Empty() )
pContentStore->Restore( *pNewNd, nChPos, nChPos + 1 );
// Delete separator and correct search string
pTextNd->EraseText( aCntPos.nContent, 1 );
nChPos = 0;
// Set the TableNode as StartNode for all TextNodes in the Table
const SwNodeIndex aTmpIdx( aCntPos.nNode, -1 );
pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
SwTableBoxStartNode );
new SwEndNode( aCntPos.nNode, *pSttNd );
pNewNd->m_pStartOfSection = pSttNd;
// Assign Section to the Box
pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
}
else
{
++nChPos;
}
}
}
// Now for the last substring
if( !pContentStore->Empty())
pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );
pSttNd = new SwStartNode( aCntPos.nNode, SwNodeType::Start, SwTableBoxStartNode );
const SwNodeIndex aTmpIdx( aCntPos.nNode, 1 );
new SwEndNode( aTmpIdx, *pSttNd );
pTextNd->m_pStartOfSection = pSttNd;
pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
if( nMaxBoxes < nBoxes )
nMaxBoxes = nBoxes;
}
lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
pUndo, &aPosArr);
lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, *pDoc, &aPosArr);
return pTableNd;
}
const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
{
if (rTableNodes.empty())
return nullptr;
const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();
if (rFirstRange.empty())
return nullptr;
const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();
if (rLastRange.empty())
return nullptr;
/* Save first node in the selection if it is a content node. */
SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();
const SwNodeRange& rStartRange = *rFirstRange.begin();
const SwNodeRange& rEndRange = *rLastRange.rbegin();
//!!! not necessarily TextNodes !!!
SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
const SwPosition *pStt = aOriginal.GetMark();
const SwPosition *pEnd = aOriginal.GetPoint();
bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
if (bUndo)
{
// Do not add splitting the TextNode to the Undo history
GetIDocumentUndoRedo().DoUndo(false);
}
::PaMCorrAbs( aOriginal, *pEnd );
// make sure that the range is on Node Edges
SwNodeRange aRg( pStt->nNode, pEnd->nNode );
if( pStt->nContent.GetIndex() )
getIDocumentContentOperations().SplitNode( *pStt, false );
bool bEndContent = 0 != pEnd->nContent.GetIndex();
// Do not split at the End of a Line (except at the End of the Doc)
if( bEndContent )
{
if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
|| pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
{
getIDocumentContentOperations().SplitNode( *pEnd, false );
--const_cast<SwNodeIndex&>(pEnd->nNode);
const_cast<SwIndex&>(pEnd->nContent).Assign(
pEnd->nNode.GetNode().GetContentNode(), 0 );
// A Node and at the End?
if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
--aRg.aStart;
}
else
++aRg.aEnd;
}
assert(aRg.aEnd == pEnd->nNode);
assert(aRg.aStart == pStt->nNode);
if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
{
OSL_FAIL( "empty range" );
++aRg.aEnd;
}
{
// TODO: this is not Undo-able - only good enough for file import
IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
SwNodeIndex const* pPrev(&prev);
// pPrev could point to non-textnode now
for (auto row = rTableNodes.begin(); row != rTableNodes.end(); ++row)
{
for (auto cell = row->begin(); cell != row->end(); ++cell)
{
assert(SwNodeIndex(*pPrev, +1) == cell->aStart);
SwPaM pam(cell->aStart, 0, *pPrev,
(pPrev->GetNode().IsContentNode())
? pPrev->GetNode().GetContentNode()->Len() : 0);
rIDRA.SplitRedline(pam);
pPrev = &cell->aEnd;
}
}
// another one to break between last cell and node after table
SwPaM pam(SwNodeIndex(*pPrev, +1), 0, *pPrev,
(pPrev->GetNode().IsContentNode())
? pPrev->GetNode().GetContentNode()->Len() : 0);
rIDRA.SplitRedline(pam);
}
// We always use Upper to insert the Table
SwNode2Layout aNode2Layout( aRg.aStart.GetNode() );
GetIDocumentUndoRedo().DoUndo(bUndo);
// Create the Box/Line/Table construct
SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
SwTableLineFormat* pLineFormat = MakeTableLineFormat();
SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
// All Lines have a left-to-right Fill Order
pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
// The Table's SSize is USHRT_MAX
pTableFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, USHRT_MAX ));
/* If the first node in the selection is a context node and if it
has an item FRAMEDIR set (no default) propagate the item to the
replacing table. */
if (pSttContentNd)
{
const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
const SfxPoolItem *pItem = nullptr;
if (SfxItemState::SET == aNdSet.GetItemState( RES_FRAMEDIR, true, &pItem )
&& pItem != nullptr)
{
pTableFormat->SetFormatAttr( *pItem );
}
}
//Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
//until after RegisterToFormat is completed
bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
getIDocumentState().SetEnableSetModified(false);
SwTableNode* pTableNd = GetNodes().TextToTable(
rTableNodes, pTableFormat, pLineFormat, pBoxFormat );
SwTable& rNdTable = pTableNd->GetTable();
rNdTable.RegisterToFormat(*pTableFormat);
if( !pBoxFormat->HasWriterListeners() )
{
// The Box's Formats already have the right size, we must only set
// the right Border/AutoFormat.
pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
delete pBoxFormat;
}
sal_uLong nIdx = pTableNd->GetIndex();
aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
getIDocumentState().SetEnableSetModified(bEnableSetModified);
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
return &rNdTable;
}
SwNodeRange * SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange)
{
SwNodeRange * pResult = nullptr;
bool bChanged = false;
SwNodeIndex aNewStart = rRange.aStart;
SwNodeIndex aNewEnd = rRange.aEnd;
SwNodeIndex aEndIndex = rRange.aEnd;
SwNodeIndex aIndex = rRange.aStart;
while (aIndex < aEndIndex)
{
SwNode& rNode = aIndex.GetNode();
if (rNode.IsStartNode())
{
// advance aIndex to the end node of this start node
SwNode * pEndNode = rNode.EndOfSectionNode();
aIndex = *pEndNode;
if (aIndex > aNewEnd)
{
aNewEnd = aIndex;
bChanged = true;
}
}
else if (rNode.IsEndNode())
{
SwNode * pStartNode = rNode.StartOfSectionNode();
SwNodeIndex aStartIndex = *pStartNode;
if (aStartIndex < aNewStart)
{
aNewStart = aStartIndex;
bChanged = true;
}
}
if (aIndex < aEndIndex)
++aIndex;
}
SwNode * pNode = &aIndex.GetNode();
while (pNode->IsEndNode() && aIndex < Count() - 1)
{
SwNode * pStartNode = pNode->StartOfSectionNode();
SwNodeIndex aStartIndex(*pStartNode);
aNewStart = aStartIndex;
aNewEnd = aIndex;
bChanged = true;
++aIndex;
pNode = &aIndex.GetNode();
}
if (bChanged)
pResult = new SwNodeRange(aNewStart, aNewEnd);
return pResult;
}
static void
lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
{
// rhbz#820283, fdo#55462: set default box widths so table width is covered
SwTableLines & rLines = rTable.GetTabLines();
for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
{
SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
assert(!rBoxes.empty()); // ensured by convertToTable
size_t const nMissing = nMaxBoxes - rBoxes.size();
if (nMissing)
{
// default width for box at the end of an incomplete line
SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
pNewFormat->SetFormatAttr( SwFormatFrameSize(ATT_VAR_SIZE,
nWidth * (nMissing + 1)) );
pNewFormat->Add(rBoxes.back());
}
}
size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
// default width for all boxes not at the end of an incomplete line
rBoxFormat.SetFormatAttr(SwFormatFrameSize(ATT_VAR_SIZE, nWidth));
}
SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
SwTableFormat* pTableFormat,
SwTableLineFormat* pLineFormat,
SwTableBoxFormat* pBoxFormat )
{
if( rTableNodes.empty() )
return nullptr;
SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart );
//insert the end node after the last text node
SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
++aInsertIndex;
//!! ownership will be transferred in c-tor to SwNodes array.
//!! Thus no real problem here...
new SwEndNode( aInsertIndex, *pTableNd );
SwDoc* pDoc = GetDoc();
SwTable& rTable = pTableNd->GetTable();
SwTableBox* pBox;
sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
// delete frames of all contained content nodes
for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
{
SwNode& rNode = aNodeIndex.GetNode();
if( rNode.IsContentNode() )
{
lcl_RemoveBreaks(static_cast<SwContentNode&>(rNode),
(0 == nLines) ? pTableFormat : nullptr);
}
}
std::vector<std::vector < SwNodeRange > >::const_iterator aRowIter = rTableNodes.begin();
for( nLines = 0, nBoxes = 0;
aRowIter != rTableNodes.end();
++aRowIter, nLines++, nBoxes = 0 )
{
SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
std::vector< SwNodeRange >::const_iterator aCellIter = aRowIter->begin();
for( ; aCellIter != aRowIter->end(); ++aCellIter )
{
const SwNodeIndex aTmpIdx( aCellIter->aStart, 0 );
SwNodeIndex aCellEndIdx(aCellIter->aEnd);
++aCellEndIdx;
SwStartNode* pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
SwTableBoxStartNode );
// Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
// SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
// So this statement is only executed for the side-effect.
new SwEndNode( aCellEndIdx, *pSttNd );
//set the start node on all node of the current cell
SwNodeIndex aCellNodeIdx = aCellIter->aStart;
for(;aCellNodeIdx <= aCellIter->aEnd; ++aCellNodeIdx )
{
aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
//skip start/end node pairs
if( aCellNodeIdx.GetNode().IsStartNode() )
aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
}
// assign Section to the Box
pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
}
if( nMaxBoxes < nBoxes )
nMaxBoxes = nBoxes;
}
lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, *pDoc);
return pTableNd;
}
/**
* Table to Text
*/
bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
{
if( !pTableNd )
return false;
// #i34471#
// If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
// the table cursor.
SwEditShell* pESh = GetEditShell();
if( pESh && pESh->IsTableMode() )
pESh->ClearMark();
SwNodeRange aRg( *pTableNd, 0, *pTableNd->EndOfSectionNode() );
SwUndoTableToText* pUndo = nullptr;
SwNodeRange* pUndoRg = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().ClearRedo();
pUndoRg = new SwNodeRange( aRg.aStart, -1, aRg.aEnd, +1 );
pUndo = new SwUndoTableToText( pTableNd->GetTable(), cCh );
}
SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
aMsgHint.m_eFlags = TBL_BOXNAME;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
bool bRet = GetNodes().TableToText( aRg, cCh, pUndo );
if( pUndoRg )
{
++pUndoRg->aStart;
--pUndoRg->aEnd;
pUndo->SetRange( *pUndoRg );
GetIDocumentUndoRedo().AppendUndo(pUndo);
delete pUndoRg;
}
if( bRet )
getIDocumentState().SetModified();
return bRet;
}
/**
* Use the ForEach method from PtrArray to recreate Text from a Table.
* The Boxes can also contain Lines!
*/
struct DelTabPara
{
SwTextNode* pLastNd;
SwNodes& rNds;
SwUndoTableToText* pUndo;
sal_Unicode cCh;
DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
};
// Forward declare so that the Lines and Boxes can use recursion
static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );
static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
{
assert(pPara && "The parameters are missing!");
DelTabPara aPara( *pPara );
for( SwTableBoxes::iterator it = pLine->GetTabBoxes().begin();
it != pLine->GetTabBoxes().end(); ++it)
lcl_DelBox(*it, &aPara );
if( pLine->GetUpper() ) // Is there a parent Box?
// Return the last TextNode
pPara->pLastNd = aPara.pLastNd;
}
static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
{
assert(pDelPara && "The parameters are missing");
// Delete the Box's Lines
if( !pBox->GetTabLines().empty() )
{
for( SwTableLine* pLine : pBox->GetTabLines() )
lcl_DelLine( pLine, pDelPara );
}
else
{
SwDoc* pDoc = pDelPara->rNds.GetDoc();
SwNodeRange aDelRg( *pBox->GetSttNd(), 0,
*pBox->GetSttNd()->EndOfSectionNode() );
// Delete the Section
pDelPara->rNds.SectionUp( &aDelRg );
const SwTextNode* pCurTextNd = nullptr;
if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
if (nullptr != pCurTextNd)
{
// Join the current text node with the last from the previous box if possible
sal_uLong nNdIdx = aDelRg.aStart.GetIndex();
--aDelRg.aStart;
if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
{
// Inserting the separator
SwIndex aCntIdx( pDelPara->pLastNd,
pDelPara->pLastNd->GetText().getLength());
pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
SwInsertFlags::EMPTYEXPAND );
if( pDelPara->pUndo )
pDelPara->pUndo->AddBoxPos( *pDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
aCntIdx.GetIndex() );
const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
pContentStore->Save( pDoc, nNdIdx, pCurTextNd->GetText().getLength() );
pDelPara->pLastNd->JoinNext();
if( !pContentStore->Empty() )
pContentStore->Restore( pDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
}
else if( pDelPara->pUndo )
{
++aDelRg.aStart;
pDelPara->pUndo->AddBoxPos( *pDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
}
}
else if( pDelPara->pUndo )
pDelPara->pUndo->AddBoxPos( *pDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
--aDelRg.aEnd;
pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();
// Do not take over the NumberFormatting's adjustment
if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
}
}
bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
SwUndoTableToText* pUndo )
{
// Is a Table selected?
if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
return false;
SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
if (nullptr == pTableNd ||
&rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
return false;
// If the Table was alone in a Section, create the Frames via the Table's Upper
SwNode2Layout* pNode2Layout = nullptr;
SwNodeIndex aFrameIdx( rRange.aStart );
SwNode* pFrameNd = FindPrvNxtFrameNode( aFrameIdx, &rRange.aEnd.GetNode() );
if( !pFrameNd )
// Collect all Uppers
pNode2Layout = new SwNode2Layout( *pTableNd );
// Delete the Frames
pTableNd->DelFrames();
// "Delete" the Table and merge all Lines/Boxes
DelTabPara aDelPara( *this, cCh, pUndo );
for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
lcl_DelLine( pLine, &aDelPara );
// We just created a TextNode with fitting separator for every TableLine.
// Now we only need to delete the TableSection and create the Frames for the
// new TextNode.
SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );
// If the Table has PageDesc/Break Attributes, carry them over to the
// first Text Node
{
// What about UNDO?
const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
const SfxPoolItem *pBreak, *pDesc;
if( SfxItemState::SET != rTableSet.GetItemState( RES_PAGEDESC, false, &pDesc ))
pDesc = nullptr;
if( SfxItemState::SET != rTableSet.GetItemState( RES_BREAK, false, &pBreak ))
pBreak = nullptr;
if( pBreak || pDesc )
{
SwNodeIndex aIdx( *pTableNd );
SwContentNode* pCNd = GoNext( &aIdx );
if( pBreak )
pCNd->SetAttr( *pBreak );
if( pDesc )
pCNd->SetAttr( *pDesc );
}
}
SectionUp( &aDelRg ); // Delete this Section and by that the Table
// #i28006#
sal_uLong nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
if( !pFrameNd )
{
pNode2Layout->RestoreUpperFrames( *this,
aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
delete pNode2Layout;
}
else
{
SwContentNode *pCNd;
SwSectionNode *pSNd;
while( aDelRg.aStart.GetIndex() < nEnd )
{
if( nullptr != ( pCNd = aDelRg.aStart.GetNode().GetContentNode()))
{
if( pFrameNd->IsContentNode() )
static_cast<SwContentNode*>(pFrameNd)->MakeFrames( *pCNd );
else if( pFrameNd->IsTableNode() )
static_cast<SwTableNode*>(pFrameNd)->MakeFrames( aDelRg.aStart );
else if( pFrameNd->IsSectionNode() )
static_cast<SwSectionNode*>(pFrameNd)->MakeFrames( aDelRg.aStart );
pFrameNd = pCNd;
}
else if( nullptr != ( pSNd = aDelRg.aStart.GetNode().GetSectionNode()))
{
if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
{
pSNd->MakeFrames( &aFrameIdx, &aDelRg.aEnd );
break;
}
aDelRg.aStart = *pSNd->EndOfSectionNode();
}
++aDelRg.aStart;
}
}
// #i28006# Fly frames have to be restored even if the table was
// #alone in the section
const SwFrameFormats& rFlyArr = *GetDoc()->GetSpzFrameFormats();
for( auto pFly : rFlyArr )
{
SwFrameFormat *const pFormat = pFly;
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
SwPosition const*const pAPos = rAnchor.GetContentAnchor();
if (pAPos &&
((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
nStt <= pAPos->nNode.GetIndex() &&
pAPos->nNode.GetIndex() < nEnd )
{
pFormat->MakeFrames();
}
}
return true;
}
/**
* Inserting Columns/Rows
*/
void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
{
if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
return;
// Find the Boxes via the Layout
SwSelBoxes aBoxes;
::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
if( !aBoxes.empty() )
InsertCol( aBoxes, nCnt, bBehind );
}
bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
{
OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
if( !pTableNd )
return false;
SwTable& rTable = pTableNd->GetTable();
if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
return false;
SwTableSortBoxes aTmpLst;
SwUndoTableNdsChg* pUndo = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL, rBoxes, *pTableNd,
0, 0, nCnt, bBehind, false );
aTmpLst.insert( rTable.GetTabSortBoxes() );
}
bool bRet(false);
{
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
SwTableFormulaUpdate aMsgHint( &rTable );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
bRet = rTable.InsertCol( this, rBoxes, nCnt, bBehind );
if (bRet)
{
getIDocumentState().SetModified();
::ClearFEShellTabCols(*this, nullptr);
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
}
if( pUndo )
{
if( bRet )
{
pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
return bRet;
}
void SwDoc::InsertRow( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
{
// Find the Boxes via the Layout
SwSelBoxes aBoxes;
GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
if( !aBoxes.empty() )
InsertRow( aBoxes, nCnt, bBehind );
}
bool SwDoc::InsertRow( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
{
OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
if( !pTableNd )
return false;
SwTable& rTable = pTableNd->GetTable();
if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
return false;
SwTableSortBoxes aTmpLst;
SwUndoTableNdsChg* pUndo = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW,rBoxes, *pTableNd,
0, 0, nCnt, bBehind, false );
aTmpLst.insert( rTable.GetTabSortBoxes() );
}
bool bRet(false);
{
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
SwTableFormulaUpdate aMsgHint( &rTable );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
bRet = rTable.InsertRow( this, rBoxes, nCnt, bBehind );
if (bRet)
{
getIDocumentState().SetModified();
::ClearFEShellTabCols(*this, nullptr);
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
}
if( pUndo )
{
if( bRet )
{
pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
return bRet;
}
/**
* Deleting Columns/Rows
*/
void SwDoc::DeleteRow( const SwCursor& rCursor )
{
// Find the Boxes via the Layout
SwSelBoxes aBoxes;
GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
if( ::HasProtectedCells( aBoxes ))
return;
// Remove the Cursor from the to-be-deleted Section.
// The Cursor is placed after the table, except for
// - when there's another Line, we place it in that one
// - when a Line precedes it, we place it in that one
{
SwTableNode* pTableNd = rCursor.GetNode().FindTableNode();
if(dynamic_cast<const SwDDETable*>( & pTableNd->GetTable()) != nullptr)
return;
// Find all Boxes/Lines
FndBox_ aFndBox( nullptr, nullptr );
{
FndPara aPara( aBoxes, &aFndBox );
ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
}
if( aFndBox.GetLines().empty() )
return;
SwEditShell* pESh = GetEditShell();
if( pESh )
{
pESh->KillPams();
// FIXME: actually we should be iterating over all Shells!
}
FndBox_* pFndBox = &aFndBox;
while( 1 == pFndBox->GetLines().size() &&
1 == pFndBox->GetLines().front()->GetBoxes().size() )
{
FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
if( pTmp->GetBox()->GetSttNd() )
break; // Else it gets too far
pFndBox = pTmp;
}
SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
while( !pDelBox->GetSttNd() )
{
SwTableLine* pLn = pDelBox->GetTabLines()[
pDelBox->GetTabLines().size()-1 ];
pDelBox = pLn->GetTabBoxes().back();
}
SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
pDelBox );
while( pNextBox &&
pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
if( !pNextBox ) // No succeeding Boxes? Then take the preceding one
{
pDelLine = pFndBox->GetLines().front()->GetLine();
pDelBox = pDelLine->GetTabBoxes()[ 0 ];
while( !pDelBox->GetSttNd() )
pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
pDelBox );
while( pNextBox &&
pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
}
sal_uLong nIdx;
if( pNextBox ) // Place the Cursor here
nIdx = pNextBox->GetSttIdx() + 1;
else // Else after the Table
nIdx = pTableNd->EndOfSectionIndex() + 1;
SwNodeIndex aIdx( GetNodes(), nIdx );
SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
if( !pCNd )
pCNd = GetNodes().GoNext( &aIdx );
if( pCNd )
{
// Change the Shell's Cursor or the one passed?
SwPaM* pPam = const_cast<SwPaM*>(static_cast<SwPaM const *>(&rCursor));
pPam->GetPoint()->nNode = aIdx;
pPam->GetPoint()->nContent.Assign( pCNd, 0 );
pPam->SetMark(); // Both want a part of it
pPam->DeleteMark();
}
}
// Thus delete the Rows
GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE, nullptr);
DeleteRowCol( aBoxes );
GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE, nullptr);
}
void SwDoc::DeleteCol( const SwCursor& rCursor )
{
// Find the Boxes via the Layout
SwSelBoxes aBoxes;
GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
if( ::HasProtectedCells( aBoxes ))
return;
// The Cursors need to be removed from the to-be-deleted range.
// Always place them after/on top of the Table; they are always set
// to the old position via the document position.
SwEditShell* pESh = GetEditShell();
if( pESh )
{
const SwNode* pNd = rCursor.GetNode().FindTableBoxStartNode();
pESh->ParkCursor( SwNodeIndex( *pNd ) );
}
// Thus delete the Columns
GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
DeleteRowCol( aBoxes, true );
GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
}
bool SwDoc::DeleteRowCol( const SwSelBoxes& rBoxes, bool bColumn )
{
if( ::HasProtectedCells( rBoxes ))
return false;
OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
if( !pTableNd )
return false;
if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr)
return false;
::ClearFEShellTabCols(*this, nullptr);
SwSelBoxes aSelBoxes( rBoxes );
SwTable &rTable = pTableNd->GetTable();
long nMin = 0;
long nMax = 0;
if( rTable.IsNewModel() )
{
if( bColumn )
rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
else
rTable.FindSuperfluousRows( aSelBoxes );
}
// Are we deleting the whole Table?
const sal_uLong nTmpIdx1 = pTableNd->GetIndex();
const sal_uLong nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1;
if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() &&
aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 &&
nTmpIdx2 == pTableNd->EndOfSectionIndex() )
{
bool bNewTextNd = false;
// Is it alone in a FlyFrame?
SwNodeIndex aIdx( *pTableNd, -1 );
const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode();
if( pSttNd )
{
const sal_uLong nTableEnd = pTableNd->EndOfSectionIndex() + 1;
const sal_uLong nSectEnd = pSttNd->EndOfSectionIndex();
if( nTableEnd == nSectEnd )
{
if( SwFlyStartNode == pSttNd->GetStartNodeType() )
{
SwFrameFormat* pFormat = pSttNd->GetFlyFormat();
if( pFormat )
{
// That's the FlyFormat we're looking for
getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
return true;
}
}
// No Fly? Thus Header or Footer: always leave a TextNode
// We can forget about Undo then!
bNewTextNd = true;
}
}
// No Fly? Then it is a Header or Footer, so keep always a TextNode
++aIdx;
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().ClearRedo();
SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() );
if( bNewTextNd )
{
const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
GetNodes().MakeTextNode( aTmpIdx,
getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
}
// Save the cursors (UNO and otherwise)
SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
{
*aSavePaM.GetMark() = SwPosition( *pTableNd );
aSavePaM.Move( fnMoveBackward, GoInNode );
}
{
SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
}
// Move hard PageBreaks to the succeeding Node
bool bSavePageBreak = false, bSavePageDesc = false;
sal_uLong nNextNd = pTableNd->EndOfSectionIndex()+1;
SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode();
if( pNextNd )
{
SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
const SfxPoolItem *pItem;
if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
false, &pItem ) )
{
pNextNd->SetAttr( *pItem );
bSavePageDesc = true;
}
if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
false, &pItem ) )
{
pNextNd->SetAttr( *pItem );
bSavePageBreak = true;
}
}
SwUndoDelete* pUndo = new SwUndoDelete( aPaM );
if( bNewTextNd )
pUndo->SetTableDelLastNd();
pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
{
if( bNewTextNd )
{
const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
GetNodes().MakeTextNode( aTmpIdx,
getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
}
// Save the cursors (UNO and otherwise)
SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
{
*aSavePaM.GetMark() = SwPosition( *pTableNd );
aSavePaM.Move( fnMoveBackward, GoInNode );
}
{
SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
}
// Move hard PageBreaks to the succeeding Node
SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
if( pNextNd )
{
SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
const SfxPoolItem *pItem;
if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
false, &pItem ) )
pNextNd->SetAttr( *pItem );
if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
false, &pItem ) )
pNextNd->SetAttr( *pItem );
}
pTableNd->DelFrames();
getIDocumentContentOperations().DeleteSection( pTableNd );
}
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting();
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
return true;
}
SwUndoTableNdsChg* pUndo = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX, aSelBoxes, *pTableNd,
nMin, nMax, 0, false, false );
}
bool bRet(false);
{
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
if (rTable.IsNewModel())
{
if (bColumn)
rTable.PrepareDeleteCol( nMin, nMax );
rTable.FindSuperfluousRows( aSelBoxes );
if (pUndo)
pUndo->ReNewBoxes( aSelBoxes );
}
bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo, true, true );
if (bRet)
{
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting();
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
}
if( pUndo )
{
if( bRet )
{
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
return bRet;
}
/**
* Split up/merge Boxes in the Table
*/
bool SwDoc::SplitTable( const SwSelBoxes& rBoxes, bool bVert, sal_uInt16 nCnt,
bool bSameHeight )
{
OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box list" );
SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
if( !pTableNd )
return false;
SwTable& rTable = pTableNd->GetTable();
if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
return false;
std::vector<sal_uLong> aNdsCnts;
SwTableSortBoxes aTmpLst;
SwUndoTableNdsChg* pUndo = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT, rBoxes, *pTableNd, 0, 0,
nCnt, bVert, bSameHeight );
aTmpLst.insert( rTable.GetTabSortBoxes() );
if( !bVert )
{
for (size_t n = 0; n < rBoxes.size(); ++n)
{
const SwStartNode* pSttNd = rBoxes[ n ]->GetSttNd();
aNdsCnts.push_back( pSttNd->EndOfSectionIndex() -
pSttNd->GetIndex() );
}
}
}
bool bRet(false);
{
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
SwTableFormulaUpdate aMsgHint( &rTable );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
if (bVert)
bRet = rTable.SplitCol( this, rBoxes, nCnt );
else
bRet = rTable.SplitRow( this, rBoxes, nCnt, bSameHeight );
if (bRet)
{
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting();
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
}
if( pUndo )
{
if( bRet )
{
if( bVert )
pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
else
pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
return bRet;
}
TableMergeErr SwDoc::MergeTable( SwPaM& rPam )
{
// Check if the current cursor's Point/Mark are inside a Table
SwTableNode* pTableNd = rPam.GetNode().FindTableNode();
if( !pTableNd )
return TableMergeErr::NoSelection;
SwTable& rTable = pTableNd->GetTable();
if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr )
return TableMergeErr::NoSelection;
TableMergeErr nRet = TableMergeErr::NoSelection;
if( !rTable.IsNewModel() )
{
nRet =::CheckMergeSel( rPam );
if( TableMergeErr::Ok != nRet )
return nRet;
nRet = TableMergeErr::NoSelection;
}
// #i33394#
GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE, nullptr );
RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
SwUndoTableMerge *const pUndo( (GetIDocumentUndoRedo().DoesUndo())
? new SwUndoTableMerge( rPam )
: nullptr );
// Find the Boxes via the Layout
SwSelBoxes aBoxes;
SwSelBoxes aMerged;
SwTableBox* pMergeBox;
if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo ) )
{ // No cells found to merge
getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
if( pUndo )
{
delete pUndo;
SwUndoId nLastUndoId(SwUndoId::EMPTY);
if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
&& (SwUndoId::REDLINE == nLastUndoId))
{
// FIXME: why is this horrible cleanup necessary?
SwUndoRedline *const pU = dynamic_cast<SwUndoRedline*>(
GetUndoManager().RemoveLastUndo());
if (pU && pU->GetRedlSaveCount())
{
SwEditShell *const pEditShell(GetEditShell());
assert(pEditShell);
::sw::UndoRedoContext context(*this, *pEditShell);
static_cast<SfxUndoAction *>(pU)->UndoWithContext(context);
}
delete pU;
}
}
}
else
{
// The PaMs need to be removed from the to-be-deleted range. Thus always place
// them at the end of/on top of the Table; it's always set to the old position via
// the Document Position.
// For a start remember an index for the temporary position, because we cannot
// access it after GetMergeSel
{
rPam.DeleteMark();
rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
rPam.GetPoint()->nContent.Assign( nullptr, 0 );
rPam.SetMark();
rPam.DeleteMark();
SwPaM* pTmp = &rPam;
while( &rPam != ( pTmp = pTmp->GetNext() ))
for( int i = 0; i < 2; ++i )
pTmp->GetBound( static_cast<bool>(i) ) = *rPam.GetPoint();
}
// Merge them
SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo ))
{
nRet = TableMergeErr::Ok;
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
if( pUndo )
{
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
}
else
delete pUndo;
rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
rPam.Move();
::ClearFEShellTabCols(*this, nullptr);
getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
}
GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE, nullptr );
return nRet;
}
SwTableNode::SwTableNode( const SwNodeIndex& rIdx )
: SwStartNode( rIdx, SwNodeType::Table )
{
m_pTable.reset(new SwTable);
}
SwTableNode::~SwTableNode()
{
// Notify UNO wrappers
SwFrameFormat* pTableFormat = GetTable().GetFrameFormat();
SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT,
pTableFormat );
pTableFormat->ModifyNotification( &aMsgHint, &aMsgHint );
DelFrames();
m_pTable->SetTableNode(this); // set this so that ~SwDDETable can read it!
m_pTable.reset();
}
SwTabFrame *SwTableNode::MakeFrame( SwFrame* pSib )
{
return new SwTabFrame( *m_pTable, pSib );
}
/**
* Creates all Views from the Document for the preceding Node. The resulting ContentFrames
* are added to the corresponding Layout.
*/
void SwTableNode::MakeFrames(const SwNodeIndex & rIdx )
{
if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
return;
SwFrame *pFrame;
SwContentNode * pNode = rIdx.GetNode().GetContentNode();
OSL_ENSURE( pNode, "No ContentNode or CopyNode and new Node is identical");
bool bBefore = rIdx < GetIndex();
SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
{
SwFrame *pNew = pNode->MakeFrame( pFrame );
// Will the Node receive Frames before or after?
if ( bBefore )
// The new one precedes me
pNew->Paste( pFrame->GetUpper(), pFrame );
else
// The new one succeeds me
pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
}
}
/**
* Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
*/
void SwTableNode::MakeFrames( SwNodeIndex* pIdxBehind )
{
OSL_ENSURE( pIdxBehind, "No Index" );
*pIdxBehind = *this;
SwNode *pNd = GetNodes().FindPrvNxtFrameNode( *pIdxBehind, EndOfSectionNode() );
if( !pNd )
return ;
SwFrame *pFrame( nullptr );
SwLayoutFrame *pUpper( nullptr );
SwNode2Layout aNode2Layout( *pNd, GetIndex() );
while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, *this )) )
{
SwTabFrame* pNew = MakeFrame( pUpper );
pNew->Paste( pUpper, pFrame );
// #i27138#
// notify accessibility paragraphs objects about changed
// CONTENT_FLOWS_FROM/_TO relation.
// Relation CONTENT_FLOWS_FROM for next paragraph will change
// and relation CONTENT_FLOWS_TO for previous paragraph will change.
{
SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{
pViewShell->InvalidateAccessibleParaFlowRelation(
dynamic_cast<SwTextFrame*>(pNew->FindNextCnt( true )),
dynamic_cast<SwTextFrame*>(pNew->FindPrevCnt()) );
}
}
pNew->RegistFlys();
}
}
void SwTableNode::DelFrames()
{
/* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
The TabFrames are attached to the FrameFormat of the SwTable.
We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
SwIterator<SwTabFrame,SwFormat> aIter( *(m_pTable->GetFrameFormat()) );
SwTabFrame *pFrame = aIter.First();
while ( pFrame )
{
bool bAgain = false;
{
if ( !pFrame->IsFollow() )
{
while ( pFrame->HasFollow() )
pFrame->JoinAndDelFollows();
// #i27138#
// notify accessibility paragraphs objects about changed
// CONTENT_FLOWS_FROM/_TO relation.
// Relation CONTENT_FLOWS_FROM for current next paragraph will change
// and relation CONTENT_FLOWS_TO for current previous paragraph will change.
{
SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
if ( pViewShell && pViewShell->GetLayout() &&
pViewShell->GetLayout()->IsAnyShellAccessible() )
{
pViewShell->InvalidateAccessibleParaFlowRelation(
dynamic_cast<SwTextFrame*>(pFrame->FindNextCnt( true )),
dynamic_cast<SwTextFrame*>(pFrame->FindPrevCnt()) );
}
}
pFrame->Cut();
SwFrame::DestroyFrame(pFrame);
bAgain = true;
}
}
pFrame = bAgain ? aIter.First() : aIter.Next();
}
}
void SwTableNode::SetNewTable( std::unique_ptr<SwTable> pNewTable, bool bNewFrames )
{
DelFrames();
m_pTable->SetTableNode(this);
m_pTable = std::move(pNewTable);
if( bNewFrames )
{
SwNodeIndex aIdx( *EndOfSectionNode());
GetNodes().GoNext( &aIdx );
MakeFrames( &aIdx );
}
}
void SwTableNode::RemoveRedlines()
{
SwDoc* pDoc = GetDoc();
if (pDoc)
{
SwTable& rTable = GetTable();
if ( pDoc->getIDocumentRedlineAccess().HasExtraRedlineTable() )
pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines( pDoc, rTable, true, USHRT_MAX );
}
}
void SwDoc::GetTabCols( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
{
OSL_ENSURE( pBoxFrame, "pBoxFrame needs to be specified!" );
if( !pBoxFrame )
return;
SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
const SwTableBox* pBox = pBoxFrame->GetTabBox();
// Set fixed points, LeftMin in Document coordinates, all others relative
SwRectFnSet aRectFnSet(pTab);
const SwPageFrame* pPage = pTab->FindPageFrame();
const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
aRectFnSet.GetLeft(pPage->getFrameArea());
const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
aRectFnSet.GetLeft(pPage->getFrameArea());
rFill.SetLeftMin ( nLeftMin );
rFill.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
rFill.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
rFill.SetRightMax( nRightMax - nLeftMin );
pTab->GetTable()->GetTabCols( rFill, pBox );
}
// Here are some little helpers used in SwDoc::GetTabRows
#define ROWFUZZY 25
struct FuzzyCompare
{
bool operator() ( long s1, long s2 ) const;
};
bool FuzzyCompare::operator() ( long s1, long s2 ) const
{
return ( s1 < s2 && std::abs( s1 - s2 ) > ROWFUZZY );
}
static bool lcl_IsFrameInColumn( const SwCellFrame& rFrame, SwSelBoxes const & rBoxes )
{
for (size_t i = 0; i < rBoxes.size(); ++i)
{
if ( rFrame.GetTabBox() == rBoxes[ i ] )
return true;
}
return false;
}
void SwDoc::GetTabRows( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
{
OSL_ENSURE( pBoxFrame, "GetTabRows called without pBoxFrame" );
// Make code robust:
if ( !pBoxFrame )
return;
// #i39552# Collection of the boxes of the current
// column has to be done at the beginning of this function, because
// the table may be formatted in ::GetTableSel.
SwDeletionChecker aDelCheck( pBoxFrame );
SwSelBoxes aBoxes;
const SwContentFrame* pContent = ::GetCellContent( *pBoxFrame );
if ( pContent && pContent->IsTextFrame() )
{
const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
const SwCursor aTmpCursor( aPos, nullptr );
::GetTableSel( aTmpCursor, aBoxes, SwTableSearchType::Col );
}
// Make code robust:
if ( aDelCheck.HasBeenDeleted() )
{
OSL_FAIL( "Current box has been deleted during GetTabRows()" );
return;
}
// Make code robust:
const SwTabFrame* pTab = pBoxFrame->FindTabFrame();
OSL_ENSURE( pTab, "GetTabRows called without a table" );
if ( !pTab )
return;
const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
// Set fixed points, LeftMin in Document coordinates, all others relative
SwRectFnSet aRectFnSet(pTab);
const SwPageFrame* pPage = pTab->FindPageFrame();
const long nLeftMin = ( aRectFnSet.IsVert() ?
pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
pTab->GetPrtTop() - pPage->getFrameArea().Top() );
const long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
const long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
const long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
rFill.SetLeftMin( nLeftMin );
rFill.SetLeft( nLeft );
rFill.SetRight( nRight );
rFill.SetRightMax( nRightMax );
typedef std::map< long, std::pair< long, long >, FuzzyCompare > BoundaryMap;
BoundaryMap aBoundaries;
BoundaryMap::iterator aIter;
std::pair< long, long > aPair;
typedef std::map< long, bool > HiddenMap;
HiddenMap aHidden;
HiddenMap::iterator aHiddenIter;
while ( pFrame && pTab->IsAnLower( pFrame ) )
{
if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
{
// upper and lower borders of current cell frame:
long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
// get boundaries for nUpperBorder:
aIter = aBoundaries.find( nUpperBorder );
if ( aIter == aBoundaries.end() )
{
aPair.first = nUpperBorder; aPair.second = LONG_MAX;
aBoundaries[ nUpperBorder ] = aPair;
}
// get boundaries for nLowerBorder:
aIter = aBoundaries.find( nLowerBorder );
if ( aIter == aBoundaries.end() )
{
aPair.first = nUpperBorder; aPair.second = LONG_MAX;
}
else
{
nLowerBorder = (*aIter).first;
long nNewLowerBorderUpperBoundary = std::max( (*aIter).second.first, nUpperBorder );
aPair.first = nNewLowerBorderUpperBoundary; aPair.second = LONG_MAX;
}
aBoundaries[ nLowerBorder ] = aPair;
// calculate hidden flags for entry nUpperBorder/nLowerBorder:
long nTmpVal = nUpperBorder;
for ( sal_uInt8 i = 0; i < 2; ++i )
{
aHiddenIter = aHidden.find( nTmpVal );
if ( aHiddenIter == aHidden.end() )
aHidden[ nTmpVal ] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes );
else
{
if ( aHidden[ nTmpVal ] &&
lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes ) )
aHidden[ nTmpVal ] = false;
}
nTmpVal = nLowerBorder;
}
}
pFrame = pFrame->GetNextLayoutLeaf();
}
// transfer calculated values from BoundaryMap and HiddenMap into rFill:
size_t nIdx = 0;
for ( aIter = aBoundaries.begin(); aIter != aBoundaries.end(); ++aIter )
{
const long nTabTop = aRectFnSet.GetPrtTop(*pTab);
const long nKey = aRectFnSet.YDiff( (*aIter).first, nTabTop );
const std::pair< long, long > aTmpPair = (*aIter).second;
const long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
const long nSecond = aTmpPair.second;
aHiddenIter = aHidden.find( (*aIter).first );
const bool bHidden = aHiddenIter != aHidden.end() && (*aHiddenIter).second;
rFill.Insert( nKey, nFirst, nSecond, bHidden, nIdx++ );
}
// delete first and last entry
OSL_ENSURE( rFill.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
// #i60818# There may be only one entry in rFill. Make
// code robust by checking count of rFill.
if ( rFill.Count() ) rFill.Remove( 0 );
if ( rFill.Count() ) rFill.Remove( rFill.Count() - 1 );
rFill.SetLastRowAllowedToChange( !pTab->HasFollowFlowLine() );
}
void SwDoc::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly,
const SwCellFrame* pBoxFrame )
{
const SwTableBox* pBox = nullptr;
SwTabFrame *pTab = nullptr;
if( pBoxFrame )
{
pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
pBox = pBoxFrame->GetTabBox();
}
else
{
OSL_ENSURE( false, "must specify pBoxFrame" );
return ;
}
// If the Table is still using relative values (USHRT_MAX)
// we need to switch to absolute ones.
SwTable& rTab = *pTab->GetTable();
const SwFormatFrameSize& rTableFrameSz = rTab.GetFrameFormat()->GetFrameSize();
SwRectFnSet aRectFnSet(pTab);
// #i17174# - With fix for #i9040# the shadow size is taken
// from the table width. Thus, add its left and right size to current table
// printing area width in order to get the correct table size attribute.
SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
{
SvxShadowItem aShadow( rTab.GetFrameFormat()->GetShadow() );
nPrtWidth += aShadow.CalcShadowSpace( SvxShadowItemSide::LEFT ) +
aShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
}
if( nPrtWidth != rTableFrameSz.GetWidth() )
{
SwFormatFrameSize aSz( rTableFrameSz );
aSz.SetWidth( nPrtWidth );
rTab.GetFrameFormat()->SetFormatAttr( aSz );
}
SwTabCols aOld( rNew.Count() );
const SwPageFrame* pPage = pTab->FindPageFrame();
const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
aRectFnSet.GetLeft(pPage->getFrameArea());
const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
aRectFnSet.GetLeft(pPage->getFrameArea());
// Set fixed points, LeftMin in Document coordinates, all others relative
aOld.SetLeftMin ( nLeftMin );
aOld.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
aOld.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
aOld.SetRightMax( nRightMax - nLeftMin );
rTab.GetTabCols( aOld, pBox );
SetTabCols(rTab, rNew, aOld, pBox, bCurRowOnly );
}
void SwDoc::SetTabRows( const SwTabCols &rNew, bool bCurColOnly,
const SwCellFrame* pBoxFrame )
{
SwTabFrame *pTab = nullptr;
if( pBoxFrame )
{
pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
}
else
{
OSL_ENSURE( false, "must specify pBoxFrame" );
return ;
}
// If the Table is still using relative values (USHRT_MAX)
// we need to switch to absolute ones.
SwRectFnSet aRectFnSet(pTab);
SwTabCols aOld( rNew.Count() );
// Set fixed points, LeftMin in Document coordinates, all others relative
const SwPageFrame* pPage = pTab->FindPageFrame();
aOld.SetRight( aRectFnSet.GetHeight(pTab->getFramePrintArea()) );
long nLeftMin;
if ( aRectFnSet.IsVert() )
{
nLeftMin = pTab->GetPrtLeft() - pPage->getFrameArea().Left();
aOld.SetLeft ( LONG_MAX );
aOld.SetRightMax( aOld.GetRight() );
}
else
{
nLeftMin = pTab->GetPrtTop() - pPage->getFrameArea().Top();
aOld.SetLeft ( 0 );
aOld.SetRightMax( LONG_MAX );
}
aOld.SetLeftMin ( nLeftMin );
GetTabRows( aOld, pBoxFrame );
GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR, nullptr );
// check for differences between aOld and rNew:
const size_t nCount = rNew.Count();
const SwTable* pTable = pTab->GetTable();
OSL_ENSURE( pTable, "My colleague told me, this couldn't happen" );
for ( size_t i = 0; i <= nCount; ++i )
{
const size_t nIdxStt = aRectFnSet.IsVert() ? nCount - i : i - 1;
const size_t nIdxEnd = aRectFnSet.IsVert() ? nCount - i - 1 : i;
const long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
const long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
const long nOldRowHeight = nOldRowEnd - nOldRowStart;
const long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
const long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
const long nNewRowHeight = nNewRowEnd - nNewRowStart;
const long nDiff = nNewRowHeight - nOldRowHeight;
if ( std::abs( nDiff ) >= ROWFUZZY )
{
// For the old table model pTextFrame and pLine will be set for every box.
// For the new table model pTextFrame will be set if the box is not covered,
// but the pLine will be set if the box is not an overlapping box
// In the new table model the row height can be adjusted,
// when both variables are set.
const SwTextFrame* pTextFrame = nullptr;
const SwTableLine* pLine = nullptr;
// Iterate over all SwCellFrames with Bottom = nOldPos
const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
while ( pFrame && pTab->IsAnLower( pFrame ) )
{
if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
{
const long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
const sal_uLong nTabTop = aRectFnSet.GetPrtTop(*pTab);
if ( std::abs( aRectFnSet.YInc( nTabTop, nOldRowEnd ) - nLowerBorder ) <= ROWFUZZY )
{
if ( !bCurColOnly || pFrame == pBoxFrame )
{
const SwFrame* pContent = ::GetCellContent( static_cast<const SwCellFrame&>(*pFrame) );
if ( pContent && pContent->IsTextFrame() )
{
const SwTableBox* pBox = static_cast<const SwCellFrame*>(pFrame)->GetTabBox();
const long nRowSpan = pBox->getRowSpan();
if( nRowSpan > 0 ) // Not overlapped
pTextFrame = static_cast<const SwTextFrame*>(pContent);
if( nRowSpan < 2 ) // Not overlapping for row height
pLine = pBox->GetUpper();
if( pLine && pTextFrame ) // always for old table model
{
// The new row height must not to be calculated from a overlapping box
SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
const long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
if( nNewSize != aNew.GetHeight() )
{
aNew.SetHeight( nNewSize );
if ( ATT_VAR_SIZE == aNew.GetHeightSizeType() )
aNew.SetHeightSizeType( ATT_MIN_SIZE );
// This position must not be in an overlapped box
const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
const SwCursor aTmpCursor( aPos, nullptr );
SetRowHeight( aTmpCursor, aNew );
// For the new table model we're done, for the old one
// there might be another (sub)row to adjust...
if( pTable->IsNewModel() )
break;
}
pLine = nullptr;
}
}
}
}
}
pFrame = pFrame->GetNextLayoutLeaf();
}
}
}
GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR, nullptr );
::ClearFEShellTabCols(*this, nullptr);
}
/**
* Direct access for UNO
*/
void SwDoc::SetTabCols(SwTable& rTab, const SwTabCols &rNew, const SwTabCols &rOld,
const SwTableBox *pStart, bool bCurRowOnly )
{
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().AppendUndo(
new SwUndoAttrTable( *rTab.GetTableNode(), true ));
}
rTab.SetTabCols( rNew, rOld, pStart, bCurRowOnly );
::ClearFEShellTabCols(*this, nullptr);
getIDocumentState().SetModified();
}
void SwDoc::SetRowsToRepeat( SwTable &rTable, sal_uInt16 nSet )
{
if( nSet == rTable.GetRowsToRepeat() )
return;
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().AppendUndo(
new SwUndoTableHeadline(rTable, rTable.GetRowsToRepeat(), nSet) );
}
SwMsgPoolItem aChg( RES_TBLHEADLINECHG );
rTable.SetRowsToRepeat( nSet );
rTable.GetFrameFormat()->ModifyNotification( &aChg, &aChg );
getIDocumentState().SetModified();
}
void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
{
if( pHst )
pHst->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text );
}
void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
{
aPosArr.push_back(nWidth);
SwTableBox* p = const_cast<SwTableBox*>(&rBox);
m_Boxes.push_back(p);
nWidth = nWidth + static_cast<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
}
const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
{
const SwTableBox* pRet = nullptr;
if( !aPosArr.empty() )
{
std::vector<sal_uInt16>::size_type n;
for( n = 0; n < aPosArr.size(); ++n )
if( aPosArr[ n ] == nWidth )
break;
else if( aPosArr[ n ] > nWidth )
{
if( n )
--n;
break;
}
if( n >= aPosArr.size() )
--n;
nWidth = nWidth + static_cast<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
pRet = m_Boxes[ n ];
}
return pRet;
}
bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
{
if( !aPosArr.empty() )
{
std::vector<sal_uInt16>::size_type n;
for( n = 0; n < aPosArr.size(); ++n )
{
if( aPosArr[ n ] == nOffset )
break;
else if( aPosArr[ n ] > nOffset )
{
if( n )
--n;
break;
}
}
aPosArr.erase( aPosArr.begin(), aPosArr.begin() + n );
m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
size_t nArrSize = aPosArr.size();
if (nArrSize)
{
if (nOldWidth == 0)
throw o3tl::divide_by_zero();
// Adapt the positions to the new Size
for( n = 0; n < nArrSize; ++n )
{
sal_uLong nSize = nWidth;
nSize *= ( aPosArr[ n ] - nOffset );
nSize /= nOldWidth;
aPosArr[ n ] = sal_uInt16( nSize );
}
}
}
return !aPosArr.empty();
}
bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
{
SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
if( pSplPara->IsGetValues() )
for( SwTableBoxes::iterator it = const_cast<SwTableLine*>(rpLine)->GetTabBoxes().begin();
it != const_cast<SwTableLine*>(rpLine)->GetTabBoxes().end(); ++it)
sw_Box_CollectBox(*it, pSplPara );
else
for( SwTableBoxes::iterator it = const_cast<SwTableLine*>(rpLine)->GetTabBoxes().begin();
it != const_cast<SwTableLine*>(rpLine)->GetTabBoxes().end(); ++it)
sw_BoxSetSplitBoxFormats(*it, pSplPara );
return true;
}
void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
{
auto nLen = pBox->GetTabLines().size();
if( nLen )
{
// Continue with the actual Line
if( pSplPara->IsGetFromTop() )
nLen = 0;
else
--nLen;
const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
sw_Line_CollectBox( pLn, pSplPara );
}
else
pSplPara->AddBox( *pBox );
}
void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
{
auto nLen = pBox->GetTabLines().size();
if( nLen )
{
// Continue with the actual Line
if( pSplPara->IsGetFromTop() )
nLen = 0;
else
--nLen;
const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
sw_Line_CollectBox( pLn, pSplPara );
}
else
{
const SwTableBox* pSrcBox = pSplPara->GetBoxOfPos( *pBox );
SwFrameFormat* pFormat = pSrcBox->GetFrameFormat();
if( SplitTable_HeadlineOption::BorderCopy == pSplPara->GetMode() )
{
const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
if( !rBoxItem.GetTop() )
{
SvxBoxItem aNew( rBoxItem );
aNew.SetLine( pFormat->GetBox().GetBottom(), SvxBoxItemLine::TOP );
if( aNew != rBoxItem )
pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
}
}
else
{
sal_uInt16 const aTableSplitBoxSetRange[] {
RES_LR_SPACE, RES_UL_SPACE,
RES_BACKGROUND, RES_SHADOW,
RES_PROTECT, RES_PROTECT,
RES_VERT_ORIENT, RES_VERT_ORIENT,
0 };
SfxItemSet aTmpSet( pFormat->GetDoc()->GetAttrPool(),
aTableSplitBoxSetRange );
aTmpSet.Put( pFormat->GetAttrSet() );
if( aTmpSet.Count() )
pBox->ClaimFrameFormat()->SetFormatAttr( aTmpSet );
if( SplitTable_HeadlineOption::BoxAttrAllCopy == pSplPara->GetMode() )
{
SwNodeIndex aIdx( *pSrcBox->GetSttNd(), 1 );
SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
if( !pCNd )
pCNd = aIdx.GetNodes().GoNext( &aIdx );
aIdx = *pBox->GetSttNd();
SwContentNode* pDNd = aIdx.GetNodes().GoNext( &aIdx );
// If the Node is alone in the Section
if( 2 == pDNd->EndOfSectionIndex() -
pDNd->StartOfSectionIndex() )
{
pSplPara->AddToUndoHistory( *pDNd );
pDNd->ChgFormatColl( pCNd->GetFormatColl() );
}
}
// note conditional template
pBox->GetSttNd()->CheckSectionCondColl();
}
}
}
/**
* Splits a Table in the top-level Line which contains the Index.
* All succeeding top-level Lines go into a new Table/Node.
*
* @param bCalcNewSize true
* Calculate the new Size for both from the
* Boxes' Max; but only if Size is using absolute
* values (USHRT_MAX)
*/
bool SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
bool bCalcNewSize )
{
SwNode* pNd = &rPos.nNode.GetNode();
SwTableNode* pTNd = pNd->FindTableNode();
if( !pTNd || pNd->IsTableNode() )
return false;
if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
return false;
SwTable& rTable = pTNd->GetTable();
rTable.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
SwTableFormulaUpdate aMsgHint( &rTable );
SwHistory aHistory;
if (GetIDocumentUndoRedo().DoesUndo())
{
aMsgHint.m_pHistory = &aHistory;
}
{
sal_uLong nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
// Find top-level Line
SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
if( pBox )
{
SwTableLine* pLine = pBox->GetUpper();
while( pLine->GetUpper() )
pLine = pLine->GetUpper()->GetUpper();
// pLine contains the top-level Line now
aMsgHint.m_nSplitLine = rTable.GetTabLines().GetPos( pLine );
}
OUString sNewTableNm( GetUniqueTableName() );
aMsgHint.m_aData.pNewTableNm = &sNewTableNm;
aMsgHint.m_eFlags = TBL_SPLITTBL;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
}
// Find Lines for the Layout update
FndBox_ aFndBox( nullptr, nullptr );
aFndBox.SetTableLines( rTable );
aFndBox.DelFrames( rTable );
SwTableNode* pNew = GetNodes().SplitTable( rPos.nNode, false, bCalcNewSize );
if( pNew )
{
std::unique_ptr<SwSaveRowSpan> pSaveRowSp = pNew->GetTable().CleanUpTopRowSpan( rTable.GetTabLines().size() );
SwUndoSplitTable* pUndo = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoSplitTable(
*pNew, std::move(pSaveRowSp), eHdlnMode, bCalcNewSize);
GetIDocumentUndoRedo().AppendUndo(pUndo);
if( aHistory.Count() )
pUndo->SaveFormula( aHistory );
}
switch( eHdlnMode )
{
// Set the lower Border of the preceding Line to
// the upper Border of the current one
case SplitTable_HeadlineOption::BorderCopy:
{
SwCollectTableLineBoxes aPara( false, eHdlnMode );
SwTableLine* pLn = rTable.GetTabLines()[
rTable.GetTabLines().size() - 1 ];
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_Box_CollectBox(*it, &aPara );
aPara.SetValues( true );
pLn = pNew->GetTable().GetTabLines()[ 0 ];
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_BoxSetSplitBoxFormats(*it, &aPara );
// Switch off repeating Header
pNew->GetTable().SetRowsToRepeat( 0 );
}
break;
// Take over the Attributes of the first Line to the new one
case SplitTable_HeadlineOption::BoxAttrCopy:
case SplitTable_HeadlineOption::BoxAttrAllCopy:
{
SwHistory* pHst = nullptr;
if( SplitTable_HeadlineOption::BoxAttrAllCopy == eHdlnMode && pUndo )
pHst = pUndo->GetHistory();
SwCollectTableLineBoxes aPara( true, eHdlnMode, pHst );
SwTableLine* pLn = rTable.GetTabLines()[ 0 ];
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_Box_CollectBox(*it, &aPara );
aPara.SetValues( true );
pLn = pNew->GetTable().GetTabLines()[ 0 ];
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
sw_BoxSetSplitBoxFormats(*it, &aPara );
}
break;
case SplitTable_HeadlineOption::ContentCopy:
rTable.CopyHeadlineIntoTable( *pNew );
if( pUndo )
pUndo->SetTableNodeOffset( pNew->GetIndex() );
break;
case SplitTable_HeadlineOption::NONE:
// Switch off repeating the Header
pNew->GetTable().SetRowsToRepeat( 0 );
break;
}
// And insert Frames
SwNodeIndex aNdIdx( *pNew->EndOfSectionNode() );
GetNodes().GoNext( &aNdIdx ); // To the next ContentNode
pNew->MakeFrames( &aNdIdx );
// Insert a paragraph between the Table
GetNodes().MakeTextNode( SwNodeIndex( *pNew ),
getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
}
// Update Layout
aFndBox.MakeFrames( rTable );
// TL_CHART2: need to inform chart of probably changed cell names
UpdateCharts( rTable.GetFrameFormat()->GetName() );
// update table style formatting of both the tables
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pTNd);
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pNew);
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
return nullptr != pNew;
}
static bool lcl_ChgTableSize( SwTable& rTable )
{
// The Attribute must not be set via the Modify or else all Boxes are
// set back to 0.
// So lock the Format.
SwFrameFormat* pFormat = rTable.GetFrameFormat();
SwFormatFrameSize aTableMaxSz( pFormat->GetFrameSize() );
if( USHRT_MAX == aTableMaxSz.GetWidth() )
return false;
bool bLocked = pFormat->IsModifyLocked();
pFormat->LockModify();
aTableMaxSz.SetWidth( 0 );
SwTableLines& rLns = rTable.GetTabLines();
for( auto pLn : rLns )
{
SwTwips nMaxLnWidth = 0;
SwTableBoxes& rBoxes = pLn->GetTabBoxes();
for( auto pBox : rBoxes )
nMaxLnWidth += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
if( nMaxLnWidth > aTableMaxSz.GetWidth() )
aTableMaxSz.SetWidth( nMaxLnWidth );
}
pFormat->SetFormatAttr( aTableMaxSz );
if( !bLocked ) // Release the Lock if appropriate
pFormat->UnlockModify();
return true;
}
class SplitTable_Para
{
std::map<SwFrameFormat const *, SwFrameFormat*> aSrcDestMap;
SwTableNode* pNewTableNd;
SwTable& rOldTable;
public:
SplitTable_Para( SwTableNode* pNew, SwTable& rOld )
: aSrcDestMap(), pNewTableNd( pNew ), rOldTable( rOld )
{}
SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
{
auto it = aSrcDestMap.find( pSrcFormat );
return it == aSrcDestMap.end() ? nullptr : it->second;
}
void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
{ aSrcDestMap[ pSrcFormat ] = pDestFormat; }
void ChgBox( SwTableBox* pBox )
{
rOldTable.GetTabSortBoxes().erase( pBox );
pNewTableNd->GetTable().GetTabSortBoxes().insert( pBox );
}
};
static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara );
static void lcl_SplitTable_CpyLine( SwTableLine* pLn, SplitTable_Para* pPara )
{
SwFrameFormat *pSrcFormat = pLn->GetFrameFormat();
SwTableLineFormat* pDestFormat = static_cast<SwTableLineFormat*>( pPara->GetDestFormat( pSrcFormat ) );
if( pDestFormat == nullptr )
{
pPara->InsertSrcDest( pSrcFormat, pLn->ClaimFrameFormat() );
}
else
pLn->ChgFrameFormat( pDestFormat );
for( SwTableBoxes::iterator it = pLn->GetTabBoxes().begin();
it != pLn->GetTabBoxes().end(); ++it)
lcl_SplitTable_CpyBox(*it, pPara );
}
static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara )
{
SwFrameFormat *pSrcFormat = pBox->GetFrameFormat();
SwTableBoxFormat* pDestFormat = static_cast<SwTableBoxFormat*>(pPara->GetDestFormat( pSrcFormat ));
if( pDestFormat == nullptr )
{
pPara->InsertSrcDest( pSrcFormat, pBox->ClaimFrameFormat() );
}
else
pBox->ChgFrameFormat( pDestFormat );
if( pBox->GetSttNd() )
pPara->ChgBox( pBox );
else
for( SwTableLine* pLine : pBox->GetTabLines() )
lcl_SplitTable_CpyLine( pLine, pPara );
}
SwTableNode* SwNodes::SplitTable( const SwNodeIndex& rPos, bool bAfter,
bool bCalcNewSize )
{
SwNode* pNd = &rPos.GetNode();
SwTableNode* pTNd = pNd->FindTableNode();
if( !pTNd || pNd->IsTableNode() )
return nullptr;
sal_uLong nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
// Find this Box/top-level line
SwTable& rTable = pTNd->GetTable();
SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
if( !pBox )
return nullptr;
SwTableLine* pLine = pBox->GetUpper();
while( pLine->GetUpper() )
pLine = pLine->GetUpper()->GetUpper();
// pLine now contains the top-level line
sal_uInt16 nLinePos = rTable.GetTabLines().GetPos( pLine );
if( USHRT_MAX == nLinePos ||
( bAfter ? ++nLinePos >= rTable.GetTabLines().size() : !nLinePos ))
return nullptr; // Not found or last Line!
// Find the first Box of the succeeding Line
SwTableLine* pNextLine = rTable.GetTabLines()[ nLinePos ];
pBox = pNextLine->GetTabBoxes()[0];
while( !pBox->GetSttNd() )
pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
// Insert an EndNode and TableNode into the Nodes Array
SwTableNode * pNewTableNd;
{
SwEndNode* pOldTableEndNd = pTNd->EndOfSectionNode()->GetEndNode();
assert(pOldTableEndNd && "Where is the EndNode?");
SwNodeIndex aIdx( *pBox->GetSttNd() );
new SwEndNode( aIdx, *pTNd );
pNewTableNd = new SwTableNode( aIdx );
pNewTableNd->GetTable().SetTableModel( rTable.IsNewModel() );
pOldTableEndNd->m_pStartOfSection = pNewTableNd;
pNewTableNd->m_pEndOfSection = pOldTableEndNd;
SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
do {
OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
pBoxNd->m_pStartOfSection = pNewTableNd;
pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
} while( pBoxNd != pOldTableEndNd );
}
{
// Move the Lines
SwTable& rNewTable = pNewTableNd->GetTable();
rNewTable.GetTabLines().insert( rNewTable.GetTabLines().begin(),
rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
/* From the back (bottom right) to the front (top left) deregister all Boxes from the
Chart Data Provider. The Modify event is triggered in the calling function.
TL_CHART2: */
SwChartDataProvider *pPCD = rTable.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
if( pPCD )
{
for (SwTableLines::size_type k = nLinePos; k < rTable.GetTabLines().size(); ++k)
{
const SwTableLines::size_type nLineIdx = (rTable.GetTabLines().size() - 1) - k + nLinePos;
const SwTableBoxes::size_type nBoxCnt = rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes().size();
for (SwTableBoxes::size_type j = 0; j < nBoxCnt; ++j)
{
const SwTableBoxes::size_type nIdx = nBoxCnt - 1 - j;
pPCD->DeleteBox( &rTable, *rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes()[nIdx] );
}
}
}
// Delete
sal_uInt16 nDeleted = rTable.GetTabLines().size() - nLinePos;
rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
// Move the affected Boxes. Make the Formats unique and correct the StartNodes
SplitTable_Para aPara( pNewTableNd, rTable );
for( SwTableLine* pNewLine : rNewTable.GetTabLines() )
lcl_SplitTable_CpyLine( pNewLine, &aPara );
rTable.CleanUpBottomRowSpan( nDeleted );
}
{
// Copy the Table FrameFormat
SwFrameFormat* pOldTableFormat = rTable.GetFrameFormat();
SwFrameFormat* pNewTableFormat = pOldTableFormat->GetDoc()->MakeTableFrameFormat(
pOldTableFormat->GetDoc()->GetUniqueTableName(),
pOldTableFormat->GetDoc()->GetDfltFrameFormat() );
*pNewTableFormat = *pOldTableFormat;
pNewTableNd->GetTable().RegisterToFormat( *pNewTableFormat );
pNewTableNd->GetTable().SetTableStyleName(rTable.GetTableStyleName());
// Calculate a new Size?
// lcl_ChgTableSize: Only execute the second call if the first call was
// successful, thus has an absolute Size
if( bCalcNewSize && lcl_ChgTableSize( rTable ) )
lcl_ChgTableSize( pNewTableNd->GetTable() );
}
// TL_CHART2: need to inform chart of probably changed cell names
rTable.UpdateCharts();
return pNewTableNd; // That's it!
}
/**
* rPos needs to be in the Table that remains
*
* @param bWithPrev merge the current Table with the preceding
* or succeeding one
*/
bool SwDoc::MergeTable( const SwPosition& rPos, bool bWithPrev, sal_uInt16 nMode )
{
SwTableNode* pTableNd = rPos.nNode.GetNode().FindTableNode(), *pDelTableNd;
if( !pTableNd )
return false;
SwNodes& rNds = GetNodes();
if( bWithPrev )
pDelTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
else
pDelTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
if( !pDelTableNd )
return false;
if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr ||
dynamic_cast<const SwDDETable*>( &pDelTableNd->GetTable() ) != nullptr)
return false;
// Delete HTML Layout
pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
pDelTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
// Both Tables are present; we can start
SwUndoMergeTable* pUndo = nullptr;
SwHistory* pHistory = nullptr;
if (GetIDocumentUndoRedo().DoesUndo())
{
pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev, nMode );
GetIDocumentUndoRedo().AppendUndo(pUndo);
pHistory = new SwHistory;
}
// Adapt all "TableFormulas"
SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
aMsgHint.m_aData.pDelTable = &pDelTableNd->GetTable();
aMsgHint.m_eFlags = TBL_MERGETBL;
aMsgHint.m_pHistory = pHistory;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
// The actual merge
SwNodeIndex aIdx( bWithPrev ? *pTableNd : *pDelTableNd );
bool bRet = rNds.MergeTable( aIdx, !bWithPrev, nMode );
if( pHistory )
{
if( pHistory->Count() )
pUndo->SaveFormula( *pHistory );
delete pHistory;
}
if( bRet )
{
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting();
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
return bRet;
}
bool SwNodes::MergeTable( const SwNodeIndex& rPos, bool bWithPrev,
sal_uInt16 nMode )
{
SwTableNode* pDelTableNd = rPos.GetNode().GetTableNode();
OSL_ENSURE( pDelTableNd, "Where did the TableNode go?" );
SwTableNode* pTableNd = (*this)[ rPos.GetIndex() - 1]->FindTableNode();
OSL_ENSURE( pTableNd, "Where did the TableNode go?" );
if( !pDelTableNd || !pTableNd )
return false;
pDelTableNd->DelFrames();
SwTable& rDelTable = pDelTableNd->GetTable();
SwTable& rTable = pTableNd->GetTable();
// Find Lines for the Layout update
FndBox_ aFndBox( nullptr, nullptr );
aFndBox.SetTableLines( rTable );
aFndBox.DelFrames( rTable );
// TL_CHART2:
// tell the charts about the table to be deleted and have them use their own data
GetDoc()->getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable );
// Sync the TableFormat's Width
{
const SwFormatFrameSize& rTableSz = rTable.GetFrameFormat()->GetFrameSize();
const SwFormatFrameSize& rDelTableSz = rDelTable.GetFrameFormat()->GetFrameSize();
if( rTableSz != rDelTableSz )
{
// The needs correction
if( bWithPrev )
rDelTable.GetFrameFormat()->SetFormatAttr( rTableSz );
else
rTable.GetFrameFormat()->SetFormatAttr( rDelTableSz );
}
}
if( !bWithPrev )
{
// Transfer all Attributes of the succeeding Table to the preceding one
// We do this, because the succeeding one is deleted when deleting the Node
rTable.SetRowsToRepeat( rDelTable.GetRowsToRepeat() );
rTable.SetTableChgMode( rDelTable.GetTableChgMode() );
rTable.GetFrameFormat()->LockModify();
*rTable.GetFrameFormat() = *rDelTable.GetFrameFormat();
// Also switch the Name
rTable.GetFrameFormat()->SetName( rDelTable.GetFrameFormat()->GetName() );
rTable.GetFrameFormat()->UnlockModify();
}
// Move the Lines and Boxes
SwTableLines::size_type nOldSize = rTable.GetTabLines().size();
rTable.GetTabLines().insert( rTable.GetTabLines().begin() + nOldSize,
rDelTable.GetTabLines().begin(), rDelTable.GetTabLines().end() );
rDelTable.GetTabLines().clear();
rTable.GetTabSortBoxes().insert( rDelTable.GetTabSortBoxes() );
rDelTable.GetTabSortBoxes().clear();
// The preceding Table always remains, while the succeeding one is deleted
SwEndNode* pTableEndNd = pDelTableNd->EndOfSectionNode();
pTableNd->m_pEndOfSection = pTableEndNd;
SwNodeIndex aIdx( *pDelTableNd, 1 );
SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
do {
OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
pBoxNd->m_pStartOfSection = pTableNd;
pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
} while( pBoxNd != pTableEndNd );
pBoxNd->m_pStartOfSection = pTableNd;
aIdx -= 2;
DelNodes( aIdx, 2 );
// tweak the conditional styles at the first inserted Line
const SwTableLine* pFirstLn = rTable.GetTabLines()[ nOldSize ];
if( 1 == nMode )
{
// Set Header Template in the Line and save in the History
// if needed for Undo!
}
sw_LineSetHeadCondColl( pFirstLn );
// Clean up the Borders
if( nOldSize )
{
SwGCLineBorder aPara( rTable );
aPara.nLinePos = --nOldSize;
pFirstLn = rTable.GetTabLines()[ nOldSize ];
sw_GC_Line_Border( pFirstLn, &aPara );
}
// Update Layout
aFndBox.MakeFrames( rTable );
return true;
}
// Use the PtrArray's ForEach method
struct SetAFormatTabPara
{
SwTableAutoFormat& rTableFormat;
SwUndoTableAutoFormat* pUndo;
sal_uInt16 nEndBox, nCurBox;
sal_uInt8 nAFormatLine, nAFormatBox;
explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
: rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 )
{}
};
// Forward declare so that the Lines and Boxes can use recursion
static bool lcl_SetAFormatBox(FndBox_ &, SetAFormatTabPara *pSetPara, bool bResetDirect);
static bool lcl_SetAFormatLine(FndLine_ &, SetAFormatTabPara *pPara, bool bResetDirect);
static bool lcl_SetAFormatLine(FndLine_ & rLine, SetAFormatTabPara *pPara, bool bResetDirect)
{
for (auto const& it : rLine.GetBoxes())
{
lcl_SetAFormatBox(*it, pPara, bResetDirect);
}
return true;
}
static bool lcl_SetAFormatBox(FndBox_ & rBox, SetAFormatTabPara *pSetPara, bool bResetDirect)
{
if (!rBox.GetUpper()->GetUpper()) // Box on first level?
{
if( !pSetPara->nCurBox )
pSetPara->nAFormatBox = 0;
else if( pSetPara->nCurBox == pSetPara->nEndBox )
pSetPara->nAFormatBox = 3;
else
pSetPara->nAFormatBox = static_cast<sal_uInt8>(1 + ((pSetPara->nCurBox-1) & 1));
}
if (rBox.GetBox()->GetSttNd())
{
SwTableBox* pSetBox = rBox.GetBox();
if (!pSetBox->HasDirectFormatting() || bResetDirect)
{
if (bResetDirect)
pSetBox->SetDirectFormatting(false);
SwDoc* pDoc = pSetBox->GetFrameFormat()->GetDoc();
SfxItemSet aCharSet(pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1>{});
SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
pSetPara->rTableFormat.UpdateToSet(nPos, aCharSet, SwTableAutoFormat::UPDATE_CHAR, nullptr);
pSetPara->rTableFormat.UpdateToSet(nPos, aBoxSet, SwTableAutoFormat::UPDATE_BOX, pDoc->GetNumberFormatter());
if (aCharSet.Count())
{
sal_uLong nSttNd = pSetBox->GetSttIdx()+1;
sal_uLong nEndNd = pSetBox->GetSttNd()->EndOfSectionIndex();
for (; nSttNd < nEndNd; ++nSttNd)
{
SwContentNode* pNd = pDoc->GetNodes()[ nSttNd ]->GetContentNode();
if (pNd)
pNd->SetAttr(aCharSet);
}
}
if (aBoxSet.Count())
{
if (pSetPara->pUndo && SfxItemState::SET == aBoxSet.GetItemState(RES_BOXATR_FORMAT))
pSetPara->pUndo->SaveBoxContent( *pSetBox );
pSetBox->ClaimFrameFormat()->SetFormatAttr(aBoxSet);
}
}
}
else
{
for (auto const& rpFndLine : rBox.GetLines())
{
lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
}
}
if (!rBox.GetUpper()->GetUpper()) // a BaseLine
++pSetPara->nCurBox;
return true;
}
bool SwDoc::SetTableAutoFormat(const SwSelBoxes& rBoxes, const SwTableAutoFormat& rNew, bool bResetDirect, bool const isSetStyleName)
{
OSL_ENSURE( !rBoxes.empty(), "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( pTableNd->GetTable().GetTabLines(), &aPara );
}
if( aFndBox.GetLines().empty() )
return false;
SwTable &table = pTableNd->GetTable();
table.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
FndBox_* pFndBox = &aFndBox;
while( 1 == pFndBox->GetLines().size() &&
1 == pFndBox->GetLines().front()->GetBoxes().size())
{
pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
}
if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
pFndBox = pFndBox->GetUpper()->GetUpper();
// Disable Undo, but first store parameters
SwUndoTableAutoFormat* pUndo = nullptr;
bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
if (bUndo)
{
pUndo = new SwUndoTableAutoFormat( *pTableNd, rNew );
GetIDocumentUndoRedo().AppendUndo(pUndo);
GetIDocumentUndoRedo().DoUndo(false);
}
if (isSetStyleName)
{ // tdf#98226 do this here where undo can record it
pTableNd->GetTable().SetTableStyleName(rNew.GetName());
}
rNew.RestoreTableProperties(table);
SetAFormatTabPara aPara( rNew );
FndLines_t& rFLns = pFndBox->GetLines();
for (FndLines_t::size_type n = 0; n < rFLns.size(); ++n)
{
FndLine_* pLine = rFLns[n].get();
// Set Upper to 0 (thus simulate BaseLine)
FndBox_* pSaveBox = pLine->GetUpper();
pLine->SetUpper( nullptr );
if( !n )
aPara.nAFormatLine = 0;
else if (static_cast<size_t>(n+1) == rFLns.size())
aPara.nAFormatLine = 3;
else
aPara.nAFormatLine = static_cast<sal_uInt8>(1 + ((n-1) & 1 ));
aPara.nAFormatBox = 0;
aPara.nCurBox = 0;
aPara.nEndBox = pLine->GetBoxes().size()-1;
aPara.pUndo = pUndo;
for (auto const& it : pLine->GetBoxes())
{
lcl_SetAFormatBox(*it, &aPara, bResetDirect);
}
pLine->SetUpper( pSaveBox );
}
if( pUndo )
{
GetIDocumentUndoRedo().DoUndo(bUndo);
}
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
return true;
}
/**
* Find out who has the Attributes
*/
bool SwDoc::GetTableAutoFormat( const SwSelBoxes& rBoxes, SwTableAutoFormat& rGet )
{
OSL_ENSURE( !rBoxes.empty(), "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( pTableNd->GetTable().GetTabLines(), &aPara );
}
if( aFndBox.GetLines().empty() )
return false;
// Store table properties
SwTable &table = pTableNd->GetTable();
rGet.StoreTableProperties(table);
FndBox_* pFndBox = &aFndBox;
while( 1 == pFndBox->GetLines().size() &&
1 == pFndBox->GetLines().front()->GetBoxes().size())
{
pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
}
if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
pFndBox = pFndBox->GetUpper()->GetUpper();
FndLines_t& rFLns = pFndBox->GetLines();
sal_uInt16 aLnArr[4];
aLnArr[0] = 0;
aLnArr[1] = 1 < rFLns.size() ? 1 : 0;
aLnArr[2] = 2 < rFLns.size() ? 2 : aLnArr[1];
aLnArr[3] = rFLns.size() - 1;
for( sal_uInt8 nLine = 0; nLine < 4; ++nLine )
{
FndLine_& rLine = *rFLns[ aLnArr[ nLine ] ];
sal_uInt16 aBoxArr[4];
aBoxArr[0] = 0;
aBoxArr[1] = 1 < rLine.GetBoxes().size() ? 1 : 0;
aBoxArr[2] = 2 < rLine.GetBoxes().size() ? 2 : aBoxArr[1];
aBoxArr[3] = rLine.GetBoxes().size() - 1;
for( sal_uInt8 nBox = 0; nBox < 4; ++nBox )
{
SwTableBox* pFBox = rLine.GetBoxes()[ aBoxArr[ nBox ] ]->GetBox();
// Always apply to the first ones
while( !pFBox->GetSttNd() )
pFBox = pFBox->GetTabLines()[0]->GetTabBoxes()[0];
sal_uInt8 nPos = nLine * 4 + nBox;
SwNodeIndex aIdx( *pFBox->GetSttNd(), 1 );
SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
if( !pCNd )
pCNd = GetNodes().GoNext( &aIdx );
if( pCNd )
rGet.UpdateFromSet( nPos, pCNd->GetSwAttrSet(),
SwTableAutoFormat::UPDATE_CHAR, nullptr );
rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
SwTableAutoFormat::UPDATE_BOX,
GetNumberFormatter() );
}
}
return true;
}
SwTableAutoFormatTable& SwDoc::GetTableStyles()
{
if (!m_pTableStyles)
{
m_pTableStyles.reset(new SwTableAutoFormatTable);
m_pTableStyles->Load();
}
return *m_pTableStyles.get();
}
OUString SwDoc::GetUniqueTableName() const
{
if( IsInMailMerge())
{
OUString newName = "MailMergeTable"
+ OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ OUString::number( mpTableFrameFormatTable->size() + 1 );
return newName;
}
const OUString aName(SwResId(STR_TABLE_DEFNAME));
const size_t nFlagSize = ( mpTableFrameFormatTable->size() / 8 ) + 2;
std::unique_ptr<sal_uInt8[]> pSetFlags( new sal_uInt8[ nFlagSize ] );
memset( pSetFlags.get(), 0, nFlagSize );
for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
{
const SwFrameFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
pFormat->GetName().startsWith( aName ) )
{
// Get number and set the Flag
const sal_Int32 nNmLen = aName.getLength();
size_t nNum = pFormat->GetName().copy( nNmLen ).toInt32();
if( nNum-- && nNum < mpTableFrameFormatTable->size() )
pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
}
}
// All numbers are flagged properly, thus calculate the right number
size_t nNum = mpTableFrameFormatTable->size();
for( size_t n = 0; n < nFlagSize; ++n )
{
auto nTmp = pSetFlags[ n ];
if( nTmp != 0xFF )
{
// Calculate the number
nNum = n * 8;
while( nTmp & 1 )
{
++nNum;
nTmp >>= 1;
}
break;
}
}
return aName + OUString::number( ++nNum );
}
SwTableFormat* SwDoc::FindTableFormatByName( const OUString& rName, bool bAll ) const
{
const SwFormat* pRet = nullptr;
if( bAll )
pRet = FindFormatByName( *mpTableFrameFormatTable, rName );
else
{
// Only the ones set in the Doc
for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
{
const SwFrameFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
pFormat->GetName() == rName )
{
pRet = pFormat;
break;
}
}
}
return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
}
bool SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
SwTwips nAbsDiff, SwTwips nRelDiff )
{
SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
SwUndo* pUndo = nullptr;
if( (TableChgWidthHeightType::InsertDeleteMode & eType) && dynamic_cast<const SwDDETable*>( &pTableNd->GetTable()) != nullptr)
return false;
SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
aMsgHint.m_eFlags = TBL_BOXPTR;
getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
bool bRet = false;
switch( extractPosition(eType) )
{
case TableChgWidthHeightType::ColLeft:
case TableChgWidthHeightType::ColRight:
case TableChgWidthHeightType::CellLeft:
case TableChgWidthHeightType::CellRight:
{
bRet = pTableNd->GetTable().SetColWidth( rCurrentBox,
eType, nAbsDiff, nRelDiff,
bUndo ? &pUndo : nullptr );
}
break;
case TableChgWidthHeightType::RowBottom:
case TableChgWidthHeightType::CellTop:
case TableChgWidthHeightType::CellBottom:
bRet = pTableNd->GetTable().SetRowHeight( rCurrentBox,
eType, nAbsDiff, nRelDiff,
bUndo ? &pUndo : nullptr );
break;
default: break;
}
GetIDocumentUndoRedo().DoUndo(bUndo); // SetColWidth can turn it off
if( pUndo )
{
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
if( bRet )
{
getIDocumentState().SetModified();
if( TableChgWidthHeightType::InsertDeleteMode & eType )
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
return bRet;
}
bool SwDoc::IsNumberFormat( const OUString& rString, sal_uInt32& F_Index, double& fOutNumber )
{
if( rString.getLength() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
return false;
// remove any comment anchor marks
OUStringBuffer sStringBuffer(rString);
sal_Int32 nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD );
while( nCommentPosition != -1 )
{
sStringBuffer.remove( nCommentPosition, 1 );
nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD, nCommentPosition );
}
return GetNumberFormatter()->IsNumberFormat( sStringBuffer.makeStringAndClear(), F_Index, fOutNumber );
}
void SwDoc::ChkBoxNumFormat( SwTableBox& rBox, bool bCallUpdate )
{
// Optimization: If the Box says it's Text, it remains Text
const SfxPoolItem* pNumFormatItem = nullptr;
if( SfxItemState::SET == rBox.GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT,
false, &pNumFormatItem ) && GetNumberFormatter()->IsTextFormat(
static_cast<const SwTableBoxNumFormat*>(pNumFormatItem)->GetValue() ))
return ;
SwUndoTableNumFormat* pUndo = nullptr;
bool bIsEmptyTextNd;
bool bChgd = true;
sal_uInt32 nFormatIdx;
double fNumber;
if( rBox.HasNumContent( fNumber, nFormatIdx, bIsEmptyTextNd ) )
{
if( !rBox.IsNumberChanged() )
bChgd = false;
else
{
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
pUndo = new SwUndoTableNumFormat( rBox );
pUndo->SetNumFormat( nFormatIdx, fNumber );
}
SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
SfxItemSet aBoxSet( GetAttrPool(), svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} );
bool bLockModify = true;
bool bSetNumberFormat = IsInsTableFormatNum();
const bool bForceNumberFormat = IsInsTableFormatNum() && IsInsTableChangeNumFormat();
// if the user forced a number format in this cell previously,
// keep it, unless the user set that she wants the full number
// format recognition
if( pNumFormatItem && !bForceNumberFormat )
{
sal_uLong nOldNumFormat = static_cast<const SwTableBoxNumFormat*>(pNumFormatItem)->GetValue();
SvNumberFormatter* pNumFormatr = GetNumberFormatter();
SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIdx );
if( nFormatType == pNumFormatr->GetType( nOldNumFormat ) || SvNumFormatType::NUMBER == nFormatType )
{
// Current and specified NumFormat match
// -> keep old Format
nFormatIdx = nOldNumFormat;
bSetNumberFormat = true;
}
else
{
// Current and specified NumFormat do not match
// -> insert as Text
bLockModify = bSetNumberFormat = false;
}
}
if( bSetNumberFormat || bForceNumberFormat )
{
pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
aBoxSet.Put( SwTableBoxValue( fNumber ));
aBoxSet.Put( SwTableBoxNumFormat( nFormatIdx ));
}
// It's not enough to only reset the Formula.
// Make sure that the Text is formatted accordingly
if( !bSetNumberFormat && !bIsEmptyTextNd && pNumFormatItem )
{
// Just resetting Attributes is not enough
// Make sure that the Text is formatted accordingly
pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
}
if( bLockModify ) pBoxFormat->LockModify();
pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
if( bLockModify ) pBoxFormat->UnlockModify();
if( bSetNumberFormat )
pBoxFormat->SetFormatAttr( aBoxSet );
}
}
else
{
// It's not a number
const SfxPoolItem* pValueItem = nullptr, *pFormatItem = nullptr;
SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT,
false, &pFormatItem ) ||
SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE,
false, &pValueItem ))
{
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
pUndo = new SwUndoTableNumFormat( rBox );
}
pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
// Remove all number formats
sal_uInt16 nWhich1 = RES_BOXATR_FORMULA;
if( !bIsEmptyTextNd )
{
nWhich1 = RES_BOXATR_FORMAT;
// Just resetting Attributes is not enough
// Make sure that the Text is formatted accordingly
pBoxFormat->SetFormatAttr( *GetDfltAttr( nWhich1 ));
}
pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
}
else
bChgd = false;
}
if( bChgd )
{
if( pUndo )
{
pUndo->SetBox( rBox );
GetIDocumentUndoRedo().AppendUndo(pUndo);
GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
}
const SwTableNode* pTableNd = rBox.GetSttNd()->FindTableNode();
if( bCallUpdate )
{
SwTableFormulaUpdate aTableUpdate( &pTableNd->GetTable() );
getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
// TL_CHART2: update charts (when cursor leaves cell and
// automatic update is enabled)
if (AUTOUPD_FIELD_AND_CHARTS == GetDocumentSettingManager().getFieldUpdateFlags(true))
pTableNd->GetTable().UpdateCharts();
}
getIDocumentState().SetModified();
}
}
void SwDoc::SetTableBoxFormulaAttrs( SwTableBox& rBox, const SfxItemSet& rSet )
{
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().AppendUndo( new SwUndoTableNumFormat(rBox, &rSet) );
}
SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
{
pBoxFormat->LockModify();
pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
pBoxFormat->UnlockModify();
}
else if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE ))
{
pBoxFormat->LockModify();
pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
pBoxFormat->UnlockModify();
}
pBoxFormat->SetFormatAttr( rSet );
getIDocumentState().SetModified();
}
void SwDoc::ClearLineNumAttrs( SwPosition const & rPos )
{
SwPaM aPam(rPos);
aPam.Move(fnMoveBackward);
SwContentNode *pNode = aPam.GetContentNode();
if ( nullptr == pNode )
return ;
if( pNode->IsTextNode() )
{
SwTextNode * pTextNode = pNode->GetTextNode();
if (pTextNode && pTextNode->IsNumbered()
&& pTextNode->GetText().isEmpty())
{
const SfxPoolItem* pFormatItem = nullptr;
SfxItemSet rSet( pTextNode->GetDoc()->GetAttrPool(),
svl::Items<RES_PARATR_BEGIN, RES_PARATR_END - 1>{});
pTextNode->SwContentNode::GetAttr( rSet );
if ( SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE , false , &pFormatItem ) )
{
SwUndoDelNum * pUndo;
if( GetIDocumentUndoRedo().DoesUndo() )
{
GetIDocumentUndoRedo().ClearRedo();
GetIDocumentUndoRedo().AppendUndo( pUndo = new SwUndoDelNum( aPam ) );
}
else
pUndo = nullptr;
SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
aRegH.RegisterInModify( pTextNode , *pTextNode );
if ( pUndo )
pUndo->AddNode( *pTextNode );
SfxStringItem * pNewItem = static_cast<SfxStringItem*>(pFormatItem->Clone());
pNewItem->SetValue(OUString());
rSet.Put( *pNewItem );
pTextNode->SetAttr( rSet );
delete pNewItem;
}
}
}
}
void SwDoc::ClearBoxNumAttrs( const SwNodeIndex& rNode )
{
SwStartNode* pSttNd;
if( nullptr != ( pSttNd = rNode.GetNode().
FindSttNodeByType( SwTableBoxStartNode )) &&
2 == pSttNd->EndOfSectionIndex() - pSttNd->GetIndex() )
{
SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
GetTableBox( pSttNd->GetIndex() );
const SfxPoolItem* pFormatItem = nullptr;
const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMAT, false, &pFormatItem ) ||
SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
{
if (GetIDocumentUndoRedo().DoesUndo())
{
GetIDocumentUndoRedo().AppendUndo(new SwUndoTableNumFormat(*pBox));
}
SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
// Keep TextFormats!
sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
if( pFormatItem && GetNumberFormatter()->IsTextFormat(
static_cast<const SwTableBoxNumFormat*>(pFormatItem)->GetValue() ))
nWhich1 = RES_BOXATR_FORMULA;
else
// Just resetting Attributes is not enough
// Make sure that the Text is formatted accordingly
pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
getIDocumentState().SetModified();
}
}
}
/**
* Copies a Table from the same or another Doc into itself
* We create a new Table or an existing one is filled with the Content.
* We either fill in the Content from a certain Box or a certain TableSelection
*
* This method is called by edglss.cxx/fecopy.cxx
*/
bool SwDoc::InsCopyOfTable( SwPosition& rInsPos, const SwSelBoxes& rBoxes,
const SwTable* pCpyTable, bool bCpyName, bool bCorrPos )
{
bool bRet;
const SwTableNode* pSrcTableNd = pCpyTable
? pCpyTable->GetTableNode()
: rBoxes[ 0 ]->GetSttNd()->FindTableNode();
SwTableNode * pInsTableNd = rInsPos.nNode.GetNode().FindTableNode();
bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
if( !pCpyTable && !pInsTableNd )
{
SwUndoCpyTable* pUndo = nullptr;
if (bUndo)
{
GetIDocumentUndoRedo().ClearRedo();
pUndo = new SwUndoCpyTable(this);
}
{
::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
bRet = pSrcTableNd->GetTable().MakeCopy( this, rInsPos, rBoxes,
bCpyName );
}
if( pUndo )
{
if( !bRet )
{
delete pUndo;
pUndo = nullptr;
}
else
{
pInsTableNd = GetNodes()[ rInsPos.nNode.GetIndex() - 1 ]->FindTableNode();
pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
}
}
else
{
RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
if( getIDocumentRedlineAccess().IsRedlineOn() )
getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
RedlineFlags::ShowInsert |
RedlineFlags::ShowDelete );
SwUndoTableCpyTable* pUndo = nullptr;
if (bUndo)
{
GetIDocumentUndoRedo().ClearRedo();
pUndo = new SwUndoTableCpyTable(this);
GetIDocumentUndoRedo().DoUndo(false);
}
rtl::Reference<SwDoc> xCpyDoc( const_cast<SwDoc*>(pSrcTableNd->GetDoc()) );
bool bDelCpyDoc = xCpyDoc == this;
if( bDelCpyDoc )
{
// Copy the Table into a temporary Doc
xCpyDoc = new SwDoc;
SwPosition aPos( SwNodeIndex( xCpyDoc->GetNodes().GetEndOfContent() ));
if( !pSrcTableNd->GetTable().MakeCopy( xCpyDoc.get(), aPos, rBoxes, true ))
{
xCpyDoc.clear();
if( pUndo )
{
GetIDocumentUndoRedo().DoUndo(bUndo);
delete pUndo;
pUndo = nullptr;
}
return false;
}
aPos.nNode -= 1; // Set to the Table's EndNode
pSrcTableNd = aPos.nNode.GetNode().FindTableNode();
}
const SwStartNode* pSttNd = rInsPos.nNode.GetNode().FindTableBoxStartNode();
rInsPos.nContent.Assign( nullptr, 0 );
// no complex into complex, but copy into or from new model is welcome
if( ( !pSrcTableNd->GetTable().IsTableComplex() || pInsTableNd->GetTable().IsNewModel() )
&& ( bDelCpyDoc || !rBoxes.empty() ) )
{
// Copy the Table "relatively"
const SwSelBoxes* pBoxes;
SwSelBoxes aBoxes;
if( bDelCpyDoc )
{
SwTableBox* pBox = pInsTableNd->GetTable().GetTableBox(
pSttNd->GetIndex() );
OSL_ENSURE( pBox, "Box is not in this Table" );
aBoxes.insert( pBox );
pBoxes = &aBoxes;
}
else
pBoxes = &rBoxes;
// Copy Table to the selected Lines
bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
*pBoxes, pUndo );
}
else
{
SwNodeIndex aNdIdx( *pSttNd, 1 );
bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
aNdIdx, pUndo );
}
xCpyDoc.clear();
if( pUndo )
{
// If the Table could not be copied, delete the Undo object
GetIDocumentUndoRedo().DoUndo(bUndo);
if( !bRet && pUndo->IsEmpty() )
delete pUndo;
else
{
GetIDocumentUndoRedo().AppendUndo(pUndo);
}
}
if( bCorrPos )
{
rInsPos.nNode = *pSttNd;
rInsPos.nContent.Assign( GetNodes().GoNext( &rInsPos.nNode ), 0 );
}
getIDocumentRedlineAccess().SetRedlineFlags( eOld );
}
if( bRet )
{
getIDocumentState().SetModified();
getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, 0 );
}
return bRet;
}
bool SwDoc::UnProtectTableCells( SwTable& rTable )
{
bool bChgd = false;
SwUndoAttrTable *const pUndo = (GetIDocumentUndoRedo().DoesUndo())
? new SwUndoAttrTable( *rTable.GetTableNode() )
: nullptr;
SwTableSortBoxes& rSrtBox = rTable.GetTabSortBoxes();
for (size_t i = rSrtBox.size(); i; )
{
SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
if( pBoxFormat->GetProtect().IsContentProtected() )
{
pBoxFormat->ResetFormatAttr( RES_PROTECT );
bChgd = true;
}
}
if( pUndo )
{
if( bChgd )
{
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
return bChgd;
}
void SwDoc::UnProtectCells( const OUString& rName )
{
bool bChgd = false;
SwTableFormat* pFormat = FindTableFormatByName( rName );
if( pFormat )
{
bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
if( bChgd )
getIDocumentState().SetModified();
}
}
bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
{
bool bChgd = false;
if( !rBoxes.empty() )
{
SwUndoAttrTable *const pUndo = (GetIDocumentUndoRedo().DoesUndo())
? new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() )
: nullptr;
std::map<SwFrameFormat*, SwTableBoxFormat*> aFormatsMap;
for (size_t i = rBoxes.size(); i; )
{
SwTableBox* pBox = rBoxes[ --i ];
SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
if( pBoxFormat->GetProtect().IsContentProtected() )
{
std::map<SwFrameFormat*, SwTableBoxFormat*>::const_iterator const it =
aFormatsMap.find(pBoxFormat);
if (aFormatsMap.end() != it)
pBox->ChgFrameFormat(it->second);
else
{
SwTableBoxFormat *const pNewBoxFormat(
static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()));
pNewBoxFormat->ResetFormatAttr( RES_PROTECT );
aFormatsMap.insert(std::make_pair(pBoxFormat, pNewBoxFormat));
}
bChgd = true;
}
}
if( pUndo )
{
if( bChgd )
{
GetIDocumentUndoRedo().AppendUndo( pUndo );
}
else
delete pUndo;
}
}
return bChgd;
}
void SwDoc::UnProtectTables( const SwPaM& rPam )
{
GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
bool bChgd = false, bHasSel = rPam.HasMark() ||
rPam.GetNext() != &rPam;
SwFrameFormats& rFormats = *GetTableFrameFormats();
SwTable* pTable;
const SwTableNode* pTableNd;
for( auto n = rFormats.size(); n ; )
if( nullptr != (pTable = SwTable::FindTable( rFormats[ --n ] )) &&
nullptr != (pTableNd = pTable->GetTableNode() ) &&
pTableNd->GetNodes().IsDocNodes() )
{
sal_uLong nTableIdx = pTableNd->GetIndex();
// Check whether the Table is within the Selection
if( bHasSel )
{
bool bFound = false;
SwPaM* pTmp = const_cast<SwPaM*>(&rPam);
do {
const SwPosition *pStt = pTmp->Start(),
*pEnd = pTmp->End();
bFound = pStt->nNode.GetIndex() < nTableIdx &&
nTableIdx < pEnd->nNode.GetIndex();
} while( !bFound && &rPam != ( pTmp = pTmp->GetNext() ) );
if( !bFound )
continue; // Continue searching
}
// Lift the protection
bChgd |= UnProtectTableCells( *pTable );
}
GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
if( bChgd )
getIDocumentState().SetModified();
}
bool SwDoc::HasTableAnyProtection( const SwPosition* pPos,
const OUString* pTableName,
bool* pFullTableProtection )
{
bool bHasProtection = false;
SwTable* pTable = nullptr;
if( pTableName )
pTable = SwTable::FindTable( FindTableFormatByName( *pTableName ) );
else if( pPos )
{
SwTableNode* pTableNd = pPos->nNode.GetNode().FindTableNode();
if( pTableNd )
pTable = &pTableNd->GetTable();
}
if( pTable )
{
SwTableSortBoxes& rSrtBox = pTable->GetTabSortBoxes();
for (size_t i = rSrtBox.size(); i; )
{
SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
if( pBoxFormat->GetProtect().IsContentProtected() )
{
if( !bHasProtection )
{
bHasProtection = true;
if( !pFullTableProtection )
break;
*pFullTableProtection = true;
}
}
else if( bHasProtection && pFullTableProtection )
{
*pFullTableProtection = false;
break;
}
}
}
return bHasProtection;
}
SwTableAutoFormat* SwDoc::MakeTableStyle(const OUString& rName, bool bBroadcast)
{
SwTableAutoFormat aTableFormat(rName);
GetTableStyles().AddAutoFormat(aTableFormat);
SwTableAutoFormat* pTableFormat = GetTableStyles().FindAutoFormat(rName);
getIDocumentState().SetModified();
if (GetIDocumentUndoRedo().DoesUndo())
{
SwUndo * pUndo = new SwUndoTableStyleMake(rName, this);
GetIDocumentUndoRedo().AppendUndo(pUndo);
}
if (bBroadcast)
BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetCreated);
return pTableFormat;
}
std::unique_ptr<SwTableAutoFormat> SwDoc::DelTableStyle(const OUString& rName, bool bBroadcast)
{
if (bBroadcast)
BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetErased);
std::unique_ptr<SwTableAutoFormat> pReleasedFormat = GetTableStyles().ReleaseAutoFormat(rName);
std::vector<SwTable*> vAffectedTables;
if (pReleasedFormat.get())
{
size_t nTableCount = GetTableFrameFormatCount(true);
for (size_t i=0; i < nTableCount; ++i)
{
SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
SwTable* pTable = SwTable::FindTable(pFrameFormat);
if (pTable->GetTableStyleName() == pReleasedFormat->GetName())
{
pTable->SetTableStyleName("");
vAffectedTables.push_back(pTable);
}
}
getIDocumentState().SetModified();
if (GetIDocumentUndoRedo().DoesUndo())
{
SwUndo * pUndo = new SwUndoTableStyleDelete(std::move(pReleasedFormat), vAffectedTables, this);
GetIDocumentUndoRedo().AppendUndo(pUndo);
}
}
return pReleasedFormat;
}
void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
{
SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
if (pFormat)
{
SwTableAutoFormat aOldFormat = *pFormat;
*pFormat = rNewFormat;
pFormat->SetName(rName);
size_t nTableCount = GetTableFrameFormatCount(true);
for (size_t i=0; i < nTableCount; ++i)
{
SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
SwTable* pTable = SwTable::FindTable(pFrameFormat);
if (pTable->GetTableStyleName() == rName)
GetDocShell()->GetFEShell()->UpdateTableStyleFormatting(pTable->GetTableNode());
}
getIDocumentState().SetModified();
if (GetIDocumentUndoRedo().DoesUndo())
{
SwUndo * pUndo = new SwUndoTableStyleUpdate(*pFormat, aOldFormat, this);
GetIDocumentUndoRedo().AppendUndo(pUndo);
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V595 The 'pContentNd' pointer was utilized before it was verified against nullptr. Check lines: 393, 401.
↑ V506 Pointer to local variable 'sNewTableNm' is stored outside the scope of this variable. Such a pointer will become invalid.
↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.