Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / docnode / ndtbl.cxx
blobcec3e7df57c4714888335ccb3c518d063f9a50a9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <libxml/xmlwriter.h>
21 #include <config_wasm_strip.h>
22 #include <memory>
23 #include <fesh.hxx>
24 #include <hintids.hxx>
25 #include <editeng/lrspitem.hxx>
26 #include <editeng/protitem.hxx>
27 #include <editeng/boxitem.hxx>
28 #include <svl/stritem.hxx>
29 #include <editeng/shaditem.hxx>
30 #include <fmtfsize.hxx>
31 #include <fmtornt.hxx>
32 #include <fmtfordr.hxx>
33 #include <fmtpdsc.hxx>
34 #include <fmtanchr.hxx>
35 #include <fmtlsplt.hxx>
36 #include <frmatr.hxx>
37 #include <cellfrm.hxx>
38 #include <pagefrm.hxx>
39 #include <tabcol.hxx>
40 #include <doc.hxx>
41 #include <IDocumentUndoRedo.hxx>
42 #include <UndoManager.hxx>
43 #include <DocumentSettingManager.hxx>
44 #include <IDocumentChartDataProviderAccess.hxx>
45 #include <IDocumentRedlineAccess.hxx>
46 #include <IDocumentStylePoolAccess.hxx>
47 #include <IDocumentFieldsAccess.hxx>
48 #include <IDocumentLayoutAccess.hxx>
49 #include <IDocumentState.hxx>
50 #include <cntfrm.hxx>
51 #include <pam.hxx>
52 #include <swcrsr.hxx>
53 #include <swtable.hxx>
54 #include <swundo.hxx>
55 #include <tblsel.hxx>
56 #include <poolfmt.hxx>
57 #include <tabfrm.hxx>
58 #include <UndoCore.hxx>
59 #include <UndoRedline.hxx>
60 #include <UndoDelete.hxx>
61 #include <UndoNumbering.hxx>
62 #include <UndoTable.hxx>
63 #include <hints.hxx>
64 #include <tblafmt.hxx>
65 #include <frminf.hxx>
66 #include <cellatr.hxx>
67 #include <swtblfmt.hxx>
68 #include <swddetbl.hxx>
69 #include <mvsave.hxx>
70 #include <docary.hxx>
71 #include <redline.hxx>
72 #include <rolbck.hxx>
73 #include <tblrwcl.hxx>
74 #include <editsh.hxx>
75 #include <txtfrm.hxx>
76 #include <section.hxx>
77 #include <frmtool.hxx>
78 #include <node2lay.hxx>
79 #include <strings.hrc>
80 #include <docsh.hxx>
81 #include <unochart.hxx>
82 #include <node.hxx>
83 #include <ndtxt.hxx>
84 #include <cstdlib>
85 #include <map>
86 #include <algorithm>
87 #include <rootfrm.hxx>
88 #include <fldupde.hxx>
89 #include <calbck.hxx>
90 #include <fntcache.hxx>
91 #include <frameformats.hxx>
92 #include <o3tl/numeric.hxx>
93 #include <o3tl/string_view.hxx>
94 #include <svl/numformat.hxx>
95 #include <tools/datetimeutils.hxx>
96 #include <sal/log.hxx>
97 #include <osl/diagnose.h>
99 #ifdef DBG_UTIL
100 #define CHECK_TABLE(t) (t).CheckConsistency();
101 #else
102 #define CHECK_TABLE(t)
103 #endif
105 using ::editeng::SvxBorderLine;
106 using namespace ::com::sun::star;
108 const sal_Unicode T2T_PARA = 0x0a;
110 static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
112 bool bTop = false, bBottom = false, bLeft = false, bRight = false;
113 switch ( nId )
115 case 0: bTop = bBottom = bLeft = true; break;
116 case 1: bTop = bBottom = bLeft = bRight = true; break;
117 case 2: bBottom = bLeft = true; break;
118 case 3: bBottom = bLeft = bRight = true; break;
121 const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
122 Color aCol( bHTML ? COL_GRAY : COL_BLACK );
123 // Default border in Writer: 0.5pt (matching Word)
124 SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin );
125 if ( bHTML )
127 aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
129 SvxBoxItem aBox(RES_BOX);
130 aBox.SetAllDistances(55);
131 if ( bTop )
132 aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
133 if ( bBottom )
134 aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
135 if ( bLeft )
136 aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
137 if ( bRight )
138 aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
139 rFormat.SetFormatAttr( aBox );
142 typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
143 typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;
145 static void
146 lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
147 sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
149 DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
150 if (!pMap)
152 pMap = new DfltBoxAttrMap_t;
153 rBoxFormatArr[ nId ] = pMap;
156 SwTableBoxFormat* pNewTableBoxFormat = nullptr;
157 SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
158 DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
159 if (pMap->end() != iter)
161 pNewTableBoxFormat = iter->second;
163 else
165 SwDoc* pDoc = pBoxFrameFormat->GetDoc();
166 // format does not exist, so create it
167 pNewTableBoxFormat = pDoc->MakeTableBoxFormat();
168 pNewTableBoxFormat->SetFormatAttr( pBoxFrameFormat->GetAttrSet().Get( RES_FRM_SIZE ) );
170 if( pAutoFormat )
171 pAutoFormat->UpdateToSet( nId, false, false,
172 const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
173 SwTableAutoFormatUpdateFlags::Box,
174 pDoc->GetNumberFormatter() );
175 else
176 ::lcl_SetDfltBoxAttr( *pNewTableBoxFormat, nId );
178 (*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
180 rBox.ChgFrameFormat( pNewTableBoxFormat );
183 static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
184 sal_uInt16 nCols, sal_uInt8 nId )
186 if ( !rBoxFormatArr[nId] )
188 SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
189 if( USHRT_MAX != nCols )
190 pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
191 USHRT_MAX / nCols, 0 ));
192 ::lcl_SetDfltBoxAttr( *pBoxFormat, nId );
193 rBoxFormatArr[ nId ] = pBoxFormat;
195 return rBoxFormatArr[nId];
198 static SwTableBoxFormat *lcl_CreateAFormatBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
199 const SwTableAutoFormat& rAutoFormat,
200 const sal_uInt16 nRows, const sal_uInt16 nCols, sal_uInt8 nId )
202 if( !rBoxFormatArr[nId] )
204 SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
205 rAutoFormat.UpdateToSet( nId, nRows==1, nCols==1,
206 const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
207 SwTableAutoFormatUpdateFlags::Box,
208 rDoc.GetNumberFormatter( ) );
209 if( USHRT_MAX != nCols )
210 pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
211 USHRT_MAX / nCols, 0 ));
212 rBoxFormatArr[ nId ] = pBoxFormat;
214 return rBoxFormatArr[nId];
217 SwTableNode* SwDoc::IsIdxInTable( const SwNodeIndex& rIdx ) { return IsInTable(rIdx.GetNode()); }
219 SwTableNode* SwDoc::IsInTable(const SwNode& rIdx)
221 SwNode* pNd = const_cast<SwNode*>(&rIdx);
222 do {
223 pNd = pNd->StartOfSectionNode();
224 SwTableNode* pTableNd = pNd->GetTableNode();
225 if( pTableNd )
226 return pTableNd;
227 } while ( pNd->GetIndex() );
228 return nullptr;
232 * Insert a new Box before the InsPos
234 bool SwNodes::InsBoxen( SwTableNode* pTableNd,
235 SwTableLine* pLine,
236 SwTableBoxFormat* pBoxFormat,
237 SwTextFormatColl* pTextColl,
238 const SfxItemSet* pAutoAttr,
239 sal_uInt16 nInsPos,
240 sal_uInt16 nCnt )
242 if( !nCnt )
243 return false;
244 OSL_ENSURE( pLine, "No valid Line" );
246 // Move Index after the Line's last Box
247 SwNodeOffset nIdxPos(0);
248 SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
249 if( !pLine->GetTabBoxes().empty() )
251 if( nInsPos < pLine->GetTabBoxes().size() )
253 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
254 pLine->GetTabBoxes()[ nInsPos ] );
255 if( nullptr == pPrvBox )
256 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
258 else
260 pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
261 pLine->GetTabBoxes().back() );
262 if( nullptr == pNxtBox )
263 pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
266 else
268 pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
269 if( nullptr == pNxtBox )
270 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
273 if( !pPrvBox && !pNxtBox )
275 bool bSetIdxPos = true;
276 if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
278 const SwTableLine* pTableLn = pLine;
279 while( pTableLn->GetUpper() )
280 pTableLn = pTableLn->GetUpper()->GetUpper();
282 if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
284 // Before the Table's first Box
285 while( !( pNxtBox = pLine->GetTabBoxes()[0])->GetTabLines().empty() )
286 pLine = pNxtBox->GetTabLines()[0];
287 nIdxPos = pNxtBox->GetSttIdx();
288 bSetIdxPos = false;
291 if( bSetIdxPos )
292 // Tables without content or at the end; move before the End
293 nIdxPos = pTableNd->EndOfSectionIndex();
295 else if( pNxtBox ) // There is a successor
296 nIdxPos = pNxtBox->GetSttIdx();
297 else // There is a predecessor
298 nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;
300 SwNodeIndex aEndIdx( *this, nIdxPos );
301 for( sal_uInt16 n = 0; n < nCnt; ++n )
303 SwStartNode* pSttNd = new SwStartNode( aEndIdx.GetNode(), SwNodeType::Start,
304 SwTableBoxStartNode );
305 pSttNd->m_pStartOfSection = pTableNd;
306 new SwEndNode( aEndIdx.GetNode(), *pSttNd );
308 pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
310 SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
311 sal_uInt16 nRealInsPos = nInsPos + n;
312 if (nRealInsPos > rTabBoxes.size())
313 nRealInsPos = rTabBoxes.size();
315 rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );
317 if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
318 && RES_CONDTXTFMTCOLL != pTextColl->Which()
320 new SwTextNode( *pSttNd->EndOfSectionNode(), pTextColl, pAutoAttr );
321 else
323 // Handle Outline numbering correctly!
324 SwTextNode* pTNd = new SwTextNode(
325 *pSttNd->EndOfSectionNode(),
326 GetDoc().GetDfltTextFormatColl(),
327 pAutoAttr );
328 pTNd->ChgFormatColl( pTextColl );
331 return true;
335 * Insert a new Table
337 const SwTable* SwDoc::InsertTable( const SwInsertTableOptions& rInsTableOpts,
338 const SwPosition& rPos, sal_uInt16 nRows,
339 sal_uInt16 nCols, sal_Int16 eAdjust,
340 const SwTableAutoFormat* pTAFormat,
341 const std::vector<sal_uInt16> *pColArr,
342 bool bCalledFromShell,
343 bool bNewModel,
344 const OUString& rTableName )
346 assert(nRows && "Table without line?");
347 assert(nCols && "Table without rows?");
350 // Do not copy into Footnotes!
351 if( rPos.GetNode() < GetNodes().GetEndOfInserts() &&
352 rPos.GetNode().GetIndex() >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
353 return nullptr;
355 // If the ColumnArray has a wrong count, ignore it!
356 if( pColArr &&
357 static_cast<size_t>(nCols + ( text::HoriOrientation::NONE == eAdjust ? 2 : 1 )) != pColArr->size() )
358 pColArr = nullptr;
361 OUString aTableName = rTableName;
362 if (aTableName.isEmpty() || FindTableFormatByName(aTableName) != nullptr)
363 aTableName = GetUniqueTableName();
365 if( GetIDocumentUndoRedo().DoesUndo() )
367 GetIDocumentUndoRedo().AppendUndo(
368 std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<sal_uInt16>(eAdjust),
369 rInsTableOpts, pTAFormat, pColArr,
370 aTableName));
373 // Start with inserting the Nodes and get the AutoFormat for the Table
374 SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
375 *pHeadColl = pBodyColl;
377 bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );
379 if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
380 pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );
382 const sal_uInt16 nRowsToRepeat =
383 SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
384 rInsTableOpts.mnRowsToRepeat :
387 /* Save content node to extract FRAMEDIR from. */
388 const SwContentNode * pContentNd = rPos.GetNode().GetContentNode();
390 /* If we are called from a shell pass the attrset from
391 pContentNd (aka the node the table is inserted at) thus causing
392 SwNodes::InsertTable to propagate an adjust item if
393 necessary. */
394 SwTableNode *pTableNd = SwNodes::InsertTable(
395 rPos.GetNode(),
396 nCols,
397 pBodyColl,
398 nRows,
399 nRowsToRepeat,
400 pHeadColl,
401 bCalledFromShell ? &pContentNd->GetSwAttrSet() : nullptr );
403 // Create the Box/Line/Table construct
404 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
405 SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );
407 /* If the node to insert the table at is a context node and has a
408 non-default FRAMEDIR propagate it to the table. */
409 if (pContentNd)
411 const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
412 if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
414 pTableFormat->SetFormatAttr( *pItem );
418 // Set Orientation at the Table's Format
419 pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
420 // All lines use the left-to-right Fill-Order!
421 pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
423 // Set USHRT_MAX as the Table's default SSize
424 SwTwips nWidth = USHRT_MAX;
425 if( pColArr )
427 sal_uInt16 nSttPos = pColArr->front();
428 sal_uInt16 nLastPos = pColArr->back();
429 if( text::HoriOrientation::NONE == eAdjust )
431 sal_uInt16 nFrameWidth = nLastPos;
432 nLastPos = (*pColArr)[ pColArr->size()-2 ];
433 pTableFormat->SetFormatAttr( SvxLRSpaceItem( nSttPos, nFrameWidth - nLastPos, 0, RES_LR_SPACE ) );
435 nWidth = nLastPos - nSttPos;
437 else
439 nWidth /= nCols;
440 nWidth *= nCols; // to avoid rounding problems
442 pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
443 if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
444 pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
446 // Move the hard PageDesc/PageBreak Attributes if needed
447 SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
448 ->GetContentNode();
449 if( pNextNd && pNextNd->HasSwAttrSet() )
451 const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
452 if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
454 pTableFormat->SetFormatAttr( *pItem );
455 pNextNd->ResetAttr( RES_PAGEDESC );
456 pNdSet = pNextNd->GetpSwAttrSet();
458 const SvxFormatBreakItem* pItem;
459 if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
461 pTableFormat->SetFormatAttr( *pItem );
462 pNextNd->ResetAttr( RES_BREAK );
466 SwTable& rNdTable = pTableNd->GetTable();
467 rNdTable.RegisterToFormat( *pTableFormat );
469 rNdTable.SetRowsToRepeat( nRowsToRepeat );
470 rNdTable.SetTableModel( bNewModel );
472 std::vector<SwTableBoxFormat*> aBoxFormatArr;
473 SwTableBoxFormat* pBoxFormat = nullptr;
474 if( !bDfltBorders && !pTAFormat )
476 pBoxFormat = MakeTableBoxFormat();
477 pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
479 else
481 const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
482 aBoxFormatArr.resize( nBoxArrLen, nullptr );
484 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
486 SwNodeIndex aNdIdx( *pTableNd, 1 ); // Set to StartNode of first Box
487 SwTableLines& rLines = rNdTable.GetTabLines();
488 for( sal_uInt16 n = 0; n < nRows; ++n )
490 SwTableLine* pLine = new SwTableLine( pLineFormat, nCols, nullptr );
491 rLines.insert( rLines.begin() + n, pLine );
492 SwTableBoxes& rBoxes = pLine->GetTabBoxes();
493 for( sal_uInt16 i = 0; i < nCols; ++i )
495 SwTableBoxFormat *pBoxF;
496 if( pTAFormat )
498 sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
499 pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
500 nRows, nCols, nId );
502 // Set the Paragraph/Character Attributes if needed
503 if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
505 aCharSet.ClearItem();
506 pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
507 SwTableAutoFormatUpdateFlags::Char, nullptr );
508 if( aCharSet.Count() )
509 GetNodes()[ aNdIdx.GetIndex()+1 ]->GetContentNode()->
510 SetAttr( aCharSet );
513 else if( bDfltBorders )
515 sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
516 pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
518 else
519 pBoxF = pBoxFormat;
521 // For AutoFormat on input: the columns are set when inserting the Table
522 // The Array contains the columns positions and not their widths!
523 if( pColArr )
525 nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
526 if( pBoxF->GetFrameSize().GetWidth() != nWidth )
528 if( pBoxF->HasWriterListeners() ) // Create new Format
530 SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
531 *pNewFormat = *pBoxF;
532 pBoxF = pNewFormat;
534 pBoxF->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
538 SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
539 rBoxes.insert( rBoxes.begin() + i, pBox );
540 aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
543 // Insert Frames
544 pTableNd->MakeOwnFrames();
546 // To-Do - add 'SwExtraRedlineTable' also ?
547 if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
549 SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, SwNodeOffset(1) );
550 if( getIDocumentRedlineAccess().IsRedlineOn() )
551 getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
552 else
553 getIDocumentRedlineAccess().SplitRedline( aPam );
556 getIDocumentState().SetModified();
557 CHECK_TABLE(rNdTable);
558 return &rNdTable;
561 SwTableNode* SwNodes::InsertTable( SwNode& rNd,
562 sal_uInt16 nBoxes,
563 SwTextFormatColl* pContentTextColl,
564 sal_uInt16 nLines,
565 sal_uInt16 nRepeat,
566 SwTextFormatColl* pHeadlineTextColl,
567 const SwAttrSet * pAttrSet)
569 if( !nBoxes )
570 return nullptr;
572 // If Lines is given, create the Matrix from Lines and Boxes
573 if( !pHeadlineTextColl || !nLines )
574 pHeadlineTextColl = pContentTextColl;
576 SwTableNode * pTableNd = new SwTableNode( rNd );
577 SwEndNode* pEndNd = new SwEndNode( rNd, *pTableNd );
579 if( !nLines ) // For the for loop
580 ++nLines;
582 SwTextFormatColl* pTextColl = pHeadlineTextColl;
583 for( sal_uInt16 nL = 0; nL < nLines; ++nL )
585 for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
587 SwStartNode* pSttNd = new SwStartNode( *pEndNd, SwNodeType::Start,
588 SwTableBoxStartNode );
589 pSttNd->m_pStartOfSection = pTableNd;
591 SwTextNode * pTmpNd = new SwTextNode( *pEndNd, pTextColl );
593 // #i60422# Propagate some more attributes.
594 const SfxPoolItem* pItem = nullptr;
595 if ( nullptr != pAttrSet )
597 static const sal_uInt16 aPropagateItems[] = {
598 RES_PARATR_ADJUST,
599 RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
600 RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
601 RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, 0 };
603 const sal_uInt16* pIdx = aPropagateItems;
604 while ( *pIdx != 0 )
606 if ( SfxItemState::SET != pTmpNd->GetSwAttrSet().GetItemState( *pIdx ) &&
607 SfxItemState::SET == pAttrSet->GetItemState( *pIdx, true, &pItem ) )
608 static_cast<SwContentNode *>(pTmpNd)->SetAttr(*pItem);
609 ++pIdx;
613 new SwEndNode( *pEndNd, *pSttNd );
615 if ( nL + 1 >= nRepeat )
616 pTextColl = pContentTextColl;
618 return pTableNd;
622 * Text to Table
624 const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
625 const SwPaM& rRange, sal_Unicode cCh,
626 sal_Int16 eAdjust,
627 const SwTableAutoFormat* pTAFormat )
629 // See if the selection contains a Table
630 auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
632 SwNodeOffset nCnt = pStt->GetNodeIndex();
633 for( ; nCnt <= pEnd->GetNodeIndex(); ++nCnt )
634 if( !GetNodes()[ nCnt ]->IsTextNode() )
635 return nullptr;
638 if (GetIDocumentUndoRedo().DoesUndo())
640 GetIDocumentUndoRedo().StartUndo(SwUndoId::TEXTTOTABLE, nullptr);
643 // tdf#153115 first, remove all redlines; splitting them at cell boundaries
644 // would be tricky to implement, and it's unclear what the value of
645 // existing redlines is once it's been converted to a table
646 getIDocumentRedlineAccess().AcceptRedline(rRange, true);
648 // Save first node in the selection if it is a context node
649 SwContentNode * pSttContentNd = pStt->GetNode().GetContentNode();
651 SwPaM aOriginal( *pStt, *pEnd );
652 pStt = aOriginal.GetMark();
653 pEnd = aOriginal.GetPoint();
655 SwUndoTextToTable* pUndo = nullptr;
656 if( GetIDocumentUndoRedo().DoesUndo() )
658 pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
659 o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
660 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
662 // Do not add splitting the TextNode to the Undo history
663 GetIDocumentUndoRedo().DoUndo( false );
666 ::PaMCorrAbs( aOriginal, *pEnd );
668 // Make sure that the range is on Node Edges
669 SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() );
670 if( pStt->GetContentIndex() )
671 getIDocumentContentOperations().SplitNode( *pStt, false );
673 bool bEndContent = 0 != pEnd->GetContentIndex();
675 // Do not split at the End of a Line (except at the End of the Doc)
676 if( bEndContent )
678 if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
679 || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
681 getIDocumentContentOperations().SplitNode( *pEnd, false );
682 const_cast<SwPosition*>(pEnd)->Adjust(SwNodeOffset(-1));
683 // A Node and at the End?
684 if( pStt->GetNodeIndex() >= pEnd->GetNodeIndex() )
685 --aRg.aStart;
687 else
688 ++aRg.aEnd;
691 if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
693 OSL_FAIL( "empty range" );
694 ++aRg.aEnd;
697 // We always use Upper to insert the Table
698 SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
700 GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );
702 // Create the Box/Line/Table construct
703 SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
704 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
705 SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
707 // All Lines have a left-to-right Fill Order
708 pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
709 // The Table's SSize is USHRT_MAX
710 pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
711 if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
712 pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
714 /* If the first node in the selection is a context node and if it
715 has an item FRAMEDIR set (no default) propagate the item to the
716 replacing table. */
717 if (pSttContentNd)
719 const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
720 if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
722 pTableFormat->SetFormatAttr( *pItem );
726 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
727 //until after RegisterToFormat is completed
728 bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
729 getIDocumentState().SetEnableSetModified(false);
731 SwTableNode* pTableNd = GetNodes().TextToTable(
732 aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
733 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );
735 SwTable& rNdTable = pTableNd->GetTable();
737 const sal_uInt16 nRowsToRepeat =
738 SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
739 rInsTableOpts.mnRowsToRepeat :
741 rNdTable.SetRowsToRepeat(nRowsToRepeat);
743 bool bUseBoxFormat = false;
744 if( !pBoxFormat->HasWriterListeners() )
746 // The Box's Formats already have the right size, we must only set
747 // the right Border/AutoFormat.
748 bUseBoxFormat = true;
749 pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
750 delete pBoxFormat;
751 eAdjust = text::HoriOrientation::NONE;
754 // Set Orientation in the Table's Format
755 pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
756 rNdTable.RegisterToFormat(*pTableFormat);
758 if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
760 sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
761 std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
762 std::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
763 if( bUseBoxFormat )
765 aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
767 else
769 aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
772 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
774 SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
776 SwTableBoxFormat *pBoxF = nullptr;
777 SwTableLines& rLines = rNdTable.GetTabLines();
778 const SwTableLines::size_type nRows = rLines.size();
779 for( SwTableLines::size_type n = 0; n < nRows; ++n )
781 SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
782 const SwTableBoxes::size_type nCols = rBoxes.size();
783 for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
785 SwTableBox* pBox = rBoxes[ i ];
786 bool bChgSz = false;
788 if( pTAFormat )
790 sal_uInt8 nId = static_cast<sal_uInt8>(!n ? 0 : (( n+1 == nRows )
791 ? 12 : (4 * (1 + ((n-1) & 1 )))));
792 nId = nId + static_cast<sal_uInt8>(!i ? 0 :
793 ( i+1 == nCols ? 3 : (1 + ((i-1) & 1))));
794 if( bUseBoxFormat )
795 ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId, pTAFormat );
796 else
798 bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
799 pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
800 *pTAFormat, USHRT_MAX, USHRT_MAX, nId );
803 // Set Paragraph/Character Attributes if needed
804 if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
806 aCharSet.ClearItem();
807 pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
808 SwTableAutoFormatUpdateFlags::Char, nullptr );
809 if( aCharSet.Count() )
811 SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
812 SwNodeOffset nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
813 for( ; nSttNd < nEndNd; ++nSttNd )
815 SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
816 if( pNd )
818 if( pHistory )
820 SwRegHistory aReg( pNd, *pNd, pHistory );
821 pNd->SetAttr( aCharSet );
823 else
824 pNd->SetAttr( aCharSet );
830 else
832 sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
833 if( bUseBoxFormat )
834 ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
835 else
837 bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
838 pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
839 USHRT_MAX, nId );
843 if( !bUseBoxFormat )
845 if( bChgSz )
846 pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
847 pBox->ChgFrameFormat( pBoxF );
852 if( bUseBoxFormat )
854 for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
856 delete (*aBoxFormatArr1)[ i ];
861 // Check the boxes for numbers
862 if( IsInsTableFormatNum() )
864 for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
866 ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
870 SwNodeOffset nIdx = pTableNd->GetIndex();
871 aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
874 SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
875 rTmp.DeleteMark();
876 rTmp.GetPoint()->Assign( *pTableNd );
877 GetNodes().GoNext( rTmp.GetPoint() );
880 if( pUndo )
882 GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
885 getIDocumentState().SetEnableSetModified(bEnableSetModified);
886 getIDocumentState().SetModified();
887 getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
888 return &rNdTable;
891 static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
893 // delete old layout frames, new ones need to be created...
894 rNode.DelFrames(nullptr);
896 if (!rNode.IsTextNode())
898 return;
901 SwTextNode & rTextNode = *rNode.GetTextNode();
902 // remove PageBreaks/PageDesc/ColBreak
903 SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
904 if (!pSet)
905 return;
907 if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
909 if (pTableFormat)
911 pTableFormat->SetFormatAttr(*pItem);
913 rTextNode.ResetAttr(RES_BREAK);
914 pSet = rTextNode.GetpSwAttrSet();
917 const SwFormatPageDesc* pPageDescItem;
918 if (pSet
919 && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
920 && pPageDescItem->GetPageDesc())
922 if (pTableFormat)
924 pTableFormat->SetFormatAttr(*pPageDescItem);
926 rTextNode.ResetAttr(RES_PAGEDESC);
931 * balance lines in table, insert empty boxes so all lines have the size
933 static void
934 lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
935 SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
936 SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
938 for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
940 SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
941 size_t const nBoxes = pCurrLine->GetTabBoxes().size();
942 if (nMaxBoxes != nBoxes)
944 rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
945 nullptr, nBoxes, nMaxBoxes - nBoxes);
947 if (pUndo)
949 for (size_t i = nBoxes; i < nMaxBoxes; ++i)
951 pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
955 // if the first line is missing boxes, the width array is useless!
956 if (!n && pPositions)
958 pPositions->clear();
964 static void
965 lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
966 SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
967 std::vector<sal_uInt16> *const pPositions)
969 if (pPositions && !pPositions->empty())
971 SwTableLines& rLns = rTable.GetTabLines();
972 sal_uInt16 nLastPos = 0;
973 for (size_t n = 0; n < pPositions->size(); ++n)
975 SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
976 pNewFormat->SetFormatAttr(
977 SwFormatFrameSize(SwFrameSize::Variable, (*pPositions)[n] - nLastPos));
978 for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
980 // Have to do an Add here, because the BoxFormat
981 // is still needed by the caller
982 pNewFormat->Add( rLns[ nTmpLine ]->GetTabBoxes()[ n ] );
985 nLastPos = (*pPositions)[ n ];
988 // propagate size upwards from format, so the table gets the right size
989 SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
990 "who is still registered in the format?");
991 rBoxFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nLastPos ));
993 else
995 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
996 rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
1000 SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
1001 SwTableFormat* pTableFormat,
1002 SwTableLineFormat* pLineFormat,
1003 SwTableBoxFormat* pBoxFormat,
1004 SwTextFormatColl* pTextColl,
1005 SwUndoTextToTable* pUndo )
1007 if( rRange.aStart >= rRange.aEnd )
1008 return nullptr;
1010 SwTableNode * pTableNd = new SwTableNode( rRange.aStart.GetNode() );
1011 new SwEndNode( rRange.aEnd.GetNode(), *pTableNd );
1013 SwDoc& rDoc = GetDoc();
1014 std::vector<sal_uInt16> aPosArr;
1015 SwTable& rTable = pTableNd->GetTable();
1016 SwTableBox* pBox;
1017 sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
1019 SwNodeIndex aSttIdx( *pTableNd, 1 );
1020 SwNodeIndex aEndIdx( rRange.aEnd, -1 );
1021 for( nLines = 0, nBoxes = 0;
1022 aSttIdx.GetIndex() < aEndIdx.GetIndex();
1023 aSttIdx += SwNodeOffset(2), nLines++, nBoxes = 0 )
1025 SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
1026 OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );
1028 if( !nLines && 0x0b == cCh )
1030 cCh = 0x09;
1032 // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
1033 SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
1034 if( aFInfo.IsOneLine() ) // only makes sense in this case
1036 OUString const& rText(pTextNd->GetText());
1037 for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
1039 if (rText[nChPos] == cCh)
1041 // sw_redlinehide: no idea if this makes any sense...
1042 TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
1043 aPosArr.push_back( o3tl::narrowing<sal_uInt16>(
1044 aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
1048 aPosArr.push_back(
1049 o3tl::narrowing<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
1050 aFInfo.GetFrame()->getFramePrintArea().Bottom() :
1051 aFInfo.GetFrame()->getFramePrintArea().Right()) );
1056 lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);
1058 // Set the TableNode as StartNode for all TextNodes in the Table
1059 pTextNd->m_pStartOfSection = pTableNd;
1061 SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
1062 rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
1064 SwStartNode* pSttNd;
1065 SwPosition aCntPos( aSttIdx, pTextNd, 0);
1067 const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1068 pContentStore->Save(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);
1070 if( T2T_PARA != cCh )
1072 for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
1074 if (pTextNd->GetText()[nChPos] == cCh)
1076 aCntPos.SetContent(nChPos);
1077 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
1078 [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
1080 if (!pContentStore->Empty())
1082 pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
1085 SwContentNode *const pNewNd =
1086 pTextNd->SplitContentNode(aCntPos, &restoreFunc);
1088 // Delete separator and correct search string
1089 pTextNd->EraseText( aCntPos, 1 );
1090 nChPos = 0;
1092 // Set the TableNode as StartNode for all TextNodes in the Table
1093 const SwNodeIndex aTmpIdx( aCntPos.GetNode(), -1 );
1094 pSttNd = new SwStartNode( aTmpIdx.GetNode(), SwNodeType::Start,
1095 SwTableBoxStartNode );
1096 new SwEndNode( aCntPos.GetNode(), *pSttNd );
1097 pNewNd->m_pStartOfSection = pSttNd;
1099 // Assign Section to the Box
1100 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1101 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1103 else
1105 ++nChPos;
1110 // Now for the last substring
1111 if( !pContentStore->Empty())
1112 pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );
1114 pSttNd = new SwStartNode( aCntPos.GetNode(), SwNodeType::Start, SwTableBoxStartNode );
1115 const SwNodeIndex aTmpIdx( aCntPos.GetNode(), 1 );
1116 new SwEndNode( aTmpIdx.GetNode(), *pSttNd );
1117 pTextNd->m_pStartOfSection = pSttNd;
1119 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1120 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1121 if( nMaxBoxes < nBoxes )
1122 nMaxBoxes = nBoxes;
1125 lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
1126 pUndo, &aPosArr);
1127 lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, rDoc, &aPosArr);
1129 return pTableNd;
1132 const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
1134 if (rTableNodes.empty())
1135 return nullptr;
1137 const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();
1139 if (rFirstRange.empty())
1140 return nullptr;
1142 const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();
1144 if (rLastRange.empty())
1145 return nullptr;
1147 /* Save first node in the selection if it is a content node. */
1148 SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();
1150 const SwNodeRange& rStartRange = *rFirstRange.begin();
1151 const SwNodeRange& rEndRange = *rLastRange.rbegin();
1153 //!!! not necessarily TextNodes !!!
1154 SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
1155 const SwPosition *pStt = aOriginal.GetMark();
1156 SwPosition *pEnd = aOriginal.GetPoint();
1158 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
1159 if (bUndo)
1161 // Do not add splitting the TextNode to the Undo history
1162 GetIDocumentUndoRedo().DoUndo(false);
1165 ::PaMCorrAbs( aOriginal, *pEnd );
1167 // make sure that the range is on Node Edges
1168 SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() );
1169 if( pStt->GetContentIndex() )
1170 getIDocumentContentOperations().SplitNode( *pStt, false );
1172 bool bEndContent = 0 != pEnd->GetContentIndex();
1174 // Do not split at the End of a Line (except at the End of the Doc)
1175 if( bEndContent )
1177 if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
1178 || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
1180 getIDocumentContentOperations().SplitNode( *pEnd, false );
1181 pEnd->Adjust(SwNodeOffset(-1));
1182 // A Node and at the End?
1183 if( pStt->GetNodeIndex() >= pEnd->GetNodeIndex() )
1184 --aRg.aStart;
1186 else
1187 ++aRg.aEnd;
1190 assert(aRg.aEnd.GetNode() == pEnd->GetNode());
1191 assert(aRg.aStart.GetNode() == pStt->GetNode());
1192 if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
1194 OSL_FAIL( "empty range" );
1195 ++aRg.aEnd;
1200 // TODO: this is not Undo-able - only good enough for file import
1201 IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
1202 SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
1203 SwNodeIndex const* pPrev(&prev);
1204 // pPrev could point to non-textnode now
1205 for (const auto& rRow : rTableNodes)
1207 for (const auto& rCell : rRow)
1209 assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
1210 SwPaM pam(rCell.aStart, 0, *pPrev,
1211 (pPrev->GetNode().IsContentNode())
1212 ? pPrev->GetNode().GetContentNode()->Len() : 0);
1213 rIDRA.SplitRedline(pam);
1214 pPrev = &rCell.aEnd;
1217 // another one to break between last cell and node after table
1218 SwPaM pam(pPrev->GetNode(), SwNodeOffset(+1), 0,
1219 pPrev->GetNode(), SwNodeOffset(0),
1220 (pPrev->GetNode().IsContentNode())
1221 ? pPrev->GetNode().GetContentNode()->Len() : 0);
1222 rIDRA.SplitRedline(pam);
1225 // We always use Upper to insert the Table
1226 SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
1228 GetIDocumentUndoRedo().DoUndo(bUndo);
1230 // Create the Box/Line/Table construct
1231 SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
1232 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
1233 SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
1235 // All Lines have a left-to-right Fill Order
1236 pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
1237 // The Table's SSize is USHRT_MAX
1238 pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
1240 /* If the first node in the selection is a context node and if it
1241 has an item FRAMEDIR set (no default) propagate the item to the
1242 replacing table. */
1243 if (pSttContentNd)
1245 const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
1246 if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
1248 pTableFormat->SetFormatAttr( *pItem );
1252 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
1253 //until after RegisterToFormat is completed
1254 bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
1255 getIDocumentState().SetEnableSetModified(false);
1257 SwTableNode* pTableNd = GetNodes().TextToTable(
1258 rTableNodes, pTableFormat, pLineFormat, pBoxFormat );
1260 SwTable& rNdTable = pTableNd->GetTable();
1261 rNdTable.RegisterToFormat(*pTableFormat);
1263 if( !pBoxFormat->HasWriterListeners() )
1265 // The Box's Formats already have the right size, we must only set
1266 // the right Border/AutoFormat.
1267 pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
1268 delete pBoxFormat;
1271 SwNodeOffset nIdx = pTableNd->GetIndex();
1272 aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
1274 getIDocumentState().SetEnableSetModified(bEnableSetModified);
1275 getIDocumentState().SetModified();
1276 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1277 return &rNdTable;
1280 void SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange, std::optional<SwNodeRange>& rExpandedRange)
1282 bool bChanged = false;
1284 SwNodeIndex aNewStart = rRange.aStart;
1285 SwNodeIndex aNewEnd = rRange.aEnd;
1287 SwNodeIndex aEndIndex = rRange.aEnd;
1288 SwNodeIndex aIndex = rRange.aStart;
1290 while (aIndex < aEndIndex)
1292 SwNode& rNode = aIndex.GetNode();
1294 if (rNode.IsStartNode())
1296 // advance aIndex to the end node of this start node
1297 SwNode * pEndNode = rNode.EndOfSectionNode();
1298 aIndex = *pEndNode;
1300 if (aIndex > aNewEnd)
1302 aNewEnd = aIndex;
1303 bChanged = true;
1306 else if (rNode.IsEndNode())
1308 SwNode * pStartNode = rNode.StartOfSectionNode();
1309 if (pStartNode->GetIndex() < aNewStart.GetIndex())
1311 aNewStart = *pStartNode;
1312 bChanged = true;
1316 if (aIndex < aEndIndex)
1317 ++aIndex;
1320 SwNode * pNode = &aIndex.GetNode();
1321 while (pNode->IsEndNode() && aIndex < Count() - 1)
1323 SwNode * pStartNode = pNode->StartOfSectionNode();
1324 aNewStart = *pStartNode;
1325 aNewEnd = aIndex;
1326 bChanged = true;
1328 ++aIndex;
1329 pNode = &aIndex.GetNode();
1332 if (bChanged)
1333 rExpandedRange.emplace(aNewStart, aNewEnd);
1336 static void
1337 lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
1338 SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
1340 // rhbz#820283, fdo#55462: set default box widths so table width is covered
1341 SwTableLines & rLines = rTable.GetTabLines();
1342 for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
1344 SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
1345 assert(!rBoxes.empty()); // ensured by convertToTable
1346 size_t const nMissing = nMaxBoxes - rBoxes.size();
1347 if (nMissing)
1349 // default width for box at the end of an incomplete line
1350 SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
1351 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
1352 pNewFormat->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable,
1353 nWidth * (nMissing + 1)) );
1354 pNewFormat->Add(rBoxes.back());
1357 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
1358 // default width for all boxes not at the end of an incomplete line
1359 rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
1362 SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
1363 SwTableFormat* pTableFormat,
1364 SwTableLineFormat* pLineFormat,
1365 SwTableBoxFormat* pBoxFormat )
1367 if( rTableNodes.empty() )
1368 return nullptr;
1370 SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart.GetNode() );
1371 //insert the end node after the last text node
1372 SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
1373 ++aInsertIndex;
1375 //!! ownership will be transferred in c-tor to SwNodes array.
1376 //!! Thus no real problem here...
1377 new SwEndNode( aInsertIndex.GetNode(), *pTableNd );
1379 SwDoc& rDoc = GetDoc();
1380 SwTable& rTable = pTableNd->GetTable();
1381 SwTableBox* pBox;
1382 sal_uInt16 nLines, nMaxBoxes = 0;
1384 SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
1385 // delete frames of all contained content nodes
1386 for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
1388 SwNode& rNode = aNodeIndex.GetNode();
1389 if( rNode.IsContentNode() )
1391 lcl_RemoveBreaks(static_cast<SwContentNode&>(rNode),
1392 (0 == nLines) ? pTableFormat : nullptr);
1396 nLines = 0;
1397 for( const auto& rRow : rTableNodes )
1399 sal_uInt16 nBoxes = 0;
1400 SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
1401 rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
1403 for( const auto& rCell : rRow )
1405 SwNodeIndex aCellEndIdx(rCell.aEnd);
1406 ++aCellEndIdx;
1407 SwStartNode* pSttNd = new SwStartNode( rCell.aStart.GetNode(), SwNodeType::Start,
1408 SwTableBoxStartNode );
1410 // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
1411 // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
1412 // So this statement is only executed for the side-effect.
1413 new SwEndNode( aCellEndIdx.GetNode(), *pSttNd );
1415 //set the start node on all node of the current cell
1416 SwNodeIndex aCellNodeIdx = rCell.aStart;
1417 for(;aCellNodeIdx <= rCell.aEnd; ++aCellNodeIdx )
1419 aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
1420 //skip start/end node pairs
1421 if( aCellNodeIdx.GetNode().IsStartNode() )
1422 aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
1425 // assign Section to the Box
1426 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1427 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1429 if( nMaxBoxes < nBoxes )
1430 nMaxBoxes = nBoxes;
1432 nLines++;
1435 lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);
1437 return pTableNd;
1441 * Table to Text
1443 bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
1445 if( !pTableNd )
1446 return false;
1448 // #i34471#
1449 // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
1450 // the table cursor.
1451 SwEditShell* pESh = GetEditShell();
1452 if (pESh && pESh->IsTableMode())
1453 pESh->ClearMark();
1455 SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
1456 std::unique_ptr<SwUndoTableToText> pUndo;
1457 SwNodeRange* pUndoRg = nullptr;
1458 if (GetIDocumentUndoRedo().DoesUndo())
1460 GetIDocumentUndoRedo().ClearRedo();
1461 pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
1462 pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
1465 const_cast<SwTable*>(&pTableNd->GetTable())->SwitchFormulasToExternalRepresentation();
1467 bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
1468 if( pUndoRg )
1470 ++pUndoRg->aStart;
1471 --pUndoRg->aEnd;
1472 pUndo->SetRange( *pUndoRg );
1473 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
1474 delete pUndoRg;
1477 if( bRet )
1478 getIDocumentState().SetModified();
1480 return bRet;
1483 namespace {
1486 * Use the ForEach method from PtrArray to recreate Text from a Table.
1487 * The Boxes can also contain Lines!
1489 struct DelTabPara
1491 SwTextNode* pLastNd;
1492 SwNodes& rNds;
1493 SwUndoTableToText* pUndo;
1494 sal_Unicode cCh;
1496 DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
1497 pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
1502 // Forward declare so that the Lines and Boxes can use recursion
1503 static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );
1505 static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
1507 assert(pPara && "The parameters are missing!");
1508 DelTabPara aPara( *pPara );
1509 for( auto& rpBox : pLine->GetTabBoxes() )
1510 lcl_DelBox(rpBox, &aPara );
1511 if( pLine->GetUpper() ) // Is there a parent Box?
1512 // Return the last TextNode
1513 pPara->pLastNd = aPara.pLastNd;
1516 static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
1518 assert(pDelPara && "The parameters are missing");
1520 // Delete the Box's Lines
1521 if( !pBox->GetTabLines().empty() )
1523 for( SwTableLine* pLine : pBox->GetTabLines() )
1524 lcl_DelLine( pLine, pDelPara );
1526 else
1528 SwDoc& rDoc = pDelPara->rNds.GetDoc();
1529 SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(0),
1530 *pBox->GetSttNd()->EndOfSectionNode() );
1531 // Delete the Section
1532 pDelPara->rNds.SectionUp( &aDelRg );
1533 const SwTextNode* pCurTextNd = nullptr;
1534 if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
1535 pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
1536 if (nullptr != pCurTextNd)
1538 // Join the current text node with the last from the previous box if possible
1539 SwNodeOffset nNdIdx = aDelRg.aStart.GetIndex();
1540 --aDelRg.aStart;
1541 if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
1543 // Inserting the separator
1544 SwContentIndex aCntIdx( pDelPara->pLastNd,
1545 pDelPara->pLastNd->GetText().getLength());
1546 pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
1547 SwInsertFlags::EMPTYEXPAND );
1548 if( pDelPara->pUndo )
1549 pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
1550 aCntIdx.GetIndex() );
1552 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1553 const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
1554 pContentStore->Save(rDoc, nNdIdx, SAL_MAX_INT32);
1556 pDelPara->pLastNd->JoinNext();
1558 if( !pContentStore->Empty() )
1559 pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
1561 else if( pDelPara->pUndo )
1563 ++aDelRg.aStart;
1564 pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
1567 else if( pDelPara->pUndo )
1568 pDelPara->pUndo->AddBoxPos( rDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
1569 --aDelRg.aEnd;
1570 pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();
1572 // Do not take over the NumberFormatting's adjustment
1573 if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
1574 pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
1578 bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
1579 SwUndoTableToText* pUndo )
1581 // Is a Table selected?
1582 if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
1583 return false;
1584 SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
1585 if (nullptr == pTableNd ||
1586 &rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
1587 return false;
1589 // If the Table was alone in a Section, create the Frames via the Table's Upper
1590 std::optional<SwNode2LayoutSaveUpperFrames> oNode2Layout;
1591 SwNode* pFrameNd = FindPrvNxtFrameNode( rRange.aStart.GetNode(), &rRange.aEnd.GetNode() );
1592 SwNodeIndex aFrameIdx( pFrameNd ? *pFrameNd: rRange.aStart.GetNode() );
1593 if( !pFrameNd )
1594 // Collect all Uppers
1595 oNode2Layout.emplace(*pTableNd);
1597 // Delete the Frames
1598 pTableNd->DelFrames();
1600 // "Delete" the Table and merge all Lines/Boxes
1601 DelTabPara aDelPara( *this, cCh, pUndo );
1602 for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
1603 lcl_DelLine( pLine, &aDelPara );
1605 // We just created a TextNode with fitting separator for every TableLine.
1606 // Now we only need to delete the TableSection and create the Frames for the
1607 // new TextNode.
1608 SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );
1610 // If the Table has PageDesc/Break Attributes, carry them over to the
1611 // first Text Node
1613 // What about UNDO?
1614 const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
1615 const SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
1616 const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );
1618 if( pBreak || pDesc )
1620 SwNodeIndex aIdx( *pTableNd );
1621 SwContentNode* pCNd = GoNext( &aIdx );
1622 if( pBreak )
1623 pCNd->SetAttr( *pBreak );
1624 if( pDesc )
1625 pCNd->SetAttr( *pDesc );
1629 SectionUp( &aDelRg ); // Delete this Section and by that the Table
1630 // #i28006#
1631 SwNodeOffset nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
1632 if( !pFrameNd )
1634 oNode2Layout->RestoreUpperFrames( *this,
1635 aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
1636 oNode2Layout.reset();
1638 else
1640 SwContentNode *pCNd;
1641 SwSectionNode *pSNd;
1642 while( aDelRg.aStart.GetIndex() < nEnd )
1644 pCNd = aDelRg.aStart.GetNode().GetContentNode();
1645 if( nullptr != pCNd )
1647 if( pFrameNd->IsContentNode() )
1648 static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
1649 else if( pFrameNd->IsTableNode() )
1650 static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
1651 else if( pFrameNd->IsSectionNode() )
1652 static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
1653 pFrameNd = pCNd;
1655 else
1657 pSNd = aDelRg.aStart.GetNode().GetSectionNode();
1658 if( pSNd )
1660 if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
1662 pSNd->MakeOwnFrames(&aFrameIdx, &aDelRg.aEnd);
1663 break;
1665 aDelRg.aStart = *pSNd->EndOfSectionNode();
1668 ++aDelRg.aStart;
1672 // #i28006# Fly frames have to be restored even if the table was
1673 // #alone in the section
1674 for(sw::SpzFrameFormat* pFly: *GetDoc().GetSpzFrameFormats())
1676 SwFrameFormat *const pFormat = pFly;
1677 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1678 SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
1679 if (pAnchorNode &&
1680 ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
1681 (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
1682 nStt <= pAnchorNode->GetIndex() &&
1683 pAnchorNode->GetIndex() < nEnd )
1685 pFormat->MakeFrames();
1689 return true;
1693 * Inserting Columns/Rows
1695 void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
1697 if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
1698 return;
1700 // Find the Boxes via the Layout
1701 SwSelBoxes aBoxes;
1702 ::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
1704 if( !aBoxes.empty() )
1705 InsertCol( aBoxes, nCnt, bBehind );
1708 bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
1710 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
1711 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
1712 if( !pTableNd )
1713 return false;
1715 SwTable& rTable = pTableNd->GetTable();
1716 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
1717 return false;
1719 SwTableSortBoxes aTmpLst;
1720 std::unique_ptr<SwUndoTableNdsChg> pUndo;
1721 if (GetIDocumentUndoRedo().DoesUndo())
1723 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL, rBoxes, *pTableNd,
1724 0, 0, nCnt, bBehind, false ));
1725 aTmpLst.insert( rTable.GetTabSortBoxes() );
1728 bool bRet(false);
1730 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
1732 rTable.SwitchFormulasToInternalRepresentation();
1733 bRet = rTable.InsertCol(*this, rBoxes, nCnt, bBehind, bInsertDummy);
1734 if (bRet)
1736 getIDocumentState().SetModified();
1737 ::ClearFEShellTabCols(*this, nullptr);
1738 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1742 if( pUndo && bRet )
1744 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
1745 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1747 return bRet;
1750 void SwDoc::InsertRow( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
1752 // Find the Boxes via the Layout
1753 SwSelBoxes aBoxes;
1754 GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
1756 if( !aBoxes.empty() )
1757 InsertRow( aBoxes, nCnt, bBehind );
1760 bool SwDoc::InsertRow( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
1762 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
1763 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
1764 if( !pTableNd )
1765 return false;
1767 SwTable& rTable = pTableNd->GetTable();
1768 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
1769 return false;
1771 SwTableSortBoxes aTmpLst;
1772 std::unique_ptr<SwUndoTableNdsChg> pUndo;
1773 if (GetIDocumentUndoRedo().DoesUndo())
1775 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW,rBoxes, *pTableNd,
1776 0, 0, nCnt, bBehind, false ));
1777 aTmpLst.insert( rTable.GetTabSortBoxes() );
1780 bool bRet(false);
1782 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
1783 rTable.SwitchFormulasToInternalRepresentation();
1785 bRet = rTable.InsertRow( this, rBoxes, nCnt, bBehind, bInsertDummy );
1786 if (bRet)
1788 getIDocumentState().SetModified();
1789 ::ClearFEShellTabCols(*this, nullptr);
1790 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1794 if( pUndo && bRet )
1796 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
1797 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1799 return bRet;
1804 * Deleting Columns/Rows
1806 void SwDoc::DeleteRow( const SwCursor& rCursor )
1808 // Find the Boxes via the Layout
1809 SwSelBoxes aBoxes;
1810 GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
1811 if( ::HasProtectedCells( aBoxes ))
1812 return;
1814 // Remove the Cursor from the to-be-deleted Section.
1815 // The Cursor is placed after the table, except for
1816 // - when there's another Line, we place it in that one
1817 // - when a Line precedes it, we place it in that one
1819 SwTableNode* pTableNd = rCursor.GetPointNode().FindTableNode();
1821 if(dynamic_cast<const SwDDETable*>( & pTableNd->GetTable()) != nullptr)
1822 return;
1824 // Find all Boxes/Lines
1825 FndBox_ aFndBox( nullptr, nullptr );
1827 FndPara aPara( aBoxes, &aFndBox );
1828 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
1831 if( aFndBox.GetLines().empty() )
1832 return;
1834 if (SwEditShell* pESh = GetEditShell())
1836 pESh->KillPams();
1837 // FIXME: actually we should be iterating over all Shells!
1840 FndBox_* pFndBox = &aFndBox;
1841 while( 1 == pFndBox->GetLines().size() &&
1842 1 == pFndBox->GetLines().front()->GetBoxes().size() )
1844 FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
1845 if( pTmp->GetBox()->GetSttNd() )
1846 break; // Else it gets too far
1847 pFndBox = pTmp;
1850 SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
1851 SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
1852 while( !pDelBox->GetSttNd() )
1854 SwTableLine* pLn = pDelBox->GetTabLines()[
1855 pDelBox->GetTabLines().size()-1 ];
1856 pDelBox = pLn->GetTabBoxes().back();
1858 SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
1859 pDelBox );
1860 while( pNextBox &&
1861 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
1862 pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
1864 if( !pNextBox ) // No succeeding Boxes? Then take the preceding one
1866 pDelLine = pFndBox->GetLines().front()->GetLine();
1867 pDelBox = pDelLine->GetTabBoxes()[ 0 ];
1868 while( !pDelBox->GetSttNd() )
1869 pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
1870 pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
1871 pDelBox );
1872 while( pNextBox &&
1873 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
1874 pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
1877 SwNodeOffset nIdx;
1878 if( pNextBox ) // Place the Cursor here
1879 nIdx = pNextBox->GetSttIdx() + 1;
1880 else // Else after the Table
1881 nIdx = pTableNd->EndOfSectionIndex() + 1;
1883 SwNodeIndex aIdx( GetNodes(), nIdx );
1884 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1885 if( !pCNd )
1886 pCNd = GetNodes().GoNext( &aIdx );
1888 if( pCNd )
1890 // Change the Shell's Cursor or the one passed?
1891 SwPaM* pPam = const_cast<SwPaM*>(static_cast<SwPaM const *>(&rCursor));
1892 pPam->GetPoint()->Assign(aIdx);
1893 pPam->SetMark(); // Both want a part of it
1894 pPam->DeleteMark();
1898 // Thus delete the Rows
1899 GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE, nullptr);
1900 DeleteRowCol( aBoxes );
1901 GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE, nullptr);
1904 void SwDoc::DeleteCol( const SwCursor& rCursor )
1906 // Find the Boxes via the Layout
1907 SwSelBoxes aBoxes;
1908 GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
1909 if( ::HasProtectedCells( aBoxes ))
1910 return;
1912 // The Cursors need to be removed from the to-be-deleted range.
1913 // Always place them after/on top of the Table; they are always set
1914 // to the old position via the document position.
1915 if (SwEditShell* pESh = GetEditShell())
1917 const SwNode* pNd = rCursor.GetPointNode().FindTableBoxStartNode();
1918 pESh->ParkCursor( *pNd );
1921 // Thus delete the Columns
1922 GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
1923 DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
1924 GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
1927 void SwDoc::DelTable(SwTableNode *const pTableNd)
1930 // tdf#156267 remove DdeBookmarks before deleting nodes
1931 SwPaM aTmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
1932 SwDataChanged aTmp(aTmpPaM);
1935 bool bNewTextNd = false;
1936 // Is it alone in a FlyFrame?
1937 SwNodeIndex aIdx( *pTableNd, -1 );
1938 const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode();
1939 if (pSttNd)
1941 const SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex() + 1;
1942 const SwNodeOffset nSectEnd = pSttNd->EndOfSectionIndex();
1943 if (nTableEnd == nSectEnd)
1945 if (SwFlyStartNode == pSttNd->GetStartNodeType())
1947 SwFrameFormat* pFormat = pSttNd->GetFlyFormat();
1948 if (pFormat)
1950 // That's the FlyFormat we're looking for
1951 getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
1952 return;
1955 // No Fly? Thus Header or Footer: always leave a TextNode
1956 // We can forget about Undo then!
1957 bNewTextNd = true;
1961 // No Fly? Then it is a Header or Footer, so keep always a TextNode
1962 ++aIdx;
1963 if (GetIDocumentUndoRedo().DoesUndo())
1965 GetIDocumentUndoRedo().ClearRedo();
1966 SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() );
1968 if (bNewTextNd)
1970 const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
1971 GetNodes().MakeTextNode( aTmpIdx.GetNode(),
1972 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
1975 // Save the cursors (UNO and otherwise)
1976 SwPaM const* pSavePaM(nullptr);
1977 SwPaM forwardPaM(*pTableNd->EndOfSectionNode());
1978 if (forwardPaM.Move(fnMoveForward, GoInNode))
1980 pSavePaM = &forwardPaM;
1982 SwPaM backwardPaM(*pTableNd);
1983 if (backwardPaM.Move(fnMoveBackward, GoInNode))
1985 if (pSavePaM == nullptr
1986 // try to stay in the same outer table cell
1987 || (forwardPaM.GetPoint()->GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode()
1988 && forwardPaM.GetPoint()->GetNode().StartOfSectionIndex()
1989 < backwardPaM.GetPoint()->GetNode().StartOfSectionIndex()))
1991 pSavePaM = &backwardPaM;
1994 assert(pSavePaM); // due to bNewTextNd this must succeed
1996 SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
1997 ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint());
2000 // Move hard PageBreaks to the succeeding Node
2001 bool bSavePageBreak = false, bSavePageDesc = false;
2002 SwNodeOffset nNextNd = pTableNd->EndOfSectionIndex()+1;
2003 SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode();
2004 if (pNextNd)
2006 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2007 const SfxPoolItem *pItem;
2008 if (SfxItemState::SET == pTableFormat->GetItemState(RES_PAGEDESC,
2009 false, &pItem))
2011 pNextNd->SetAttr( *pItem );
2012 bSavePageDesc = true;
2015 if (SfxItemState::SET == pTableFormat->GetItemState(RES_BREAK,
2016 false, &pItem))
2018 pNextNd->SetAttr( *pItem );
2019 bSavePageBreak = true;
2022 std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default));
2023 if (bNewTextNd)
2024 pUndo->SetTableDelLastNd();
2025 pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2026 pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
2027 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2029 else
2031 if (bNewTextNd)
2033 const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
2034 GetNodes().MakeTextNode( aTmpIdx.GetNode(),
2035 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
2038 // Save the cursors (UNO and otherwise)
2039 SwPaM const* pSavePaM(nullptr);
2040 SwPaM forwardPaM(*pTableNd->EndOfSectionNode());
2041 if (forwardPaM.Move(fnMoveForward, GoInNode))
2043 pSavePaM = &forwardPaM;
2045 SwPaM backwardPaM(*pTableNd);
2046 if (backwardPaM.Move(fnMoveBackward, GoInNode))
2048 if (pSavePaM == nullptr
2049 // try to stay in the same outer table cell
2050 || (forwardPaM.GetPoint()->GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode()
2051 && forwardPaM.GetPoint()->GetNode().StartOfSectionIndex()
2052 < backwardPaM.GetPoint()->GetNode().StartOfSectionIndex()))
2054 pSavePaM = &backwardPaM;
2057 assert(pSavePaM); // due to bNewTextNd this must succeed
2059 SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
2060 ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint());
2063 // Move hard PageBreaks to the succeeding Node
2064 SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
2065 if (pNextNd)
2067 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2068 const SfxPoolItem *pItem;
2069 if (SfxItemState::SET == pTableFormat->GetItemState(RES_PAGEDESC,
2070 false, &pItem))
2072 pNextNd->SetAttr( *pItem );
2075 if (SfxItemState::SET == pTableFormat->GetItemState(RES_BREAK,
2076 false, &pItem))
2078 pNextNd->SetAttr( *pItem );
2082 pTableNd->DelFrames();
2083 getIDocumentContentOperations().DeleteSection( pTableNd );
2086 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2087 pFEShell->UpdateTableStyleFormatting();
2089 getIDocumentState().SetModified();
2090 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2093 bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode)
2095 if (!(eMode & SwDoc::RowColMode::DeleteProtected)
2096 && ::HasProtectedCells(rBoxes))
2098 return false;
2101 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
2102 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
2103 if( !pTableNd )
2104 return false;
2106 if (!(eMode & SwDoc::RowColMode::DeleteProtected)
2107 && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr)
2109 return false;
2112 ::ClearFEShellTabCols(*this, nullptr);
2113 SwSelBoxes aSelBoxes( rBoxes );
2114 SwTable &rTable = pTableNd->GetTable();
2115 tools::Long nMin = 0;
2116 tools::Long nMax = 0;
2117 if( rTable.IsNewModel() )
2119 if (eMode & SwDoc::RowColMode::DeleteColumn)
2120 rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
2121 else
2122 rTable.FindSuperfluousRows( aSelBoxes );
2125 // Are we deleting the whole Table?
2126 const SwNodeOffset nTmpIdx1 = pTableNd->GetIndex();
2127 const SwNodeOffset nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1;
2128 if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() &&
2129 aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 &&
2130 nTmpIdx2 == pTableNd->EndOfSectionIndex() )
2132 DelTable(pTableNd);
2133 return true;
2136 std::unique_ptr<SwUndoTableNdsChg> pUndo;
2137 if (GetIDocumentUndoRedo().DoesUndo())
2139 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX, aSelBoxes, *pTableNd,
2140 nMin, nMax, 0, false, false ));
2143 bool bRet(false);
2145 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2146 rTable.SwitchFormulasToInternalRepresentation();
2148 if (rTable.IsNewModel())
2150 if (eMode & SwDoc::RowColMode::DeleteColumn)
2151 rTable.PrepareDeleteCol( nMin, nMax );
2152 rTable.FindSuperfluousRows( aSelBoxes );
2153 if (pUndo)
2154 pUndo->ReNewBoxes( aSelBoxes );
2156 bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true );
2157 if (bRet)
2159 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2160 pFEShell->UpdateTableStyleFormatting();
2162 getIDocumentState().SetModified();
2163 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2167 if( pUndo && bRet )
2169 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2172 return bRet;
2176 * Split up/merge Boxes in the Table
2178 bool SwDoc::SplitTable( const SwSelBoxes& rBoxes, bool bVert, sal_uInt16 nCnt,
2179 bool bSameHeight )
2181 OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box list" );
2182 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
2183 if( !pTableNd )
2184 return false;
2186 SwTable& rTable = pTableNd->GetTable();
2187 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
2188 return false;
2190 std::vector<SwNodeOffset> aNdsCnts;
2191 SwTableSortBoxes aTmpLst;
2192 std::unique_ptr<SwUndoTableNdsChg> pUndo;
2193 if (GetIDocumentUndoRedo().DoesUndo())
2195 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT, rBoxes, *pTableNd, 0, 0,
2196 nCnt, bVert, bSameHeight ));
2198 aTmpLst.insert( rTable.GetTabSortBoxes() );
2199 if( !bVert )
2201 for (size_t n = 0; n < rBoxes.size(); ++n)
2203 const SwStartNode* pSttNd = rBoxes[ n ]->GetSttNd();
2204 aNdsCnts.push_back( pSttNd->EndOfSectionIndex() -
2205 pSttNd->GetIndex() );
2210 bool bRet(false);
2212 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2213 rTable.SwitchFormulasToInternalRepresentation();
2215 if (bVert)
2216 bRet = rTable.SplitCol(*this, rBoxes, nCnt);
2217 else
2218 bRet = rTable.SplitRow(*this, rBoxes, nCnt, bSameHeight);
2220 if (bRet)
2222 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2223 pFEShell->UpdateTableStyleFormatting();
2225 getIDocumentState().SetModified();
2226 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2230 if( pUndo && bRet )
2232 if( bVert )
2233 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
2234 else
2235 pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
2236 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2239 return bRet;
2242 TableMergeErr SwDoc::MergeTable( SwPaM& rPam )
2244 // Check if the current cursor's Point/Mark are inside a Table
2245 SwTableNode* pTableNd = rPam.GetPointNode().FindTableNode();
2246 if( !pTableNd )
2247 return TableMergeErr::NoSelection;
2248 SwTable& rTable = pTableNd->GetTable();
2249 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr )
2250 return TableMergeErr::NoSelection;
2251 TableMergeErr nRet = TableMergeErr::NoSelection;
2252 if( !rTable.IsNewModel() )
2254 nRet =::CheckMergeSel( rPam );
2255 if( TableMergeErr::Ok != nRet )
2256 return nRet;
2257 nRet = TableMergeErr::NoSelection;
2260 // #i33394#
2261 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE, nullptr );
2263 RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
2264 getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
2266 std::unique_ptr<SwUndoTableMerge> pUndo;
2267 if (GetIDocumentUndoRedo().DoesUndo())
2268 pUndo.reset(new SwUndoTableMerge( rPam ));
2270 // Find the Boxes via the Layout
2271 SwSelBoxes aBoxes;
2272 SwSelBoxes aMerged;
2273 SwTableBox* pMergeBox;
2275 if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo.get() ) )
2276 { // No cells found to merge
2277 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
2278 if( pUndo )
2280 pUndo.reset();
2281 SwUndoId nLastUndoId(SwUndoId::EMPTY);
2282 if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
2283 && (SwUndoId::REDLINE == nLastUndoId))
2285 // FIXME: why is this horrible cleanup necessary?
2286 SwUndoRedline *const pU = dynamic_cast<SwUndoRedline*>(
2287 GetUndoManager().RemoveLastUndo());
2288 if (pU && pU->GetRedlSaveCount())
2290 SwEditShell *const pEditShell(GetEditShell());
2291 assert(pEditShell);
2292 ::sw::UndoRedoContext context(*this, *pEditShell);
2293 static_cast<SfxUndoAction *>(pU)->UndoWithContext(context);
2295 delete pU;
2299 else
2301 // The PaMs need to be removed from the to-be-deleted range. Thus always place
2302 // them at the end of/on top of the Table; it's always set to the old position via
2303 // the Document Position.
2304 // For a start remember an index for the temporary position, because we cannot
2305 // access it after GetMergeSel
2307 rPam.DeleteMark();
2308 rPam.GetPoint()->Assign(*pMergeBox->GetSttNd());
2309 rPam.SetMark();
2310 rPam.DeleteMark();
2312 SwPaM* pTmp = &rPam;
2313 while( &rPam != ( pTmp = pTmp->GetNext() ))
2314 for( int i = 0; i < 2; ++i )
2315 pTmp->GetBound( static_cast<bool>(i) ) = *rPam.GetPoint();
2317 if (SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(&rPam))
2319 // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
2320 // to not contain the soon to-be-deleted SwTableBox so if the rPam
2321 // is queried via a11y it doesn't claim the deleted cell still
2322 // exists
2323 pTableCursor->NewTableSelection();
2327 // Merge them
2328 pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
2330 if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo.get() ))
2332 nRet = TableMergeErr::Ok;
2334 getIDocumentState().SetModified();
2335 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2336 if( pUndo )
2338 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2342 rPam.GetPoint()->Assign( *pMergeBox->GetSttNd() );
2343 rPam.Move();
2345 ::ClearFEShellTabCols(*this, nullptr);
2346 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
2348 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE, nullptr );
2349 return nRet;
2352 SwTableNode::SwTableNode( const SwNode& rWhere )
2353 : SwStartNode( rWhere, SwNodeType::Table )
2355 m_pTable.reset(new SwTable);
2358 SwTableNode::~SwTableNode()
2360 // Notify UNO wrappers
2361 GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
2362 DelFrames();
2363 m_pTable->SetTableNode(this); // set this so that ~SwDDETable can read it!
2364 m_pTable.reset();
2367 SwTabFrame *SwTableNode::MakeFrame( SwFrame* pSib )
2369 return new SwTabFrame( *m_pTable, pSib );
2373 * Creates all Views from the Document for the preceding Node. The resulting ContentFrames
2374 * are added to the corresponding Layout.
2376 void SwTableNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
2378 if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
2379 return;
2381 SwFrame *pFrame;
2382 SwContentNode * pNode = rIdx.GetNode().GetContentNode();
2384 OSL_ENSURE( pNode, "No ContentNode or CopyNode and new Node is identical");
2386 bool bBefore = rIdx < GetIndex();
2388 SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
2390 while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
2392 if ( ( pFrame->getRootFrame()->HasMergedParas() &&
2393 !pNode->IsCreateFrameWhenHidingRedlines() ) ||
2394 // tdf#153819 table deletion with change tracking:
2395 // table node without frames in Hide Changes mode
2396 !pFrame->GetUpper() )
2398 continue;
2400 SwFrame *pNew = pNode->MakeFrame( pFrame );
2401 // Will the Node receive Frames before or after?
2402 if ( bBefore )
2403 // The new one precedes me
2404 pNew->Paste( pFrame->GetUpper(), pFrame );
2405 else
2406 // The new one succeeds me
2407 pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
2412 * Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
2414 void SwTableNode::MakeOwnFrames(SwPosition* pIdxBehind)
2416 SwNode *pNd = GetNodes().FindPrvNxtFrameNode( *this, EndOfSectionNode() );
2417 if( !pNd )
2419 if (pIdxBehind)
2420 pIdxBehind->Assign(*this);
2421 return;
2423 if (pIdxBehind)
2424 pIdxBehind->Assign(*pNd);
2426 SwFrame *pFrame( nullptr );
2427 SwLayoutFrame *pUpper( nullptr );
2428 SwNode2Layout aNode2Layout( *pNd, GetIndex() );
2429 while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, *this )) )
2431 if (pUpper->getRootFrame()->HasMergedParas()
2432 && !IsCreateFrameWhenHidingRedlines())
2434 continue;
2436 SwTabFrame* pNew = MakeFrame( pUpper );
2437 pNew->Paste( pUpper, pFrame );
2438 // #i27138#
2439 // notify accessibility paragraphs objects about changed
2440 // CONTENT_FLOWS_FROM/_TO relation.
2441 // Relation CONTENT_FLOWS_FROM for next paragraph will change
2442 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
2443 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2445 SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
2446 if ( pViewShell && pViewShell->GetLayout() &&
2447 pViewShell->GetLayout()->IsAnyShellAccessible() )
2449 auto pNext = pNew->FindNextCnt( true );
2450 auto pPrev = pNew->FindPrevCnt();
2451 pViewShell->InvalidateAccessibleParaFlowRelation(
2452 pNext ? pNext->DynCastTextFrame() : nullptr,
2453 pPrev ? pPrev->DynCastTextFrame() : nullptr );
2456 #endif
2457 pNew->RegistFlys();
2461 void SwTableNode::DelFrames(SwRootFrame const*const pLayout)
2463 /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
2464 The TabFrames are attached to the FrameFormat of the SwTable.
2465 We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
2467 SwIterator<SwTabFrame,SwFormat> aIter( *(m_pTable->GetFrameFormat()) );
2468 SwTabFrame *pFrame = aIter.First();
2469 while ( pFrame )
2471 bool bAgain = false;
2473 if (!pFrame->IsFollow() && (!pLayout || pLayout == pFrame->getRootFrame()))
2475 while ( pFrame->HasFollow() )
2476 pFrame->JoinAndDelFollows();
2477 // #i27138#
2478 // notify accessibility paragraphs objects about changed
2479 // CONTENT_FLOWS_FROM/_TO relation.
2480 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
2481 // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
2482 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2483 if (!GetDoc().IsInDtor())
2485 SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
2486 if ( pViewShell && pViewShell->GetLayout() &&
2487 pViewShell->GetLayout()->IsAnyShellAccessible() )
2489 auto pNext = pFrame->FindNextCnt( true );
2490 auto pPrev = pFrame->FindPrevCnt();
2491 pViewShell->InvalidateAccessibleParaFlowRelation(
2492 pNext ? pNext->DynCastTextFrame() : nullptr,
2493 pPrev ? pPrev->DynCastTextFrame() : nullptr );
2496 #endif
2497 if (pFrame->GetUpper())
2498 pFrame->Cut();
2499 SwFrame::DestroyFrame(pFrame);
2500 bAgain = true;
2503 pFrame = bAgain ? aIter.First() : aIter.Next();
2507 void SwTableNode::SetNewTable( std::unique_ptr<SwTable> pNewTable, bool bNewFrames )
2509 DelFrames();
2510 m_pTable->SetTableNode(this);
2511 m_pTable = std::move(pNewTable);
2512 if( bNewFrames )
2514 MakeOwnFrames();
2518 void SwTableNode::RemoveRedlines()
2520 SwDoc& rDoc = GetDoc();
2521 SwTable& rTable = GetTable();
2522 rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc, rTable, true, RedlineType::Any);
2525 void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter) const
2527 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableNode"));
2528 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
2529 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
2531 if (m_pTable)
2533 m_pTable->dumpAsXml(pWriter);
2536 // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
2539 void SwDoc::GetTabCols( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
2541 OSL_ENSURE( pBoxFrame, "pBoxFrame needs to be specified!" );
2542 if( !pBoxFrame )
2543 return;
2545 SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2546 const SwTableBox* pBox = pBoxFrame->GetTabBox();
2548 // Set fixed points, LeftMin in Document coordinates, all others relative
2549 SwRectFnSet aRectFnSet(pTab);
2550 const SwPageFrame* pPage = pTab->FindPageFrame();
2551 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
2552 aRectFnSet.GetLeft(pPage->getFrameArea());
2553 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
2554 aRectFnSet.GetLeft(pPage->getFrameArea());
2556 rFill.SetLeftMin ( nLeftMin );
2557 rFill.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
2558 rFill.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
2559 rFill.SetRightMax( nRightMax - nLeftMin );
2561 pTab->GetTable()->GetTabCols( rFill, pBox );
2564 // Here are some little helpers used in SwDoc::GetTabRows
2566 #define ROWFUZZY 25
2568 namespace {
2570 struct FuzzyCompare
2572 bool operator() ( tools::Long s1, tools::Long s2 ) const;
2577 bool FuzzyCompare::operator() ( tools::Long s1, tools::Long s2 ) const
2579 return ( s1 < s2 && std::abs( s1 - s2 ) > ROWFUZZY );
2582 static bool lcl_IsFrameInColumn( const SwCellFrame& rFrame, SwSelBoxes const & rBoxes )
2584 for (size_t i = 0; i < rBoxes.size(); ++i)
2586 if ( rFrame.GetTabBox() == rBoxes[ i ] )
2587 return true;
2590 return false;
2593 void SwDoc::GetTabRows( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
2595 OSL_ENSURE( pBoxFrame, "GetTabRows called without pBoxFrame" );
2597 // Make code robust:
2598 if ( !pBoxFrame )
2599 return;
2601 // #i39552# Collection of the boxes of the current
2602 // column has to be done at the beginning of this function, because
2603 // the table may be formatted in ::GetTableSel.
2604 SwDeletionChecker aDelCheck( pBoxFrame );
2606 SwSelBoxes aBoxes;
2607 const SwContentFrame* pContent = ::GetCellContent( *pBoxFrame );
2608 if ( pContent && pContent->IsTextFrame() )
2610 const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
2611 const SwCursor aTmpCursor( aPos, nullptr );
2612 ::GetTableSel( aTmpCursor, aBoxes, SwTableSearchType::Col );
2615 // Make code robust:
2616 if ( aDelCheck.HasBeenDeleted() )
2618 OSL_FAIL( "Current box has been deleted during GetTabRows()" );
2619 return;
2622 // Make code robust:
2623 const SwTabFrame* pTab = pBoxFrame->FindTabFrame();
2624 OSL_ENSURE( pTab, "GetTabRows called without a table" );
2625 if ( !pTab )
2626 return;
2628 const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
2630 // Set fixed points, LeftMin in Document coordinates, all others relative
2631 SwRectFnSet aRectFnSet(pTab);
2632 const SwPageFrame* pPage = pTab->FindPageFrame();
2633 const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
2634 pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
2635 pTab->GetPrtTop() - pPage->getFrameArea().Top() );
2636 const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
2637 const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
2638 const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
2640 rFill.SetLeftMin( nLeftMin );
2641 rFill.SetLeft( nLeft );
2642 rFill.SetRight( nRight );
2643 rFill.SetRightMax( nRightMax );
2645 typedef std::map< tools::Long, std::pair< tools::Long, long >, FuzzyCompare > BoundaryMap;
2646 BoundaryMap aBoundaries;
2647 BoundaryMap::iterator aIter;
2648 std::pair< tools::Long, long > aPair;
2650 typedef std::map< tools::Long, bool > HiddenMap;
2651 HiddenMap aHidden;
2652 HiddenMap::iterator aHiddenIter;
2654 while ( pFrame && pTab->IsAnLower( pFrame ) )
2656 if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
2658 // upper and lower borders of current cell frame:
2659 tools::Long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
2660 tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
2662 // get boundaries for nUpperBorder:
2663 aIter = aBoundaries.find( nUpperBorder );
2664 if ( aIter == aBoundaries.end() )
2666 aPair.first = nUpperBorder; aPair.second = LONG_MAX;
2667 aBoundaries[ nUpperBorder ] = aPair;
2670 // get boundaries for nLowerBorder:
2671 aIter = aBoundaries.find( nLowerBorder );
2672 if ( aIter == aBoundaries.end() )
2674 aPair.first = nUpperBorder; aPair.second = LONG_MAX;
2676 else
2678 nLowerBorder = (*aIter).first;
2679 tools::Long nNewLowerBorderUpperBoundary = std::max( (*aIter).second.first, nUpperBorder );
2680 aPair.first = nNewLowerBorderUpperBoundary; aPair.second = LONG_MAX;
2682 aBoundaries[ nLowerBorder ] = aPair;
2684 // calculate hidden flags for entry nUpperBorder/nLowerBorder:
2685 tools::Long nTmpVal = nUpperBorder;
2686 for ( sal_uInt8 i = 0; i < 2; ++i )
2688 aHiddenIter = aHidden.find( nTmpVal );
2689 if ( aHiddenIter == aHidden.end() )
2690 aHidden[ nTmpVal ] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes );
2691 else
2693 if ( aHidden[ nTmpVal ] &&
2694 lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes ) )
2695 aHidden[ nTmpVal ] = false;
2697 nTmpVal = nLowerBorder;
2701 pFrame = pFrame->GetNextLayoutLeaf();
2704 // transfer calculated values from BoundaryMap and HiddenMap into rFill:
2705 size_t nIdx = 0;
2706 for ( const auto& rEntry : aBoundaries )
2708 const tools::Long nTabTop = aRectFnSet.GetPrtTop(*pTab);
2709 const tools::Long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop );
2710 const std::pair< tools::Long, long > aTmpPair = rEntry.second;
2711 const tools::Long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
2712 const tools::Long nSecond = aTmpPair.second;
2714 aHiddenIter = aHidden.find( rEntry.first );
2715 const bool bHidden = aHiddenIter != aHidden.end() && (*aHiddenIter).second;
2716 rFill.Insert( nKey, nFirst, nSecond, bHidden, nIdx++ );
2719 // delete first and last entry
2720 OSL_ENSURE( rFill.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
2721 // #i60818# There may be only one entry in rFill. Make
2722 // code robust by checking count of rFill.
2723 if ( rFill.Count() ) rFill.Remove( 0 );
2724 if ( rFill.Count() ) rFill.Remove( rFill.Count() - 1 );
2725 rFill.SetLastRowAllowedToChange( !pTab->HasFollowFlowLine() );
2728 void SwDoc::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly,
2729 const SwCellFrame* pBoxFrame )
2731 const SwTableBox* pBox = nullptr;
2732 SwTabFrame *pTab = nullptr;
2734 if( pBoxFrame )
2736 pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2737 pBox = pBoxFrame->GetTabBox();
2739 else
2741 OSL_ENSURE( false, "must specify pBoxFrame" );
2742 return ;
2745 // If the Table is still using relative values (USHRT_MAX)
2746 // we need to switch to absolute ones.
2747 SwTable& rTab = *pTab->GetTable();
2748 const SwFormatFrameSize& rTableFrameSz = rTab.GetFrameFormat()->GetFrameSize();
2749 SwRectFnSet aRectFnSet(pTab);
2750 // #i17174# - With fix for #i9040# the shadow size is taken
2751 // from the table width. Thus, add its left and right size to current table
2752 // printing area width in order to get the correct table size attribute.
2753 SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
2755 SvxShadowItem aShadow( rTab.GetFrameFormat()->GetShadow() );
2756 nPrtWidth += aShadow.CalcShadowSpace( SvxShadowItemSide::LEFT ) +
2757 aShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
2759 if( nPrtWidth != rTableFrameSz.GetWidth() )
2761 SwFormatFrameSize aSz( rTableFrameSz );
2762 aSz.SetWidth( nPrtWidth );
2763 rTab.GetFrameFormat()->SetFormatAttr( aSz );
2766 SwTabCols aOld( rNew.Count() );
2768 const SwPageFrame* pPage = pTab->FindPageFrame();
2769 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
2770 aRectFnSet.GetLeft(pPage->getFrameArea());
2771 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
2772 aRectFnSet.GetLeft(pPage->getFrameArea());
2774 // Set fixed points, LeftMin in Document coordinates, all others relative
2775 aOld.SetLeftMin ( nLeftMin );
2776 aOld.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
2777 aOld.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
2778 aOld.SetRightMax( nRightMax - nLeftMin );
2780 rTab.GetTabCols( aOld, pBox );
2781 SetTabCols(rTab, rNew, aOld, pBox, bCurRowOnly );
2784 void SwDoc::SetTabRows( const SwTabCols &rNew, bool bCurColOnly,
2785 const SwCellFrame* pBoxFrame )
2787 SwTabFrame *pTab = nullptr;
2789 if( pBoxFrame )
2791 pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2793 else
2795 OSL_ENSURE( false, "must specify pBoxFrame" );
2796 return ;
2799 // If the Table is still using relative values (USHRT_MAX)
2800 // we need to switch to absolute ones.
2801 SwRectFnSet aRectFnSet(pTab);
2802 SwTabCols aOld( rNew.Count() );
2804 // Set fixed points, LeftMin in Document coordinates, all others relative
2805 const SwPageFrame* pPage = pTab->FindPageFrame();
2807 aOld.SetRight( aRectFnSet.GetHeight(pTab->getFramePrintArea()) );
2808 tools::Long nLeftMin;
2809 if ( aRectFnSet.IsVert() )
2811 nLeftMin = pTab->GetPrtLeft() - pPage->getFrameArea().Left();
2812 aOld.SetLeft ( LONG_MAX );
2813 aOld.SetRightMax( aOld.GetRight() );
2816 else
2818 nLeftMin = pTab->GetPrtTop() - pPage->getFrameArea().Top();
2819 aOld.SetLeft ( 0 );
2820 aOld.SetRightMax( LONG_MAX );
2822 aOld.SetLeftMin ( nLeftMin );
2824 GetTabRows( aOld, pBoxFrame );
2826 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR, nullptr );
2828 // check for differences between aOld and rNew:
2829 const size_t nCount = rNew.Count();
2830 const SwTable* pTable = pTab->GetTable();
2831 OSL_ENSURE( pTable, "My colleague told me, this couldn't happen" );
2833 for ( size_t i = 0; i <= nCount; ++i )
2835 const size_t nIdxStt = aRectFnSet.IsVert() ? nCount - i : i - 1;
2836 const size_t nIdxEnd = aRectFnSet.IsVert() ? nCount - i - 1 : i;
2838 const tools::Long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
2839 const tools::Long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
2840 const tools::Long nOldRowHeight = nOldRowEnd - nOldRowStart;
2842 const tools::Long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
2843 const tools::Long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
2844 const tools::Long nNewRowHeight = nNewRowEnd - nNewRowStart;
2846 const tools::Long nDiff = nNewRowHeight - nOldRowHeight;
2847 if ( std::abs( nDiff ) >= ROWFUZZY )
2849 // For the old table model pTextFrame and pLine will be set for every box.
2850 // For the new table model pTextFrame will be set if the box is not covered,
2851 // but the pLine will be set if the box is not an overlapping box
2852 // In the new table model the row height can be adjusted,
2853 // when both variables are set.
2854 const SwTextFrame* pTextFrame = nullptr;
2855 const SwTableLine* pLine = nullptr;
2857 // Iterate over all SwCellFrames with Bottom = nOldPos
2858 const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
2859 while ( pFrame && pTab->IsAnLower( pFrame ) )
2861 if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
2863 const tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
2864 const sal_uLong nTabTop = aRectFnSet.GetPrtTop(*pTab);
2865 if ( std::abs( aRectFnSet.YInc( nTabTop, nOldRowEnd ) - nLowerBorder ) <= ROWFUZZY )
2867 if ( !bCurColOnly || pFrame == pBoxFrame )
2869 const SwFrame* pContent = ::GetCellContent( static_cast<const SwCellFrame&>(*pFrame) );
2871 if ( pContent && pContent->IsTextFrame() )
2873 const SwTableBox* pBox = static_cast<const SwCellFrame*>(pFrame)->GetTabBox();
2874 const sal_Int32 nRowSpan = pBox->getRowSpan();
2875 if( nRowSpan > 0 ) // Not overlapped
2876 pTextFrame = static_cast<const SwTextFrame*>(pContent);
2877 if( nRowSpan < 2 ) // Not overlapping for row height
2878 pLine = pBox->GetUpper();
2879 if( pLine && pTextFrame ) // always for old table model
2881 // The new row height must not to be calculated from an overlapping box
2882 SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
2883 const tools::Long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
2884 if( nNewSize != aNew.GetHeight() )
2886 aNew.SetHeight( nNewSize );
2887 if ( SwFrameSize::Variable == aNew.GetHeightSizeType() )
2888 aNew.SetHeightSizeType( SwFrameSize::Minimum );
2889 // This position must not be in an overlapped box
2890 const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
2891 const SwCursor aTmpCursor( aPos, nullptr );
2892 SetRowHeight( aTmpCursor, aNew );
2893 // For the new table model we're done, for the old one
2894 // there might be another (sub)row to adjust...
2895 if( pTable->IsNewModel() )
2896 break;
2898 pLine = nullptr;
2904 pFrame = pFrame->GetNextLayoutLeaf();
2909 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR, nullptr );
2911 ::ClearFEShellTabCols(*this, nullptr);
2915 * Direct access for UNO
2917 void SwDoc::SetTabCols(SwTable& rTab, const SwTabCols &rNew, const SwTabCols &rOld,
2918 const SwTableBox *pStart, bool bCurRowOnly )
2920 if (GetIDocumentUndoRedo().DoesUndo())
2922 GetIDocumentUndoRedo().AppendUndo(
2923 std::make_unique<SwUndoAttrTable>( *rTab.GetTableNode(), true ));
2925 rTab.SetTabCols( rNew, rOld, pStart, bCurRowOnly );
2926 ::ClearFEShellTabCols(*this, nullptr);
2927 getIDocumentState().SetModified();
2930 void SwDoc::SetRowsToRepeat( SwTable &rTable, sal_uInt16 nSet )
2932 if( nSet == rTable.GetRowsToRepeat() )
2933 return;
2935 if (GetIDocumentUndoRedo().DoesUndo())
2937 GetIDocumentUndoRedo().AppendUndo(
2938 std::make_unique<SwUndoTableHeadline>(rTable, rTable.GetRowsToRepeat(), nSet) );
2941 rTable.SetRowsToRepeat(nSet);
2942 rTable.GetFrameFormat()->CallSwClientNotify(sw::TableHeadingChange());
2943 getIDocumentState().SetModified();
2946 void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
2948 if( m_pHistory )
2949 m_pHistory->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text );
2952 void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
2954 m_aPositionArr.push_back(m_nWidth);
2955 SwTableBox* p = const_cast<SwTableBox*>(&rBox);
2956 m_Boxes.push_back(p);
2957 m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
2960 const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
2962 const SwTableBox* pRet = nullptr;
2964 if( !m_aPositionArr.empty() )
2966 std::vector<sal_uInt16>::size_type n;
2967 for( n = 0; n < m_aPositionArr.size(); ++n )
2968 if( m_aPositionArr[ n ] == m_nWidth )
2969 break;
2970 else if( m_aPositionArr[ n ] > m_nWidth )
2972 if( n )
2973 --n;
2974 break;
2977 if( n >= m_aPositionArr.size() )
2978 --n;
2980 m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
2981 pRet = m_Boxes[ n ];
2983 return pRet;
2986 bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
2988 if( !m_aPositionArr.empty() )
2990 std::vector<sal_uInt16>::size_type n;
2991 for( n = 0; n < m_aPositionArr.size(); ++n )
2993 if( m_aPositionArr[ n ] == nOffset )
2994 break;
2995 else if( m_aPositionArr[ n ] > nOffset )
2997 if( n )
2998 --n;
2999 break;
3003 m_aPositionArr.erase( m_aPositionArr.begin(), m_aPositionArr.begin() + n );
3004 m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
3006 size_t nArrSize = m_aPositionArr.size();
3007 if (nArrSize)
3009 if (nOldWidth == 0)
3010 throw o3tl::divide_by_zero();
3012 // Adapt the positions to the new Size
3013 for( n = 0; n < nArrSize; ++n )
3015 sal_uLong nSize = m_nWidth;
3016 nSize *= ( m_aPositionArr[ n ] - nOffset );
3017 nSize /= nOldWidth;
3018 m_aPositionArr[ n ] = sal_uInt16( nSize );
3022 return !m_aPositionArr.empty();
3025 bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
3027 SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
3028 if( pSplPara->IsGetValues() )
3029 for( const auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
3030 sw_Box_CollectBox(rpBox, pSplPara );
3031 else
3032 for( auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
3033 sw_BoxSetSplitBoxFormats(rpBox, pSplPara );
3034 return true;
3037 void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
3039 auto nLen = pBox->GetTabLines().size();
3040 if( nLen )
3042 // Continue with the actual Line
3043 if( pSplPara->IsGetFromTop() )
3044 nLen = 0;
3045 else
3046 --nLen;
3048 const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
3049 sw_Line_CollectBox( pLn, pSplPara );
3051 else
3052 pSplPara->AddBox( *pBox );
3055 void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
3057 auto nLen = pBox->GetTabLines().size();
3058 if( nLen )
3060 // Continue with the actual Line
3061 if( pSplPara->IsGetFromTop() )
3062 nLen = 0;
3063 else
3064 --nLen;
3066 const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
3067 sw_Line_CollectBox( pLn, pSplPara );
3069 else
3071 const SwTableBox* pSrcBox = pSplPara->GetBoxOfPos( *pBox );
3072 SwFrameFormat* pFormat = pSrcBox->GetFrameFormat();
3074 if( SplitTable_HeadlineOption::BorderCopy == pSplPara->GetMode() )
3076 const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
3077 if( !rBoxItem.GetTop() )
3079 SvxBoxItem aNew( rBoxItem );
3080 aNew.SetLine( pFormat->GetBox().GetBottom(), SvxBoxItemLine::TOP );
3081 if( aNew != rBoxItem )
3082 pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
3085 else
3087 SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE,
3088 RES_PROTECT, RES_PROTECT,
3089 RES_VERT_ORIENT, RES_VERT_ORIENT,
3090 RES_BACKGROUND, RES_SHADOW>
3091 aTmpSet( pFormat->GetDoc()->GetAttrPool() );
3092 aTmpSet.Put( pFormat->GetAttrSet() );
3093 if( aTmpSet.Count() )
3094 pBox->ClaimFrameFormat()->SetFormatAttr( aTmpSet );
3096 if( SplitTable_HeadlineOption::BoxAttrAllCopy == pSplPara->GetMode() )
3098 SwNodeIndex aIdx( *pSrcBox->GetSttNd(), 1 );
3099 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
3100 if( !pCNd )
3101 pCNd = aIdx.GetNodes().GoNext( &aIdx );
3102 aIdx = *pBox->GetSttNd();
3103 SwContentNode* pDNd = aIdx.GetNodes().GoNext( &aIdx );
3105 // If the Node is alone in the Section
3106 if( SwNodeOffset(2) == pDNd->EndOfSectionIndex() -
3107 pDNd->StartOfSectionIndex() )
3109 pSplPara->AddToUndoHistory( *pDNd );
3110 pDNd->ChgFormatColl( pCNd->GetFormatColl() );
3114 // note conditional template
3115 pBox->GetSttNd()->CheckSectionCondColl();
3121 * Splits a Table in the top-level Line which contains the Index.
3122 * All succeeding top-level Lines go into a new Table/Node.
3124 * @param bCalcNewSize true
3125 * Calculate the new Size for both from the
3126 * Boxes' Max; but only if Size is using absolute
3127 * values (USHRT_MAX)
3129 void SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
3130 bool bCalcNewSize )
3132 SwNode* pNd = &rPos.GetNode();
3133 SwTableNode* pTNd = pNd->FindTableNode();
3134 if( !pTNd || pNd->IsTableNode() )
3135 return;
3137 if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
3138 return;
3140 SwTable& rTable = pTNd->GetTable();
3141 rTable.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
3143 SwHistory aHistory;
3145 SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
3146 // Find top-level Line
3147 SwTableBox* pBox = rTable.GetTableBox(nSttIdx);
3148 sal_uInt16 nSplitLine = 0;
3149 if(pBox)
3151 SwTableLine* pLine = pBox->GetUpper();
3152 while(pLine->GetUpper())
3153 pLine = pLine->GetUpper()->GetUpper();
3155 // pLine contains the top-level Line now
3156 nSplitLine = rTable.GetTabLines().GetPos(pLine);
3158 rTable.Split(GetUniqueTableName(), nSplitLine, GetIDocumentUndoRedo().DoesUndo() ? &aHistory : nullptr);
3161 // Find Lines for the Layout update
3162 FndBox_ aFndBox( nullptr, nullptr );
3163 aFndBox.SetTableLines( rTable );
3164 aFndBox.DelFrames( rTable );
3166 SwTableNode* pNew = GetNodes().SplitTable( rPos.GetNode(), false, bCalcNewSize );
3168 if( pNew )
3170 std::unique_ptr<SwSaveRowSpan> pSaveRowSp = pNew->GetTable().CleanUpTopRowSpan( rTable.GetTabLines().size() );
3171 SwUndoSplitTable* pUndo = nullptr;
3172 if (GetIDocumentUndoRedo().DoesUndo())
3174 pUndo = new SwUndoSplitTable(
3175 *pNew, std::move(pSaveRowSp), eHdlnMode, bCalcNewSize);
3176 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3177 if( aHistory.Count() )
3178 pUndo->SaveFormula( aHistory );
3181 switch( eHdlnMode )
3183 // Set the lower Border of the preceding Line to
3184 // the upper Border of the current one
3185 case SplitTable_HeadlineOption::BorderCopy:
3187 SwCollectTableLineBoxes aPara( false, eHdlnMode );
3188 SwTableLine* pLn = rTable.GetTabLines()[
3189 rTable.GetTabLines().size() - 1 ];
3190 for( const auto& rpBox : pLn->GetTabBoxes() )
3191 sw_Box_CollectBox(rpBox, &aPara );
3193 aPara.SetValues( true );
3194 pLn = pNew->GetTable().GetTabLines()[ 0 ];
3195 for( auto& rpBox : pLn->GetTabBoxes() )
3196 sw_BoxSetSplitBoxFormats(rpBox, &aPara );
3198 // Switch off repeating Header
3199 pNew->GetTable().SetRowsToRepeat( 0 );
3201 break;
3203 // Take over the Attributes of the first Line to the new one
3204 case SplitTable_HeadlineOption::BoxAttrCopy:
3205 case SplitTable_HeadlineOption::BoxAttrAllCopy:
3207 SwHistory* pHst = nullptr;
3208 if( SplitTable_HeadlineOption::BoxAttrAllCopy == eHdlnMode && pUndo )
3209 pHst = pUndo->GetHistory();
3211 SwCollectTableLineBoxes aPara( true, eHdlnMode, pHst );
3212 SwTableLine* pLn = rTable.GetTabLines()[ 0 ];
3213 for( const auto& rpBox : pLn->GetTabBoxes() )
3214 sw_Box_CollectBox(rpBox, &aPara );
3216 aPara.SetValues( true );
3217 pLn = pNew->GetTable().GetTabLines()[ 0 ];
3218 for( auto& rpBox : pLn->GetTabBoxes() )
3219 sw_BoxSetSplitBoxFormats(rpBox, &aPara );
3221 break;
3223 case SplitTable_HeadlineOption::ContentCopy:
3224 rTable.CopyHeadlineIntoTable( *pNew );
3225 if( pUndo )
3226 pUndo->SetTableNodeOffset( pNew->GetIndex() );
3227 break;
3229 case SplitTable_HeadlineOption::NONE:
3230 // Switch off repeating the Header
3231 pNew->GetTable().SetRowsToRepeat( 0 );
3232 break;
3235 // And insert Frames
3236 pNew->MakeOwnFrames();
3238 // Insert a paragraph between the Table
3239 GetNodes().MakeTextNode( *pNew,
3240 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
3243 // Update Layout
3244 aFndBox.MakeFrames( rTable );
3246 // TL_CHART2: need to inform chart of probably changed cell names
3247 UpdateCharts( rTable.GetFrameFormat()->GetName() );
3249 // update table style formatting of both the tables
3250 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
3252 pFEShell->UpdateTableStyleFormatting(pTNd);
3253 pFEShell->UpdateTableStyleFormatting(pNew);
3256 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3259 static bool lcl_ChgTableSize( SwTable& rTable )
3261 // The Attribute must not be set via the Modify or else all Boxes are
3262 // set back to 0.
3263 // So lock the Format.
3264 SwFrameFormat* pFormat = rTable.GetFrameFormat();
3265 SwFormatFrameSize aTableMaxSz( pFormat->GetFrameSize() );
3267 if( USHRT_MAX == aTableMaxSz.GetWidth() )
3268 return false;
3270 bool bLocked = pFormat->IsModifyLocked();
3271 pFormat->LockModify();
3273 aTableMaxSz.SetWidth( 0 );
3275 SwTableLines& rLns = rTable.GetTabLines();
3276 for( auto pLn : rLns )
3278 SwTwips nMaxLnWidth = 0;
3279 SwTableBoxes& rBoxes = pLn->GetTabBoxes();
3280 for( auto pBox : rBoxes )
3281 nMaxLnWidth += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
3283 if( nMaxLnWidth > aTableMaxSz.GetWidth() )
3284 aTableMaxSz.SetWidth( nMaxLnWidth );
3286 pFormat->SetFormatAttr( aTableMaxSz );
3287 if( !bLocked ) // Release the Lock if appropriate
3288 pFormat->UnlockModify();
3290 return true;
3293 namespace {
3295 class SplitTable_Para
3297 std::map<SwFrameFormat const*, SwFrameFormat*> m_aSrcDestMap;
3298 SwTableNode* m_pNewTableNode;
3299 SwTable& m_rOldTable;
3301 public:
3302 SplitTable_Para(SwTableNode* pNew, SwTable& rOld)
3303 : m_pNewTableNode(pNew)
3304 , m_rOldTable(rOld)
3306 SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
3308 auto it = m_aSrcDestMap.find(pSrcFormat);
3309 return it == m_aSrcDestMap.end() ? nullptr : it->second;
3312 void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
3314 m_aSrcDestMap[pSrcFormat] = pDestFormat;
3317 void ChgBox( SwTableBox* pBox )
3319 m_rOldTable.GetTabSortBoxes().erase(pBox);
3320 m_pNewTableNode->GetTable().GetTabSortBoxes().insert(pBox);
3326 static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara );
3328 static void lcl_SplitTable_CpyLine( SwTableLine* pLn, SplitTable_Para* pPara )
3330 SwFrameFormat *pSrcFormat = pLn->GetFrameFormat();
3331 SwTableLineFormat* pDestFormat = static_cast<SwTableLineFormat*>( pPara->GetDestFormat( pSrcFormat ) );
3332 if( pDestFormat == nullptr )
3334 pPara->InsertSrcDest( pSrcFormat, pLn->ClaimFrameFormat() );
3336 else
3337 pLn->ChgFrameFormat( pDestFormat );
3339 for( auto& rpBox : pLn->GetTabBoxes() )
3340 lcl_SplitTable_CpyBox(rpBox, pPara );
3343 static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara )
3345 SwFrameFormat *pSrcFormat = pBox->GetFrameFormat();
3346 SwTableBoxFormat* pDestFormat = static_cast<SwTableBoxFormat*>(pPara->GetDestFormat( pSrcFormat ));
3347 if( pDestFormat == nullptr )
3349 pPara->InsertSrcDest( pSrcFormat, pBox->ClaimFrameFormat() );
3351 else
3352 pBox->ChgFrameFormat( pDestFormat );
3354 if( pBox->GetSttNd() )
3355 pPara->ChgBox( pBox );
3356 else
3357 for( SwTableLine* pLine : pBox->GetTabLines() )
3358 lcl_SplitTable_CpyLine( pLine, pPara );
3361 SwTableNode* SwNodes::SplitTable( SwNode& rPos, bool bAfter,
3362 bool bCalcNewSize )
3364 SwNode* pNd = &rPos;
3365 SwTableNode* pTNd = pNd->FindTableNode();
3366 if( !pTNd || pNd->IsTableNode() )
3367 return nullptr;
3369 SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
3371 // Find this Box/top-level line
3372 SwTable& rTable = pTNd->GetTable();
3373 SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
3374 if( !pBox )
3375 return nullptr;
3377 SwTableLine* pLine = pBox->GetUpper();
3378 while( pLine->GetUpper() )
3379 pLine = pLine->GetUpper()->GetUpper();
3381 // pLine now contains the top-level line
3382 sal_uInt16 nLinePos = rTable.GetTabLines().GetPos( pLine );
3383 if( USHRT_MAX == nLinePos ||
3384 ( bAfter ? ++nLinePos >= rTable.GetTabLines().size() : !nLinePos ))
3385 return nullptr; // Not found or last Line!
3387 // Find the first Box of the succeeding Line
3388 SwTableLine* pNextLine = rTable.GetTabLines()[ nLinePos ];
3389 pBox = pNextLine->GetTabBoxes()[0];
3390 while( !pBox->GetSttNd() )
3391 pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
3393 // Insert an EndNode and TableNode into the Nodes Array
3394 SwTableNode * pNewTableNd;
3396 SwEndNode* pOldTableEndNd = pTNd->EndOfSectionNode()->GetEndNode();
3397 assert(pOldTableEndNd && "Where is the EndNode?");
3399 new SwEndNode( *pBox->GetSttNd(), *pTNd );
3400 pNewTableNd = new SwTableNode( *pBox->GetSttNd() );
3401 pNewTableNd->GetTable().SetTableModel( rTable.IsNewModel() );
3403 pOldTableEndNd->m_pStartOfSection = pNewTableNd;
3404 pNewTableNd->m_pEndOfSection = pOldTableEndNd;
3406 SwNode* pBoxNd = const_cast<SwStartNode*>(pBox->GetSttNd()->GetStartNode());
3407 do {
3408 OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
3409 pBoxNd->m_pStartOfSection = pNewTableNd;
3410 pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
3411 } while( pBoxNd != pOldTableEndNd );
3415 // Move the Lines
3416 SwTable& rNewTable = pNewTableNd->GetTable();
3417 rNewTable.GetTabLines().insert( rNewTable.GetTabLines().begin(),
3418 rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
3420 /* From the back (bottom right) to the front (top left) deregister all Boxes from the
3421 Chart Data Provider. The Modify event is triggered in the calling function.
3422 TL_CHART2: */
3423 SwChartDataProvider *pPCD = rTable.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
3424 if( pPCD )
3426 for (SwTableLines::size_type k = nLinePos; k < rTable.GetTabLines().size(); ++k)
3428 const SwTableLines::size_type nLineIdx = (rTable.GetTabLines().size() - 1) - k + nLinePos;
3429 const SwTableBoxes::size_type nBoxCnt = rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes().size();
3430 for (SwTableBoxes::size_type j = 0; j < nBoxCnt; ++j)
3432 const SwTableBoxes::size_type nIdx = nBoxCnt - 1 - j;
3433 pPCD->DeleteBox( &rTable, *rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes()[nIdx] );
3438 // Delete
3439 sal_uInt16 nDeleted = rTable.GetTabLines().size() - nLinePos;
3440 rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
3442 // Move the affected Boxes. Make the Formats unique and correct the StartNodes
3443 SplitTable_Para aPara( pNewTableNd, rTable );
3444 for( SwTableLine* pNewLine : rNewTable.GetTabLines() )
3445 lcl_SplitTable_CpyLine( pNewLine, &aPara );
3446 rTable.CleanUpBottomRowSpan( nDeleted );
3450 // Copy the Table FrameFormat
3451 SwFrameFormat* pOldTableFormat = rTable.GetFrameFormat();
3452 SwFrameFormat* pNewTableFormat = pOldTableFormat->GetDoc()->MakeTableFrameFormat(
3453 pOldTableFormat->GetDoc()->GetUniqueTableName(),
3454 pOldTableFormat->GetDoc()->GetDfltFrameFormat() );
3456 *pNewTableFormat = *pOldTableFormat;
3457 pNewTableNd->GetTable().RegisterToFormat( *pNewTableFormat );
3459 pNewTableNd->GetTable().SetTableStyleName(rTable.GetTableStyleName());
3461 // Calculate a new Size?
3462 // lcl_ChgTableSize: Only execute the second call if the first call was
3463 // successful, thus has an absolute Size
3464 if( bCalcNewSize && lcl_ChgTableSize( rTable ) )
3465 lcl_ChgTableSize( pNewTableNd->GetTable() );
3468 // TL_CHART2: need to inform chart of probably changed cell names
3469 rTable.UpdateCharts();
3471 return pNewTableNd; // That's it!
3475 * rPos needs to be in the Table that remains
3477 * @param bWithPrev merge the current Table with the preceding
3478 * or succeeding one
3480 bool SwDoc::MergeTable( const SwPosition& rPos, bool bWithPrev )
3482 SwTableNode* pTableNd = rPos.GetNode().FindTableNode(), *pDelTableNd;
3483 if( !pTableNd )
3484 return false;
3486 SwNodes& rNds = GetNodes();
3487 if( bWithPrev )
3488 pDelTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
3489 else
3490 pDelTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
3491 if( !pDelTableNd )
3492 return false;
3494 if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr ||
3495 dynamic_cast<const SwDDETable*>( &pDelTableNd->GetTable() ) != nullptr)
3496 return false;
3498 // Delete HTML Layout
3499 pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3500 pDelTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3502 // Both Tables are present; we can start
3503 SwUndoMergeTable* pUndo = nullptr;
3504 std::unique_ptr<SwHistory> pHistory;
3505 if (GetIDocumentUndoRedo().DoesUndo())
3507 pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev );
3508 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3509 pHistory.reset(new SwHistory);
3512 // Adapt all "TableFormulas"
3513 pTableNd->GetTable().Merge(pDelTableNd->GetTable(), pHistory.get());
3515 // The actual merge
3516 bool bRet = rNds.MergeTable( bWithPrev ? *pTableNd : *pDelTableNd, !bWithPrev );
3518 if( pHistory )
3520 if( pHistory->Count() )
3521 pUndo->SaveFormula( *pHistory );
3522 pHistory.reset();
3524 if( bRet )
3526 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
3527 pFEShell->UpdateTableStyleFormatting();
3529 getIDocumentState().SetModified();
3530 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3532 return bRet;
3535 bool SwNodes::MergeTable( SwNode& rPos, bool bWithPrev )
3537 SwTableNode* pDelTableNd = rPos.GetTableNode();
3538 OSL_ENSURE( pDelTableNd, "Where did the TableNode go?" );
3540 SwTableNode* pTableNd = (*this)[ rPos.GetIndex() - 1]->FindTableNode();
3541 OSL_ENSURE( pTableNd, "Where did the TableNode go?" );
3543 if( !pDelTableNd || !pTableNd )
3544 return false;
3546 pDelTableNd->DelFrames();
3548 SwTable& rDelTable = pDelTableNd->GetTable();
3549 SwTable& rTable = pTableNd->GetTable();
3551 // Find Lines for the Layout update
3552 FndBox_ aFndBox( nullptr, nullptr );
3553 aFndBox.SetTableLines( rTable );
3554 aFndBox.DelFrames( rTable );
3556 // TL_CHART2:
3557 // tell the charts about the table to be deleted and have them use their own data
3558 GetDoc().getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable );
3560 // Sync the TableFormat's Width
3562 const SwFormatFrameSize& rTableSz = rTable.GetFrameFormat()->GetFrameSize();
3563 const SwFormatFrameSize& rDelTableSz = rDelTable.GetFrameFormat()->GetFrameSize();
3564 if( rTableSz != rDelTableSz )
3566 // The needs correction
3567 if( bWithPrev )
3568 rDelTable.GetFrameFormat()->SetFormatAttr( rTableSz );
3569 else
3570 rTable.GetFrameFormat()->SetFormatAttr( rDelTableSz );
3574 if( !bWithPrev )
3576 // Transfer all Attributes of the succeeding Table to the preceding one
3577 // We do this, because the succeeding one is deleted when deleting the Node
3578 rTable.SetRowsToRepeat( rDelTable.GetRowsToRepeat() );
3579 rTable.SetTableChgMode( rDelTable.GetTableChgMode() );
3581 rTable.GetFrameFormat()->LockModify();
3582 *rTable.GetFrameFormat() = *rDelTable.GetFrameFormat();
3583 // Also switch the Name
3584 rTable.GetFrameFormat()->SetFormatName( rDelTable.GetFrameFormat()->GetName() );
3585 rTable.GetFrameFormat()->UnlockModify();
3588 // Move the Lines and Boxes
3589 SwTableLines::size_type nOldSize = rTable.GetTabLines().size();
3590 rTable.GetTabLines().insert( rTable.GetTabLines().begin() + nOldSize,
3591 rDelTable.GetTabLines().begin(), rDelTable.GetTabLines().end() );
3592 rDelTable.GetTabLines().clear();
3594 rTable.GetTabSortBoxes().insert( rDelTable.GetTabSortBoxes() );
3595 rDelTable.GetTabSortBoxes().clear();
3597 // The preceding Table always remains, while the succeeding one is deleted
3598 SwEndNode* pTableEndNd = pDelTableNd->EndOfSectionNode();
3599 pTableNd->m_pEndOfSection = pTableEndNd;
3601 SwNodeIndex aIdx( *pDelTableNd, 1 );
3603 SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
3604 do {
3605 OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
3606 pBoxNd->m_pStartOfSection = pTableNd;
3607 pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
3608 } while( pBoxNd != pTableEndNd );
3609 pBoxNd->m_pStartOfSection = pTableNd;
3611 aIdx -= SwNodeOffset(2);
3612 DelNodes( aIdx, SwNodeOffset(2) );
3614 // tweak the conditional styles at the first inserted Line
3615 const SwTableLine* pFirstLn = rTable.GetTabLines()[ nOldSize ];
3616 sw_LineSetHeadCondColl( pFirstLn );
3618 // Clean up the Borders
3619 if( nOldSize )
3621 SwGCLineBorder aPara( rTable );
3622 aPara.nLinePos = --nOldSize;
3623 pFirstLn = rTable.GetTabLines()[ nOldSize ];
3624 sw_GC_Line_Border( pFirstLn, &aPara );
3627 // Update Layout
3628 aFndBox.MakeFrames( rTable );
3630 return true;
3633 namespace {
3635 // Use the PtrArray's ForEach method
3636 struct SetAFormatTabPara
3638 SwTableAutoFormat& rTableFormat;
3639 SwUndoTableAutoFormat* pUndo;
3640 sal_uInt16 nEndBox, nCurBox;
3641 sal_uInt8 nAFormatLine, nAFormatBox;
3642 bool bSingleRowTable;
3644 explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
3645 : rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
3646 nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
3652 // Forward declare so that the Lines and Boxes can use recursion
3653 static bool lcl_SetAFormatBox(FndBox_ &, SetAFormatTabPara *pSetPara, bool bResetDirect);
3654 static bool lcl_SetAFormatLine(FndLine_ &, SetAFormatTabPara *pPara, bool bResetDirect);
3656 static bool lcl_SetAFormatLine(FndLine_ & rLine, SetAFormatTabPara *pPara, bool bResetDirect)
3658 for (auto const& it : rLine.GetBoxes())
3660 lcl_SetAFormatBox(*it, pPara, bResetDirect);
3662 return true;
3665 static bool lcl_SetAFormatBox(FndBox_ & rBox, SetAFormatTabPara *pSetPara, bool bResetDirect)
3667 if (!rBox.GetUpper()->GetUpper()) // Box on first level?
3669 if( !pSetPara->nCurBox )
3670 pSetPara->nAFormatBox = 0;
3671 else if( pSetPara->nCurBox == pSetPara->nEndBox )
3672 pSetPara->nAFormatBox = 3;
3673 else //Even column(1) or Odd column(2)
3674 pSetPara->nAFormatBox = static_cast<sal_uInt8>(1 + ((pSetPara->nCurBox-1) & 1));
3677 if (rBox.GetBox()->GetSttNd())
3679 SwTableBox* pSetBox = rBox.GetBox();
3680 if (!pSetBox->HasDirectFormatting() || bResetDirect)
3682 if (bResetDirect)
3683 pSetBox->SetDirectFormatting(false);
3685 SwDoc* pDoc = pSetBox->GetFrameFormat()->GetDoc();
3686 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet(pDoc->GetAttrPool());
3687 SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
3688 sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
3689 const bool bSingleRowTable = pSetPara->bSingleRowTable;
3690 const bool bSingleColTable = pSetPara->nEndBox == 0;
3691 pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aCharSet, SwTableAutoFormatUpdateFlags::Char, nullptr);
3692 pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aBoxSet, SwTableAutoFormatUpdateFlags::Box, pDoc->GetNumberFormatter());
3694 if (aCharSet.Count())
3696 SwNodeOffset nSttNd = pSetBox->GetSttIdx()+1;
3697 SwNodeOffset nEndNd = pSetBox->GetSttNd()->EndOfSectionIndex();
3698 for (; nSttNd < nEndNd; ++nSttNd)
3700 SwContentNode* pNd = pDoc->GetNodes()[ nSttNd ]->GetContentNode();
3701 if (pNd)
3702 pNd->SetAttr(aCharSet);
3706 if (aBoxSet.Count())
3708 if (pSetPara->pUndo && SfxItemState::SET == aBoxSet.GetItemState(RES_BOXATR_FORMAT))
3709 pSetPara->pUndo->SaveBoxContent( *pSetBox );
3711 pSetBox->ClaimFrameFormat()->SetFormatAttr(aBoxSet);
3715 else
3717 // Not sure how this situation can occur, but apparently we have some kind of table in table.
3718 // I am guessing at how to best handle singlerow in this situation.
3719 const bool bOrigSingleRowTable = pSetPara->bSingleRowTable;
3720 pSetPara->bSingleRowTable = rBox.GetLines().size() == 1;
3721 for (auto const& rpFndLine : rBox.GetLines())
3723 lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
3725 pSetPara->bSingleRowTable = bOrigSingleRowTable;
3728 if (!rBox.GetUpper()->GetUpper()) // a BaseLine
3729 ++pSetPara->nCurBox;
3730 return true;
3733 bool SwDoc::SetTableAutoFormat(const SwSelBoxes& rBoxes, const SwTableAutoFormat& rNew, bool bResetDirect, bool const isSetStyleName)
3735 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
3736 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
3737 if( !pTableNd )
3738 return false;
3740 // Find all Boxes/Lines
3741 FndBox_ aFndBox( nullptr, nullptr );
3743 FndPara aPara( rBoxes, &aFndBox );
3744 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
3746 if( aFndBox.GetLines().empty() )
3747 return false;
3749 SwTable &table = pTableNd->GetTable();
3750 table.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3752 FndBox_* pFndBox = &aFndBox;
3753 while( 1 == pFndBox->GetLines().size() &&
3754 1 == pFndBox->GetLines().front()->GetBoxes().size())
3756 pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
3759 if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
3760 pFndBox = pFndBox->GetUpper()->GetUpper();
3762 // Disable Undo, but first store parameters
3763 SwUndoTableAutoFormat* pUndo = nullptr;
3764 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
3765 if (bUndo)
3767 pUndo = new SwUndoTableAutoFormat( *pTableNd, rNew );
3768 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3769 GetIDocumentUndoRedo().DoUndo(false);
3772 if (isSetStyleName)
3773 { // tdf#98226 do this here where undo can record it
3774 pTableNd->GetTable().SetTableStyleName(rNew.GetName());
3777 rNew.RestoreTableProperties(table);
3779 SetAFormatTabPara aPara( rNew );
3780 FndLines_t& rFLns = pFndBox->GetLines();
3781 aPara.bSingleRowTable = rFLns.size() == 1;
3783 for (FndLines_t::size_type n = 0; n < rFLns.size(); ++n)
3785 FndLine_* pLine = rFLns[n].get();
3787 // Set Upper to 0 (thus simulate BaseLine)
3788 FndBox_* pSaveBox = pLine->GetUpper();
3789 pLine->SetUpper( nullptr );
3791 if( !n )
3792 aPara.nAFormatLine = 0;
3793 else if (static_cast<size_t>(n+1) == rFLns.size())
3794 aPara.nAFormatLine = 3;
3795 else
3796 aPara.nAFormatLine = static_cast<sal_uInt8>(1 + ((n-1) & 1 ));
3798 aPara.nAFormatBox = 0;
3799 aPara.nCurBox = 0;
3800 aPara.nEndBox = pLine->GetBoxes().size()-1;
3801 aPara.pUndo = pUndo;
3802 for (auto const& it : pLine->GetBoxes())
3804 lcl_SetAFormatBox(*it, &aPara, bResetDirect);
3807 pLine->SetUpper( pSaveBox );
3810 if( pUndo )
3812 GetIDocumentUndoRedo().DoUndo(bUndo);
3815 getIDocumentState().SetModified();
3816 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3818 return true;
3822 * Find out who has the Attributes
3824 bool SwDoc::GetTableAutoFormat( const SwSelBoxes& rBoxes, SwTableAutoFormat& rGet )
3826 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
3827 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
3828 if( !pTableNd )
3829 return false;
3831 // Find all Boxes/Lines
3832 FndBox_ aFndBox( nullptr, nullptr );
3834 FndPara aPara( rBoxes, &aFndBox );
3835 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
3837 if( aFndBox.GetLines().empty() )
3838 return false;
3840 // Store table properties
3841 SwTable &table = pTableNd->GetTable();
3842 rGet.StoreTableProperties(table);
3844 FndBox_* pFndBox = &aFndBox;
3845 while( 1 == pFndBox->GetLines().size() &&
3846 1 == pFndBox->GetLines().front()->GetBoxes().size())
3848 pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
3851 if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
3852 pFndBox = pFndBox->GetUpper()->GetUpper();
3854 FndLines_t& rFLns = pFndBox->GetLines();
3856 sal_uInt16 aLnArr[4];
3857 aLnArr[0] = 0;
3858 aLnArr[1] = 1 < rFLns.size() ? 1 : 0;
3859 aLnArr[2] = 2 < rFLns.size() ? 2 : aLnArr[1];
3860 aLnArr[3] = rFLns.size() - 1;
3862 for( sal_uInt8 nLine = 0; nLine < 4; ++nLine )
3864 FndLine_& rLine = *rFLns[ aLnArr[ nLine ] ];
3866 sal_uInt16 aBoxArr[4];
3867 aBoxArr[0] = 0;
3868 aBoxArr[1] = 1 < rLine.GetBoxes().size() ? 1 : 0;
3869 aBoxArr[2] = 2 < rLine.GetBoxes().size() ? 2 : aBoxArr[1];
3870 aBoxArr[3] = rLine.GetBoxes().size() - 1;
3872 for( sal_uInt8 nBox = 0; nBox < 4; ++nBox )
3874 SwTableBox* pFBox = rLine.GetBoxes()[ aBoxArr[ nBox ] ]->GetBox();
3875 // Always apply to the first ones
3876 while( !pFBox->GetSttNd() )
3877 pFBox = pFBox->GetTabLines()[0]->GetTabBoxes()[0];
3879 sal_uInt8 nPos = nLine * 4 + nBox;
3880 SwNodeIndex aIdx( *pFBox->GetSttNd(), 1 );
3881 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
3882 if( !pCNd )
3883 pCNd = GetNodes().GoNext( &aIdx );
3885 if( pCNd )
3886 rGet.UpdateFromSet( nPos, pCNd->GetSwAttrSet(),
3887 SwTableAutoFormatUpdateFlags::Char, nullptr );
3888 rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
3889 SwTableAutoFormatUpdateFlags::Box,
3890 GetNumberFormatter() );
3894 return true;
3897 SwTableAutoFormatTable& SwDoc::GetTableStyles()
3899 if (!m_pTableStyles)
3901 m_pTableStyles.reset(new SwTableAutoFormatTable);
3902 m_pTableStyles->Load();
3904 return *m_pTableStyles;
3907 OUString SwDoc::GetUniqueTableName() const
3909 if( IsInMailMerge())
3911 OUString newName = "MailMergeTable"
3912 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
3913 + OUString::number( mpTableFrameFormatTable->size() + 1 );
3914 return newName;
3917 const OUString aName(SwResId(STR_TABLE_DEFNAME));
3919 const size_t nFlagSize = ( mpTableFrameFormatTable->size() / 8 ) + 2;
3921 std::unique_ptr<sal_uInt8[]> pSetFlags( new sal_uInt8[ nFlagSize ] );
3922 memset( pSetFlags.get(), 0, nFlagSize );
3924 for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
3926 const SwTableFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
3927 if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
3928 pFormat->GetName().startsWith( aName ) )
3930 // Get number and set the Flag
3931 const sal_Int32 nNmLen = aName.getLength();
3932 size_t nNum = o3tl::toInt32(pFormat->GetName().subView( nNmLen ));
3933 if( nNum-- && nNum < mpTableFrameFormatTable->size() )
3934 pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
3938 // All numbers are flagged properly, thus calculate the right number
3939 size_t nNum = mpTableFrameFormatTable->size();
3940 for( size_t n = 0; n < nFlagSize; ++n )
3942 auto nTmp = pSetFlags[ n ];
3943 if( nTmp != 0xFF )
3945 // Calculate the number
3946 nNum = n * 8;
3947 while( nTmp & 1 )
3949 ++nNum;
3950 nTmp >>= 1;
3952 break;
3956 return aName + OUString::number( ++nNum );
3959 SwTableFormat* SwDoc::FindTableFormatByName( const OUString& rName, bool bAll ) const
3961 const SwFormat* pRet = nullptr;
3962 if( bAll )
3963 pRet = mpTableFrameFormatTable->FindFormatByName( rName );
3964 else
3966 auto [it, itEnd] = mpTableFrameFormatTable->findRangeByName(rName);
3967 // Only the ones set in the Doc
3968 for( ; it != itEnd; ++it )
3970 const SwFrameFormat* pFormat = *it;
3971 if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
3972 pFormat->GetName() == rName )
3974 pRet = pFormat;
3975 break;
3979 return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
3982 void SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
3983 SwTwips nAbsDiff, SwTwips nRelDiff )
3985 SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
3986 std::unique_ptr<SwUndo> pUndo;
3988 pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
3989 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
3990 bool bRet = false;
3991 switch( extractPosition(eType) )
3993 case TableChgWidthHeightType::ColLeft:
3994 case TableChgWidthHeightType::ColRight:
3995 case TableChgWidthHeightType::CellLeft:
3996 case TableChgWidthHeightType::CellRight:
3998 bRet = pTableNd->GetTable().SetColWidth( rCurrentBox,
3999 eType, nAbsDiff, nRelDiff,
4000 bUndo ? &pUndo : nullptr );
4002 break;
4003 case TableChgWidthHeightType::RowBottom:
4004 case TableChgWidthHeightType::CellTop:
4005 case TableChgWidthHeightType::CellBottom:
4006 bRet = pTableNd->GetTable().SetRowHeight( rCurrentBox,
4007 eType, nAbsDiff, nRelDiff,
4008 bUndo ? &pUndo : nullptr );
4009 break;
4010 default: break;
4013 GetIDocumentUndoRedo().DoUndo(bUndo); // SetColWidth can turn it off
4014 if( pUndo )
4016 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4019 if( bRet )
4021 getIDocumentState().SetModified();
4025 bool SwDoc::IsNumberFormat( std::u16string_view aString, sal_uInt32& F_Index, double& fOutNumber )
4027 if( aString.size() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
4028 return false;
4030 // remove any comment anchor marks
4031 OUStringBuffer sStringBuffer(aString);
4032 sal_Int32 nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD );
4033 while( nCommentPosition != -1 )
4035 sStringBuffer.remove( nCommentPosition, 1 );
4036 nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD, nCommentPosition );
4039 return GetNumberFormatter()->IsNumberFormat( sStringBuffer.makeStringAndClear(), F_Index, fOutNumber );
4042 void SwDoc::ChkBoxNumFormat( SwTableBox& rBox, bool bCallUpdate )
4044 // Optimization: If the Box says it's Text, it remains Text
4045 const SwTableBoxNumFormat* pNumFormatItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT,
4046 false );
4047 if( pNumFormatItem && GetNumberFormatter()->IsTextFormat(pNumFormatItem->GetValue()) )
4048 return ;
4050 std::unique_ptr<SwUndoTableNumFormat> pUndo;
4052 bool bIsEmptyTextNd;
4053 bool bChgd = true;
4054 sal_uInt32 nFormatIdx;
4055 double fNumber;
4056 if( rBox.HasNumContent( fNumber, nFormatIdx, bIsEmptyTextNd ) )
4058 if( !rBox.IsNumberChanged() )
4059 bChgd = false;
4060 else
4062 if (GetIDocumentUndoRedo().DoesUndo())
4064 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
4065 pUndo.reset(new SwUndoTableNumFormat( rBox ));
4066 pUndo->SetNumFormat( nFormatIdx, fNumber );
4069 SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
4070 SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( GetAttrPool() );
4072 bool bLockModify = true;
4073 bool bSetNumberFormat = IsInsTableFormatNum();
4074 const bool bForceNumberFormat = IsInsTableFormatNum() && IsInsTableChangeNumFormat();
4076 // if the user forced a number format in this cell previously,
4077 // keep it, unless the user set that she wants the full number
4078 // format recognition
4079 if( pNumFormatItem && !bForceNumberFormat )
4081 sal_uLong nOldNumFormat = pNumFormatItem->GetValue();
4082 SvNumberFormatter* pNumFormatr = GetNumberFormatter();
4084 SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIdx );
4085 if( nFormatType == pNumFormatr->GetType( nOldNumFormat ) || SvNumFormatType::NUMBER == nFormatType )
4087 // Current and specified NumFormat match
4088 // -> keep old Format
4089 nFormatIdx = nOldNumFormat;
4090 bSetNumberFormat = true;
4092 else
4094 // Current and specified NumFormat do not match
4095 // -> insert as Text
4096 bLockModify = bSetNumberFormat = false;
4100 if( bSetNumberFormat || bForceNumberFormat )
4102 pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
4104 aBoxSet.Put( SwTableBoxValue( fNumber ));
4105 aBoxSet.Put( SwTableBoxNumFormat( nFormatIdx ));
4108 // It's not enough to only reset the Formula.
4109 // Make sure that the Text is formatted accordingly
4110 if( !bSetNumberFormat && !bIsEmptyTextNd && pNumFormatItem )
4112 // Just resetting Attributes is not enough
4113 // Make sure that the Text is formatted accordingly
4114 pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
4117 if( bLockModify ) pBoxFormat->LockModify();
4118 pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
4119 if( bLockModify ) pBoxFormat->UnlockModify();
4121 if( bSetNumberFormat )
4122 pBoxFormat->SetFormatAttr( aBoxSet );
4125 else
4127 // It's not a number
4128 SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
4129 if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false ) ||
4130 SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, false ) )
4132 if (GetIDocumentUndoRedo().DoesUndo())
4134 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
4135 pUndo.reset(new SwUndoTableNumFormat( rBox ));
4138 pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
4140 // Remove all number formats
4141 sal_uInt16 nWhich1 = RES_BOXATR_FORMULA;
4142 if( !bIsEmptyTextNd )
4144 nWhich1 = RES_BOXATR_FORMAT;
4146 // Just resetting Attributes is not enough
4147 // Make sure that the Text is formatted accordingly
4148 pBoxFormat->SetFormatAttr( *GetDfltAttr( nWhich1 ));
4150 pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
4152 else
4153 bChgd = false;
4156 if( !bChgd )
4157 return;
4159 if( pUndo )
4161 pUndo->SetBox( rBox );
4162 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
4163 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
4166 const SwTableNode* pTableNd = rBox.GetSttNd()->FindTableNode();
4167 if( bCallUpdate )
4169 getIDocumentFieldsAccess().UpdateTableFields(&pTableNd->GetTable());
4171 // TL_CHART2: update charts (when cursor leaves cell and
4172 // automatic update is enabled)
4173 if (AUTOUPD_FIELD_AND_CHARTS == GetDocumentSettingManager().getFieldUpdateFlags(true))
4174 pTableNd->GetTable().UpdateCharts();
4176 getIDocumentState().SetModified();
4179 void SwDoc::SetTableBoxFormulaAttrs( SwTableBox& rBox, const SfxItemSet& rSet )
4181 if (GetIDocumentUndoRedo().DoesUndo())
4183 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoTableNumFormat>(rBox, &rSet) );
4186 SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
4187 if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
4189 pBoxFormat->LockModify();
4190 pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
4191 pBoxFormat->UnlockModify();
4193 else if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE ))
4195 pBoxFormat->LockModify();
4196 pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
4197 pBoxFormat->UnlockModify();
4199 pBoxFormat->SetFormatAttr( rSet );
4200 getIDocumentState().SetModified();
4203 void SwDoc::ClearLineNumAttrs( SwPosition const & rPos )
4205 SwPaM aPam(rPos);
4206 aPam.Move(fnMoveBackward);
4207 SwContentNode *pNode = aPam.GetPointContentNode();
4208 if ( nullptr == pNode )
4209 return ;
4210 if( !pNode->IsTextNode() )
4211 return;
4213 SwTextNode * pTextNode = pNode->GetTextNode();
4214 if (!(pTextNode && pTextNode->IsNumbered()
4215 && pTextNode->GetText().isEmpty()))
4216 return;
4218 SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1>
4219 rSet( pTextNode->GetDoc().GetAttrPool() );
4220 pTextNode->SwContentNode::GetAttr( rSet );
4221 const SfxStringItem* pFormatItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, false );
4222 if ( !pFormatItem )
4223 return;
4225 SwUndoDelNum * pUndo;
4226 if( GetIDocumentUndoRedo().DoesUndo() )
4228 GetIDocumentUndoRedo().ClearRedo();
4229 pUndo = new SwUndoDelNum( aPam );
4230 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
4232 else
4233 pUndo = nullptr;
4234 SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
4235 aRegH.RegisterInModify( pTextNode , *pTextNode );
4236 if ( pUndo )
4237 pUndo->AddNode( *pTextNode );
4238 std::unique_ptr<SfxStringItem> pNewItem(pFormatItem->Clone());
4239 pNewItem->SetValue(OUString());
4240 rSet.Put( std::move(pNewItem) );
4241 pTextNode->SetAttr( rSet );
4244 void SwDoc::ClearBoxNumAttrs( SwNode& rNode )
4246 SwStartNode* pSttNd = rNode.FindSttNodeByType( SwTableBoxStartNode );
4247 if( nullptr == pSttNd ||
4248 SwNodeOffset(2) != pSttNd->EndOfSectionIndex() - pSttNd->GetIndex())
4249 return;
4251 SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
4252 GetTableBox( pSttNd->GetIndex() );
4254 const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
4255 const SwTableBoxNumFormat* pFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false );
4256 if( !pFormatItem ||
4257 SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
4258 SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
4259 return;
4261 if (GetIDocumentUndoRedo().DoesUndo())
4263 GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoTableNumFormat>(*pBox));
4266 SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
4268 // Keep TextFormats!
4269 sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
4270 if( pFormatItem && GetNumberFormatter()->IsTextFormat(
4271 pFormatItem->GetValue() ))
4272 nWhich1 = RES_BOXATR_FORMULA;
4273 else
4274 // Just resetting Attributes is not enough
4275 // Make sure that the Text is formatted accordingly
4276 pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
4278 pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
4279 getIDocumentState().SetModified();
4283 * Copies a Table from the same or another Doc into itself
4284 * We create a new Table or an existing one is filled with the Content.
4285 * We either fill in the Content from a certain Box or a certain TableSelection
4287 * This method is called by edglss.cxx/fecopy.cxx
4289 bool SwDoc::InsCopyOfTable( SwPosition& rInsPos, const SwSelBoxes& rBoxes,
4290 const SwTable* pCpyTable, bool bCpyName, bool bCorrPos, const OUString& rStyleName )
4292 bool bRet;
4294 const SwTableNode* pSrcTableNd = pCpyTable
4295 ? pCpyTable->GetTableNode()
4296 : rBoxes[ 0 ]->GetSttNd()->FindTableNode();
4298 SwTableNode * pInsTableNd = rInsPos.GetNode().FindTableNode();
4300 bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
4301 if( !pCpyTable && !pInsTableNd )
4303 std::unique_ptr<SwUndoCpyTable> pUndo;
4304 if (bUndo)
4306 GetIDocumentUndoRedo().ClearRedo();
4307 pUndo.reset(new SwUndoCpyTable(*this));
4311 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
4312 bRet = pSrcTableNd->GetTable().MakeCopy( *this, rInsPos, rBoxes,
4313 bCpyName, rStyleName );
4316 if( pUndo && bRet )
4318 pInsTableNd = GetNodes()[ rInsPos.GetNodeIndex() - 1 ]->FindTableNode();
4320 pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
4321 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4324 else
4326 RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
4327 if( getIDocumentRedlineAccess().IsRedlineOn() )
4328 getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
4329 RedlineFlags::ShowInsert |
4330 RedlineFlags::ShowDelete );
4332 std::unique_ptr<SwUndoTableCpyTable> pUndo;
4333 if (bUndo)
4335 GetIDocumentUndoRedo().ClearRedo();
4336 pUndo.reset(new SwUndoTableCpyTable(*this));
4337 GetIDocumentUndoRedo().DoUndo(false);
4340 rtl::Reference<SwDoc> xCpyDoc(&const_cast<SwDoc&>(pSrcTableNd->GetDoc()));
4341 bool bDelCpyDoc = xCpyDoc == this;
4343 if( bDelCpyDoc )
4345 // Copy the Table into a temporary Doc
4346 xCpyDoc = new SwDoc;
4348 SwPosition aPos( xCpyDoc->GetNodes().GetEndOfContent() );
4349 if( !pSrcTableNd->GetTable().MakeCopy( *xCpyDoc, aPos, rBoxes, true ))
4351 xCpyDoc.clear();
4353 if( pUndo )
4355 GetIDocumentUndoRedo().DoUndo(bUndo);
4357 return false;
4359 aPos.Adjust(SwNodeOffset(-1)); // Set to the Table's EndNode
4360 pSrcTableNd = aPos.GetNode().FindTableNode();
4363 const SwStartNode* pSttNd = rInsPos.GetNode().FindTableBoxStartNode();
4365 rInsPos.nContent.Assign( nullptr, 0 );
4367 // no complex into complex, but copy into or from new model is welcome
4368 if( ( !pSrcTableNd->GetTable().IsTableComplex() || pInsTableNd->GetTable().IsNewModel() )
4369 && ( bDelCpyDoc || !rBoxes.empty() ) )
4371 // Copy the Table "relatively"
4372 const SwSelBoxes* pBoxes;
4373 SwSelBoxes aBoxes;
4375 if( bDelCpyDoc )
4377 SwTableBox* pBox = pInsTableNd->GetTable().GetTableBox(
4378 pSttNd->GetIndex() );
4379 OSL_ENSURE( pBox, "Box is not in this Table" );
4380 aBoxes.insert( pBox );
4381 pBoxes = &aBoxes;
4383 else
4384 pBoxes = &rBoxes;
4386 // Copy Table to the selected Lines
4387 bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
4388 *pBoxes, pUndo.get() );
4390 else
4392 SwNodeIndex aNdIdx( *pSttNd, 1 );
4393 bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
4394 aNdIdx, pUndo.get() );
4397 xCpyDoc.clear();
4399 if( pUndo )
4401 // If the Table could not be copied, delete the Undo object
4402 GetIDocumentUndoRedo().DoUndo(bUndo);
4403 if( bRet || !pUndo->IsEmpty() )
4405 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
4409 if( bCorrPos )
4411 rInsPos.Assign( *pSttNd );
4412 GetNodes().GoNext( &rInsPos );
4414 getIDocumentRedlineAccess().SetRedlineFlags( eOld );
4417 if( bRet )
4419 getIDocumentState().SetModified();
4420 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
4422 return bRet;
4425 bool SwDoc::UnProtectTableCells( SwTable& rTable )
4427 bool bChgd = false;
4428 std::unique_ptr<SwUndoAttrTable> pUndo;
4429 if (GetIDocumentUndoRedo().DoesUndo())
4430 pUndo.reset(new SwUndoAttrTable( *rTable.GetTableNode() ));
4432 SwTableSortBoxes& rSrtBox = rTable.GetTabSortBoxes();
4433 for (size_t i = rSrtBox.size(); i; )
4435 SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
4436 if( pBoxFormat->GetProtect().IsContentProtected() )
4438 pBoxFormat->ResetFormatAttr( RES_PROTECT );
4439 bChgd = true;
4443 if( pUndo && bChgd )
4444 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4445 return bChgd;
4448 void SwDoc::UnProtectCells( const OUString& rName )
4450 SwTableFormat* pFormat = FindTableFormatByName( rName );
4451 if( pFormat )
4453 bool bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
4454 if( bChgd )
4455 getIDocumentState().SetModified();
4459 bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
4461 bool bChgd = false;
4462 if( !rBoxes.empty() )
4464 std::unique_ptr<SwUndoAttrTable> pUndo;
4465 if (GetIDocumentUndoRedo().DoesUndo())
4466 pUndo.reset(new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() ));
4468 std::map<SwFrameFormat*, SwTableBoxFormat*> aFormatsMap;
4469 for (size_t i = rBoxes.size(); i; )
4471 SwTableBox* pBox = rBoxes[ --i ];
4472 SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
4473 if( pBoxFormat->GetProtect().IsContentProtected() )
4475 std::map<SwFrameFormat*, SwTableBoxFormat*>::const_iterator const it =
4476 aFormatsMap.find(pBoxFormat);
4477 if (aFormatsMap.end() != it)
4478 pBox->ChgFrameFormat(it->second);
4479 else
4481 SwTableBoxFormat *const pNewBoxFormat(
4482 static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()));
4483 pNewBoxFormat->ResetFormatAttr( RES_PROTECT );
4484 aFormatsMap.insert(std::make_pair(pBoxFormat, pNewBoxFormat));
4486 bChgd = true;
4490 if( pUndo && bChgd )
4491 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4493 return bChgd;
4496 void SwDoc::UnProtectTables( const SwPaM& rPam )
4498 GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
4500 bool bChgd = false, bHasSel = rPam.HasMark() ||
4501 rPam.GetNext() != &rPam;
4502 sw::TableFrameFormats& rFormats = *GetTableFrameFormats();
4503 SwTable* pTable;
4504 const SwTableNode* pTableNd;
4505 for( auto n = rFormats.size(); n ; )
4506 if( nullptr != (pTable = SwTable::FindTable( rFormats[ --n ])) &&
4507 nullptr != (pTableNd = pTable->GetTableNode() ) &&
4508 pTableNd->GetNodes().IsDocNodes() )
4510 SwNodeOffset nTableIdx = pTableNd->GetIndex();
4512 // Check whether the Table is within the Selection
4513 if( bHasSel )
4515 bool bFound = false;
4516 SwPaM* pTmp = const_cast<SwPaM*>(&rPam);
4517 do {
4518 auto [pStt, pEnd] = pTmp->StartEnd(); // SwPosition*
4519 bFound = pStt->GetNodeIndex() < nTableIdx &&
4520 nTableIdx < pEnd->GetNodeIndex();
4522 } while( !bFound && &rPam != ( pTmp = pTmp->GetNext() ) );
4523 if( !bFound )
4524 continue; // Continue searching
4527 // Lift the protection
4528 bChgd |= UnProtectTableCells( *pTable );
4531 GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
4532 if( bChgd )
4533 getIDocumentState().SetModified();
4536 bool SwDoc::HasTableAnyProtection( const SwPosition* pPos,
4537 const OUString* pTableName,
4538 bool* pFullTableProtection )
4540 bool bHasProtection = false;
4541 SwTable* pTable = nullptr;
4542 if( pTableName )
4543 pTable = SwTable::FindTable( FindTableFormatByName( *pTableName ) );
4544 else if( pPos )
4546 SwTableNode* pTableNd = pPos->GetNode().FindTableNode();
4547 if( pTableNd )
4548 pTable = &pTableNd->GetTable();
4551 if( pTable )
4553 SwTableSortBoxes& rSrtBox = pTable->GetTabSortBoxes();
4554 for (size_t i = rSrtBox.size(); i; )
4556 SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
4557 if( pBoxFormat->GetProtect().IsContentProtected() )
4559 if( !bHasProtection )
4561 bHasProtection = true;
4562 if( !pFullTableProtection )
4563 break;
4564 *pFullTableProtection = true;
4567 else if( bHasProtection && pFullTableProtection )
4569 *pFullTableProtection = false;
4570 break;
4574 return bHasProtection;
4577 SwTableAutoFormat* SwDoc::MakeTableStyle(const OUString& rName, bool bBroadcast)
4579 SwTableAutoFormat aTableFormat(rName);
4580 GetTableStyles().AddAutoFormat(aTableFormat);
4581 SwTableAutoFormat* pTableFormat = GetTableStyles().FindAutoFormat(rName);
4583 getIDocumentState().SetModified();
4585 if (GetIDocumentUndoRedo().DoesUndo())
4587 GetIDocumentUndoRedo().AppendUndo(
4588 std::make_unique<SwUndoTableStyleMake>(rName, *this));
4591 if (bBroadcast)
4592 BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetCreated);
4594 return pTableFormat;
4597 std::unique_ptr<SwTableAutoFormat> SwDoc::DelTableStyle(const OUString& rName, bool bBroadcast)
4599 if (bBroadcast)
4600 BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetErased);
4602 std::unique_ptr<SwTableAutoFormat> pReleasedFormat = GetTableStyles().ReleaseAutoFormat(rName);
4604 std::vector<SwTable*> vAffectedTables;
4605 if (pReleasedFormat)
4607 size_t nTableCount = GetTableFrameFormatCount(true);
4608 for (size_t i=0; i < nTableCount; ++i)
4610 SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
4611 SwTable* pTable = SwTable::FindTable(pFrameFormat);
4612 if (pTable->GetTableStyleName() == pReleasedFormat->GetName())
4614 pTable->SetTableStyleName("");
4615 vAffectedTables.push_back(pTable);
4619 getIDocumentState().SetModified();
4621 if (GetIDocumentUndoRedo().DoesUndo())
4623 GetIDocumentUndoRedo().AppendUndo(
4624 std::make_unique<SwUndoTableStyleDelete>(std::move(pReleasedFormat), std::move(vAffectedTables), *this));
4628 return pReleasedFormat;
4631 void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
4633 SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
4634 if (!pFormat)
4635 return;
4637 SwTableAutoFormat aOldFormat = *pFormat;
4638 *pFormat = rNewFormat;
4639 pFormat->SetName(rName);
4641 size_t nTableCount = GetTableFrameFormatCount(true);
4642 for (size_t i=0; i < nTableCount; ++i)
4644 SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
4645 SwTable* pTable = SwTable::FindTable(pFrameFormat);
4646 if (pTable->GetTableStyleName() == rName)
4647 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
4648 pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
4651 getIDocumentState().SetModified();
4653 if (GetIDocumentUndoRedo().DoesUndo())
4655 GetIDocumentUndoRedo().AppendUndo(
4656 std::make_unique<SwUndoTableStyleUpdate>(*pFormat, aOldFormat, *this));
4660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */