summaryrefslogtreecommitdiffstats
path: root/sw/source/core/docnode/ndtbl.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/docnode/ndtbl.cxx')
-rw-r--r--sw/source/core/docnode/ndtbl.cxx4684
1 files changed, 4684 insertions, 0 deletions
diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx
new file mode 100644
index 000000000..e76a0bbf1
--- /dev/null
+++ b/sw/source/core/docnode/ndtbl.cxx
@@ -0,0 +1,4684 @@
+/* -*- 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 <libxml/xmlwriter.h>
+
+#include <config_wasm_strip.h>
+
+#include <memory>
+#include <fesh.hxx>
+#include <hintids.hxx>
+#include <editeng/lrspitem.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 <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 <swtable.hxx>
+#include <swundo.hxx>
+#include <tblsel.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 <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 <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 <fntcache.hxx>
+#include <frameformats.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/numformat.hxx>
+#include <tools/datetimeutils.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#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 );
+ // Default border in Writer: 0.5pt (matching Word)
+ SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin );
+ if ( bHTML )
+ {
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ 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, false, false,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::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( SwFrameSize::Variable,
+ 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,
+ const sal_uInt16 nRows, const sal_uInt16 nCols, sal_uInt8 nId )
+{
+ if( !rBoxFormatArr[nId] )
+ {
+ SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
+ rAutoFormat.UpdateToSet( nId, nRows==1, nCols==1,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::Box,
+ rDoc.GetNumberFormatter( ) );
+ if( USHRT_MAX != nCols )
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ USHRT_MAX / nCols, 0 ));
+ rBoxFormatArr[ nId ] = pBoxFormat;
+ }
+ return rBoxFormatArr[nId];
+}
+
+SwTableNode* SwDoc::IsIdxInTable(const SwNodeIndex& rIdx)
+{
+ SwTableNode* pTableNd = nullptr;
+ SwNodeOffset nIndex = rIdx.GetIndex();
+ do {
+ SwNode* pNd = GetNodes()[ nIndex ]->StartOfSectionNode();
+ pTableNd = pNd->GetTableNode();
+ if( nullptr != pTableNd )
+ 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
+ SwNodeOffset nIdxPos(0);
+ SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ if( nInsPos < pLine->GetTabBoxes().size() )
+ {
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes()[ nInsPos ] );
+ if( nullptr == pPrvBox )
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes().back() );
+ if( nullptr == pNxtBox )
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ }
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ if( nullptr == pNxtBox )
+ 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(
+ std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<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();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ 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( SwFrameSize::Variable, 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();
+ if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ pNextNd->ResetAttr( RES_PAGEDESC );
+ pNdSet = pNextNd->GetpSwAttrSet();
+ }
+ const SvxFormatBreakItem* pItem;
+ if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
+ {
+ 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( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
+ }
+ else
+ {
+ const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
+ aBoxFormatArr.resize( nBoxArrLen, nullptr );
+ }
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ 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,
+ nRows, nCols, nId );
+
+ // Set the Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::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( SwFrameSize::Variable, nWidth ));
+ }
+ }
+
+ SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
+ rBoxes.insert( rBoxes.begin() + i, pBox );
+ aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
+ }
+ }
+ // Insert Frames
+ GetNodes().GoNext( &aNdIdx ); // Go to the next ContentNode
+ pTableNd->MakeOwnFrames( &aNdIdx );
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, SwNodeOffset(1) );
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::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();
+ {
+ SwNodeOffset 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,
+ o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(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
+ SwNode2LayoutSaveUpperFrames 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( SwFrameSize::Variable, 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();
+ if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
+ {
+ 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::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
+ if( bUseBoxFormat )
+ {
+ aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
+ }
+ else
+ {
+ aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
+ }
+
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ 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, USHRT_MAX, nId );
+ }
+
+ // Set Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ if( aCharSet.Count() )
+ {
+ SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
+ SwNodeOffset 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);
+ }
+ }
+
+ SwNodeOffset 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, SwNodeOffset(0));
+ return &rNdTable;
+}
+
+static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
+{
+ // delete old layout frames, new ones need to be created...
+ rNode.DelFrames(nullptr);
+
+ if (!rNode.IsTextNode())
+ {
+ return;
+ }
+
+ SwTextNode & rTextNode = *rNode.GetTextNode();
+ // remove PageBreaks/PageDesc/ColBreak
+ SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
+ if (!pSet)
+ return;
+
+ if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pItem);
+ }
+ rTextNode.ResetAttr(RES_BREAK);
+ pSet = rTextNode.GetpSwAttrSet();
+ }
+
+ const SwFormatPageDesc* pPageDescItem;
+ if (pSet
+ && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
+ && pPageDescItem->GetPageDesc())
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pPageDescItem);
+ }
+ 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(SwFrameSize::Variable, (*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( SwFrameSize::Variable, nLastPos ));
+ }
+ else
+ {
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, 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& rDoc = 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 += SwNodeOffset(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( o3tl::narrowing<sal_uInt16>(
+ aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
+ }
+ }
+
+ aPosArr.push_back(
+ o3tl::narrowing<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(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);
+
+ if( T2T_PARA != cCh )
+ {
+ for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
+ {
+ if (pTextNd->GetText()[nChPos] == cCh)
+ {
+ aCntPos.nContent = nChPos;
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
+ }
+ });
+ SwContentNode *const pNewNd =
+ pTextNd->SplitContentNode(aCntPos, &restoreFunc);
+
+ // 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, rDoc, &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 (const auto& rRow : rTableNodes)
+ {
+ for (const auto& rCell : rRow)
+ {
+ assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
+ SwPaM pam(rCell.aStart, 0, *pPrev,
+ (pPrev->GetNode().IsContentNode())
+ ? pPrev->GetNode().GetContentNode()->Len() : 0);
+ rIDRA.SplitRedline(pam);
+ pPrev = &rCell.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
+ SwNode2LayoutSaveUpperFrames 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( SwFrameSize::Variable, 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();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ 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;
+ }
+
+ SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ return &rNdTable;
+}
+
+std::unique_ptr<SwNodeRange> SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange)
+{
+ 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();
+ }
+
+ std::unique_ptr<SwNodeRange> pResult;
+ if (bChanged)
+ pResult.reset(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(SwFrameSize::Variable,
+ 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(SwFrameSize::Variable, 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& rDoc = GetDoc();
+ SwTable& rTable = pTableNd->GetTable();
+ SwTableBox* pBox;
+ sal_uInt16 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);
+ }
+ }
+
+ nLines = 0;
+ for( const auto& rRow : rTableNodes )
+ {
+ sal_uInt16 nBoxes = 0;
+ SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
+ rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
+
+ for( const auto& rCell : rRow )
+ {
+ const SwNodeIndex aTmpIdx( rCell.aStart,0 );
+
+ SwNodeIndex aCellEndIdx(rCell.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 = rCell.aStart;
+ for(;aCellNodeIdx <= rCell.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;
+
+ nLines++;
+ }
+
+ lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);
+
+ 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, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
+ std::unique_ptr<SwUndoTableToText> pUndo;
+ SwNodeRange* pUndoRg = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
+ pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
+ }
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
+ if( pUndoRg )
+ {
+ ++pUndoRg->aStart;
+ --pUndoRg->aEnd;
+ pUndo->SetRange( *pUndoRg );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ delete pUndoRg;
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+
+ return bRet;
+}
+
+namespace {
+
+/**
+ * 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( auto& rpBox : pLine->GetTabBoxes() )
+ lcl_DelBox(rpBox, &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& rDoc = pDelPara->rNds.GetDoc();
+ SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(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
+ SwNodeOffset 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( rDoc, 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(rDoc, nNdIdx, SAL_MAX_INT32);
+
+ pDelPara->pLastNd->JoinNext();
+
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
+ }
+ else if( pDelPara->pUndo )
+ {
+ ++aDelRg.aStart;
+ pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
+ }
+ }
+ else if( pDelPara->pUndo )
+ pDelPara->pUndo->AddBoxPos( rDoc, 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
+ SwNode2LayoutSaveUpperFrames * pNode2Layout = nullptr;
+ SwNodeIndex aFrameIdx( rRange.aStart );
+ SwNode* pFrameNd = FindPrvNxtFrameNode( aFrameIdx, &rRange.aEnd.GetNode() );
+ if( !pFrameNd )
+ // Collect all Uppers
+ pNode2Layout = new SwNode2LayoutSaveUpperFrames(*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 SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
+ const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );
+
+ 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#
+ SwNodeOffset 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 )
+ {
+ pCNd = aDelRg.aStart.GetNode().GetContentNode();
+ if( nullptr != pCNd )
+ {
+ if( pFrameNd->IsContentNode() )
+ static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
+ else if( pFrameNd->IsTableNode() )
+ static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ else if( pFrameNd->IsSectionNode() )
+ static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ pFrameNd = pCNd;
+ }
+ else
+ {
+ pSNd = aDelRg.aStart.GetNode().GetSectionNode();
+ if( pSNd )
+ {
+ if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
+ {
+ pSNd->MakeOwnFrames(&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;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(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, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(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;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(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, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(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;
+
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ 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 );
+ }
+
+ SwNodeOffset 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.
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ const SwNode* pNd = rCursor.GetNode().FindTableBoxStartNode();
+ pESh->ParkCursor( SwNodeIndex( *pNd ) );
+ }
+
+ // Thus delete the Columns
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
+ DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
+}
+
+bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode)
+{
+ if (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && ::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 (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr)
+ {
+ return false;
+ }
+
+ ::ClearFEShellTabCols(*this, nullptr);
+ SwSelBoxes aSelBoxes( rBoxes );
+ SwTable &rTable = pTableNd->GetTable();
+ tools::Long nMin = 0;
+ tools::Long nMax = 0;
+ if( rTable.IsNewModel() )
+ {
+ if (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
+ else
+ rTable.FindSuperfluousRows( aSelBoxes );
+ }
+
+ // Are we deleting the whole Table?
+ const SwNodeOffset nTmpIdx1 = pTableNd->GetIndex();
+ const SwNodeOffset 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 SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex() + 1;
+ const SwNodeOffset 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;
+ SwNodeOffset 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;
+ }
+ }
+ std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default));
+ if( bNewTextNd )
+ pUndo->SetTableDelLastNd();
+ pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
+ pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
+ GetIDocumentUndoRedo().AppendUndo( std::move(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 );
+ }
+
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ return true;
+ }
+
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(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 (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.PrepareDeleteCol( nMin, nMax );
+ rTable.FindSuperfluousRows( aSelBoxes );
+ if (pUndo)
+ pUndo->ReNewBoxes( aSelBoxes );
+ }
+ bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true );
+ if (bRet)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(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<SwNodeOffset> aNdsCnts;
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(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)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ if( bVert )
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ else
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
+ GetIDocumentUndoRedo().AppendUndo( std::move(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);
+
+ std::unique_ptr<SwUndoTableMerge> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoTableMerge( rPam ));
+
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ SwSelBoxes aMerged;
+ SwTableBox* pMergeBox;
+
+ if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo.get() ) )
+ { // No cells found to merge
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( pUndo )
+ {
+ pUndo.reset();
+ 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();
+
+ if (SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(&rPam))
+ {
+ // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
+ // to not contain the soon to-be-deleted SwTableBox so if the rPam
+ // is queried via a11y it doesn't claim the deleted cell still
+ // exists
+ pTableCursor->NewTableSelection();
+ }
+ }
+
+ // Merge them
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo.get() ))
+ {
+ nRet = TableMergeErr::Ok;
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(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
+ GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
+ 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::MakeFramesForAdjacentContentNode(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()) )
+ {
+ if ( ( pFrame->getRootFrame()->HasMergedParas() &&
+ !pNode->IsCreateFrameWhenHidingRedlines() ) ||
+ // tdf#153819 table deletion with change tracking:
+ // table node without frames in Hide Changes mode
+ !pFrame->GetUpper() )
+ {
+ continue;
+ }
+ 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::MakeOwnFrames(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 )) )
+ {
+ if (pUpper->getRootFrame()->HasMergedParas()
+ && !IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ 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.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pNew->RegistFlys();
+ }
+}
+
+void SwTableNode::DelFrames(SwRootFrame const*const pLayout)
+{
+ /* 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() && (!pLayout || pLayout == pFrame->getRootFrame()))
+ {
+ 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.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if (pFrame->GetUpper())
+ 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 );
+ MakeOwnFrames(&aIdx);
+ }
+}
+
+void SwTableNode::RemoveRedlines()
+{
+ SwDoc& rDoc = GetDoc();
+ SwTable& rTable = GetTable();
+ rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc, rTable, true, RedlineType::Any);
+}
+
+void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableNode"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (m_pTable)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", m_pTable.get());
+ m_pTable->GetFrameFormat()->dumpAsXml(pWriter);
+ for (const auto& pLine : m_pTable->GetTabLines())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableLine"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pLine);
+ pLine->GetFrameFormat()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+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
+
+namespace {
+
+struct FuzzyCompare
+{
+ bool operator() ( tools::Long s1, tools::Long s2 ) const;
+};
+
+}
+
+bool FuzzyCompare::operator() ( tools::Long s1, tools::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 tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
+ pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
+ pTab->GetPrtTop() - pPage->getFrameArea().Top() );
+ const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
+ const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
+ const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
+
+ rFill.SetLeftMin( nLeftMin );
+ rFill.SetLeft( nLeft );
+ rFill.SetRight( nRight );
+ rFill.SetRightMax( nRightMax );
+
+ typedef std::map< tools::Long, std::pair< tools::Long, long >, FuzzyCompare > BoundaryMap;
+ BoundaryMap aBoundaries;
+ BoundaryMap::iterator aIter;
+ std::pair< tools::Long, long > aPair;
+
+ typedef std::map< tools::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:
+ tools::Long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
+ tools::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;
+ tools::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:
+ tools::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 ( const auto& rEntry : aBoundaries )
+ {
+ const tools::Long nTabTop = aRectFnSet.GetPrtTop(*pTab);
+ const tools::Long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop );
+ const std::pair< tools::Long, long > aTmpPair = rEntry.second;
+ const tools::Long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
+ const tools::Long nSecond = aTmpPair.second;
+
+ aHiddenIter = aHidden.find( rEntry.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()) );
+ tools::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 tools::Long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
+ const tools::Long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
+ const tools::Long nOldRowHeight = nOldRowEnd - nOldRowStart;
+
+ const tools::Long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
+ const tools::Long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
+ const tools::Long nNewRowHeight = nNewRowEnd - nNewRowStart;
+
+ const tools::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 tools::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 sal_Int32 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 an overlapping box
+ SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
+ const tools::Long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
+ if( nNewSize != aNew.GetHeight() )
+ {
+ aNew.SetHeight( nNewSize );
+ if ( SwFrameSize::Variable == aNew.GetHeightSizeType() )
+ aNew.SetHeightSizeType( SwFrameSize::Minimum );
+ // 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(
+ std::make_unique<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(
+ std::make_unique<SwUndoTableHeadline>(rTable, rTable.GetRowsToRepeat(), nSet) );
+ }
+
+ rTable.SetRowsToRepeat(nSet);
+ const SwMsgPoolItem aChg(RES_TBLHEADLINECHG);
+ rTable.GetFrameFormat()->CallSwClientNotify(sw::LegacyModifyHint(&aChg, &aChg));
+ getIDocumentState().SetModified();
+}
+
+void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
+{
+ if( m_pHistory )
+ m_pHistory->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text );
+}
+
+void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
+{
+ m_aPositionArr.push_back(m_nWidth);
+ SwTableBox* p = const_cast<SwTableBox*>(&rBox);
+ m_Boxes.push_back(p);
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+}
+
+const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
+{
+ const SwTableBox* pRet = nullptr;
+
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ if( m_aPositionArr[ n ] == m_nWidth )
+ break;
+ else if( m_aPositionArr[ n ] > m_nWidth )
+ {
+ if( n )
+ --n;
+ break;
+ }
+
+ if( n >= m_aPositionArr.size() )
+ --n;
+
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+ pRet = m_Boxes[ n ];
+ }
+ return pRet;
+}
+
+bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
+{
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ {
+ if( m_aPositionArr[ n ] == nOffset )
+ break;
+ else if( m_aPositionArr[ n ] > nOffset )
+ {
+ if( n )
+ --n;
+ break;
+ }
+ }
+
+ m_aPositionArr.erase( m_aPositionArr.begin(), m_aPositionArr.begin() + n );
+ m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
+
+ size_t nArrSize = m_aPositionArr.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 = m_nWidth;
+ nSize *= ( m_aPositionArr[ n ] - nOffset );
+ nSize /= nOldWidth;
+ m_aPositionArr[ n ] = sal_uInt16( nSize );
+ }
+ }
+ }
+ return !m_aPositionArr.empty();
+}
+
+bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
+{
+ SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
+ if( pSplPara->IsGetValues() )
+ for( const auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, pSplPara );
+ else
+ for( auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, 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
+ {
+ SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BACKGROUND, RES_SHADOW>
+ aTmpSet( pFormat->GetDoc()->GetAttrPool() );
+ 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( SwNodeOffset(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)
+ */
+void SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
+ bool bCalcNewSize )
+{
+ SwNode* pNd = &rPos.nNode.GetNode();
+ SwTableNode* pTNd = pNd->FindTableNode();
+ if( !pTNd || pNd->IsTableNode() )
+ return;
+
+ if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
+ return;
+
+ 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;
+ }
+
+ {
+ SwNodeOffset 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(std::unique_ptr<SwUndo>(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( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &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( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &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->MakeOwnFrames( &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
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ {
+ pFEShell->UpdateTableStyleFormatting(pTNd);
+ pFEShell->UpdateTableStyleFormatting(pNew);
+ }
+
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+}
+
+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;
+}
+
+namespace {
+
+class SplitTable_Para
+{
+ std::map<SwFrameFormat const*, SwFrameFormat*> m_aSrcDestMap;
+ SwTableNode* m_pNewTableNode;
+ SwTable& m_rOldTable;
+
+public:
+ SplitTable_Para(SwTableNode* pNew, SwTable& rOld)
+ : m_pNewTableNode(pNew)
+ , m_rOldTable(rOld)
+ {}
+ SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
+ {
+ auto it = m_aSrcDestMap.find(pSrcFormat);
+ return it == m_aSrcDestMap.end() ? nullptr : it->second;
+ }
+
+ void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
+ {
+ m_aSrcDestMap[pSrcFormat] = pDestFormat;
+ }
+
+ void ChgBox( SwTableBox* pBox )
+ {
+ m_rOldTable.GetTabSortBoxes().erase(pBox);
+ m_pNewTableNode->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( auto& rpBox : pLn->GetTabBoxes() )
+ lcl_SplitTable_CpyBox(rpBox, 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;
+
+ SwNodeOffset 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;
+ std::unique_ptr<SwHistory> pHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev, nMode );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ pHistory.reset(new SwHistory);
+ }
+
+ // Adapt all "TableFormulas"
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_aData.pDelTable = &pDelTableNd->GetTable();
+ aMsgHint.m_eFlags = TBL_MERGETBL;
+ aMsgHint.m_pHistory = pHistory.get();
+ 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 );
+ pHistory.reset();
+ }
+ if( bRet )
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(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 -= SwNodeOffset(2);
+ DelNodes( aIdx, SwNodeOffset(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;
+}
+
+namespace {
+
+// Use the PtrArray's ForEach method
+struct SetAFormatTabPara
+{
+ SwTableAutoFormat& rTableFormat;
+ SwUndoTableAutoFormat* pUndo;
+ sal_uInt16 nEndBox, nCurBox;
+ sal_uInt8 nAFormatLine, nAFormatBox;
+ bool bSingleRowTable;
+
+ explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
+ : rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
+ nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
+ {}
+};
+
+}
+
+// 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 //Even column(1) or Odd column(2)
+ 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();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet(pDoc->GetAttrPool());
+ SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
+ sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
+ const bool bSingleRowTable = pSetPara->bSingleRowTable;
+ const bool bSingleColTable = pSetPara->nEndBox == 0;
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aCharSet, SwTableAutoFormatUpdateFlags::Char, nullptr);
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aBoxSet, SwTableAutoFormatUpdateFlags::Box, pDoc->GetNumberFormatter());
+
+ if (aCharSet.Count())
+ {
+ SwNodeOffset nSttNd = pSetBox->GetSttIdx()+1;
+ SwNodeOffset 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
+ {
+ // Not sure how this situation can occur, but apparently we have some kind of table in table.
+ // I am guessing at how to best handle singlerow in this situation.
+ const bool bOrigSingleRowTable = pSetPara->bSingleRowTable;
+ pSetPara->bSingleRowTable = rBox.GetLines().size() == 1;
+ for (auto const& rpFndLine : rBox.GetLines())
+ {
+ lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
+ }
+ pSetPara->bSingleRowTable = bOrigSingleRowTable;
+ }
+
+ 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(std::unique_ptr<SwUndo>(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();
+ aPara.bSingleRowTable = rFLns.size() == 1;
+
+ 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, SwNodeOffset(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(),
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
+ SwTableAutoFormatUpdateFlags::Box,
+ GetNumberFormatter() );
+ }
+ }
+
+ return true;
+}
+
+SwTableAutoFormatTable& SwDoc::GetTableStyles()
+{
+ if (!m_pTableStyles)
+ {
+ m_pTableStyles.reset(new SwTableAutoFormatTable);
+ m_pTableStyles->Load();
+ }
+ return *m_pTableStyles;
+}
+
+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 = o3tl::toInt32(pFormat->GetName().subView( nNmLen ));
+ 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 = mpTableFrameFormatTable->FindFormatByName( rName );
+ else
+ {
+ auto [it, itEnd] = mpTableFrameFormatTable->findRangeByName(rName);
+ // Only the ones set in the Doc
+ for( ; it != itEnd; ++it )
+ {
+ const SwFrameFormat* pFormat = *it;
+ if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
+ pFormat->GetName() == rName )
+ {
+ pRet = pFormat;
+ break;
+ }
+ }
+ }
+ return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
+}
+
+void SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff )
+{
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
+ std::unique_ptr<SwUndo> pUndo;
+
+ 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( std::move(pUndo) );
+ }
+
+ if( bRet )
+ {
+ getIDocumentState().SetModified();
+ }
+}
+
+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 SwTableBoxNumFormat* pNumFormatItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT,
+ false );
+ if( pNumFormatItem && GetNumberFormatter()->IsTextFormat(pNumFormatItem->GetValue()) )
+ return ;
+
+ std::unique_ptr<SwUndoTableNumFormat> pUndo;
+
+ 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.reset(new SwUndoTableNumFormat( rBox ));
+ pUndo->SetNumFormat( nFormatIdx, fNumber );
+ }
+
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( GetAttrPool() );
+
+ 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 = 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
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false ) ||
+ SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, false ) )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
+ pUndo.reset(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 )
+ return;
+
+ if( pUndo )
+ {
+ pUndo->SetBox( rBox );
+ GetIDocumentUndoRedo().AppendUndo(std::move(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( std::make_unique<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() )
+ return;
+
+ SwTextNode * pTextNode = pNode->GetTextNode();
+ if (!(pTextNode && pTextNode->IsNumbered()
+ && pTextNode->GetText().isEmpty()))
+ return;
+
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1>
+ rSet( pTextNode->GetDoc().GetAttrPool() );
+ pTextNode->SwContentNode::GetAttr( rSet );
+ const SfxStringItem* pFormatItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, false );
+ if ( !pFormatItem )
+ return;
+
+ SwUndoDelNum * pUndo;
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo = new SwUndoDelNum( aPam );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ else
+ pUndo = nullptr;
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+ aRegH.RegisterInModify( pTextNode , *pTextNode );
+ if ( pUndo )
+ pUndo->AddNode( *pTextNode );
+ std::unique_ptr<SfxStringItem> pNewItem(pFormatItem->Clone());
+ pNewItem->SetValue(OUString());
+ rSet.Put( std::move(pNewItem) );
+ pTextNode->SetAttr( rSet );
+}
+
+void SwDoc::ClearBoxNumAttrs( const SwNodeIndex& rNode )
+{
+ SwStartNode* pSttNd = rNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
+ if( nullptr == pSttNd ||
+ SwNodeOffset(2) != pSttNd->EndOfSectionIndex() - pSttNd->GetIndex())
+ return;
+
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+
+ const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
+ const SwTableBoxNumFormat* pFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false );
+ if( !pFormatItem ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoTableNumFormat>(*pBox));
+ }
+
+ SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
+
+ // Keep TextFormats!
+ sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
+ if( pFormatItem && GetNumberFormatter()->IsTextFormat(
+ 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, const OUString& rStyleName )
+{
+ 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 )
+ {
+ std::unique_ptr<SwUndoCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoCpyTable(*this));
+ }
+
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ bRet = pSrcTableNd->GetTable().MakeCopy( *this, rInsPos, rBoxes,
+ bCpyName, rStyleName );
+ }
+
+ if( pUndo && bRet )
+ {
+ pInsTableNd = GetNodes()[ rInsPos.nNode.GetIndex() - 1 ]->FindTableNode();
+
+ pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else
+ {
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
+ RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete );
+
+ std::unique_ptr<SwUndoTableCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(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, aPos, rBoxes, true ))
+ {
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ return false;
+ }
+ aPos.nNode -= SwNodeOffset(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.get() );
+ }
+ else
+ {
+ SwNodeIndex aNdIdx( *pSttNd, 1 );
+ bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
+ aNdIdx, pUndo.get() );
+ }
+
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ // If the Table could not be copied, delete the Undo object
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ if( bRet || !pUndo->IsEmpty() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(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, SwNodeOffset(0) );
+ }
+ return bRet;
+}
+
+bool SwDoc::UnProtectTableCells( SwTable& rTable )
+{
+ bool bChgd = false;
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rTable.GetTableNode() ));
+
+ 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 && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ return bChgd;
+}
+
+void SwDoc::UnProtectCells( const OUString& rName )
+{
+ SwTableFormat* pFormat = FindTableFormatByName( rName );
+ if( pFormat )
+ {
+ bool bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
+ if( bChgd )
+ getIDocumentState().SetModified();
+ }
+}
+
+bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
+{
+ bool bChgd = false;
+ if( !rBoxes.empty() )
+ {
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() ));
+
+ 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 && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(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() )
+ {
+ SwNodeOffset 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())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleMake>(rName, *this));
+ }
+
+ 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)
+ {
+ 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())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleDelete>(std::move(pReleasedFormat), std::move(vAffectedTables), *this));
+ }
+ }
+
+ return pReleasedFormat;
+}
+
+void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
+{
+ SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
+ if (!pFormat)
+ return;
+
+ 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)
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
+ }
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleUpdate>(*pFormat, aOldFormat, *this));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */