1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
98
99 #ifdef DBG_UTIL
100 #define CHECK_TABLE(t) (t).CheckConsistency();
101 #else
102 #define CHECK_TABLE(t)
103 #endif
104
105 using ::editeng::SvxBorderLine;
106 using namespace ::com::sun::star;
107
108 const sal_Unicode T2T_PARA = 0x0a;
109
lcl_SetDfltBoxAttr(SwFrameFormat & rFormat,sal_uInt8 nId)110 static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
111 {
112 bool bTop = false, bBottom = false, bLeft = false, bRight = false;
113 switch ( nId )
114 {
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;
119 }
120
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 )
126 {
127 aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
128 }
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 );
140 }
141
142 typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
143 typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;
144
145 static void
lcl_SetDfltBoxAttr(SwTableBox & rBox,DfltBoxAttrList_t & rBoxFormatArr,sal_uInt8 const nId,SwTableAutoFormat const * const pAutoFormat=nullptr)146 lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
147 sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
148 {
149 DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
150 if (!pMap)
151 {
152 pMap = new DfltBoxAttrMap_t;
153 rBoxFormatArr[ nId ] = pMap;
154 }
155
156 SwTableBoxFormat* pNewTableBoxFormat = nullptr;
157 SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
158 DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
159 if (pMap->end() != iter)
160 {
161 pNewTableBoxFormat = iter->second;
162 }
163 else
164 {
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 ) );
169
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 );
177
178 (*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
179 }
180 rBox.ChgFrameFormat( pNewTableBoxFormat );
181 }
182
lcl_CreateDfltBoxFormat(SwDoc & rDoc,std::vector<SwTableBoxFormat * > & rBoxFormatArr,sal_uInt16 nCols,sal_uInt8 nId)183 static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
184 sal_uInt16 nCols, sal_uInt8 nId )
185 {
186 if ( !rBoxFormatArr[nId] )
187 {
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;
194 }
195 return rBoxFormatArr[nId];
196 }
197
lcl_CreateAFormatBoxFormat(SwDoc & rDoc,std::vector<SwTableBoxFormat * > & rBoxFormatArr,const SwTableAutoFormat & rAutoFormat,const sal_uInt16 nRows,const sal_uInt16 nCols,sal_uInt8 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 )
201 {
202 if( !rBoxFormatArr[nId] )
203 {
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;
213 }
214 return rBoxFormatArr[nId];
215 }
216
IsIdxInTable(const SwNodeIndex & rIdx)217 SwTableNode* SwDoc::IsIdxInTable( const SwNodeIndex& rIdx ) { return IsInTable(rIdx.GetNode()); }
218
IsInTable(const SwNode & rIdx)219 SwTableNode* SwDoc::IsInTable(const SwNode& rIdx)
220 {
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;
229 }
230
231 /**
232 * Insert a new Box before the InsPos
233 */
InsBoxen(SwTableNode * pTableNd,SwTableLine * pLine,SwTableBoxFormat * pBoxFormat,SwTextFormatColl * pTextColl,const SfxItemSet * pAutoAttr,sal_uInt16 nInsPos,sal_uInt16 nCnt)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 )
241 {
242 if( !nCnt )
243 return false;
244 OSL_ENSURE( pLine, "No valid Line" );
245
246 // Move Index after the Line's last Box
247 SwNodeOffset nIdxPos(0);
248 SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
249 if( !pLine->GetTabBoxes().empty() )
250 {
251 if( nInsPos < pLine->GetTabBoxes().size() )
252 {
253 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
254 pLine->GetTabBoxes()[ nInsPos ] );
255 if( nullptr == pPrvBox )
256 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
257 }
258 else
259 {
260 pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
261 pLine->GetTabBoxes().back() );
262 if( nullptr == pNxtBox )
263 pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
264 }
265 }
266 else
267 {
268 pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
269 if( nullptr == pNxtBox )
270 pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
271 }
272
273 if( !pPrvBox && !pNxtBox )
274 {
275 bool bSetIdxPos = true;
276 if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
277 {
278 const SwTableLine* pTableLn = pLine;
279 while( pTableLn->GetUpper() )
280 pTableLn = pTableLn->GetUpper()->GetUpper();
281
282 if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
283 {
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;
289 }
290 }
291 if( bSetIdxPos )
292 // Tables without content or at the end; move before the End
293 nIdxPos = pTableNd->EndOfSectionIndex();
294 }
295 else if( pNxtBox ) // There is a successor
296 nIdxPos = pNxtBox->GetSttIdx();
297 else // There is a predecessor
298 nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;
299
300 SwNodeIndex aEndIdx( *this, nIdxPos );
301 for( sal_uInt16 n = 0; n < nCnt; ++n )
302 {
303 SwStartNode* pSttNd = new SwStartNode( aEndIdx.GetNode(), SwNodeType::Start,
304 SwTableBoxStartNode );
305 pSttNd->m_pStartOfSection = pTableNd;
306 new SwEndNode( aEndIdx.GetNode(), *pSttNd );
307
308 pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
309
310 SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
311 sal_uInt16 nRealInsPos = nInsPos + n;
312 if (nRealInsPos > rTabBoxes.size())
313 nRealInsPos = rTabBoxes.size();
314
315 rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );
316
317 if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
318 && RES_CONDTXTFMTCOLL != pTextColl->Which()
319 )
320 new SwTextNode( *pSttNd->EndOfSectionNode(), pTextColl, pAutoAttr );
321 else
322 {
323 // Handle Outline numbering correctly!
324 SwTextNode* pTNd = new SwTextNode(
325 *pSttNd->EndOfSectionNode(),
326 GetDoc().GetDfltTextFormatColl(),
327 pAutoAttr );
328 pTNd->ChgFormatColl( pTextColl );
329 }
330 }
331 return true;
332 }
333
334 /**
335 * Insert a new Table
336 */
InsertTable(const SwInsertTableOptions & rInsTableOpts,const SwPosition & rPos,sal_uInt16 nRows,sal_uInt16 nCols,sal_Int16 eAdjust,const SwTableAutoFormat * pTAFormat,const std::vector<sal_uInt16> * pColArr,bool bCalledFromShell,bool bNewModel,const OUString & rTableName)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 )
345 {
346 assert(nRows && "Table without line?");
347 assert(nCols && "Table without rows?");
348
349 {
350 // Do not copy into Footnotes!
351 if( rPos.GetNode() < GetNodes().GetEndOfInserts() &&
352 rPos.GetNode().GetIndex() >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
353 return nullptr;
354
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;
359 }
360
361 OUString aTableName = rTableName;
362 if (aTableName.isEmpty() || FindTableFormatByName(aTableName) != nullptr)
363 aTableName = GetUniqueTableName();
364
365 if( GetIDocumentUndoRedo().DoesUndo() )
366 {
367 GetIDocumentUndoRedo().AppendUndo(
368 std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<sal_uInt16>(eAdjust),
369 rInsTableOpts, pTAFormat, pColArr,
370 aTableName));
371 }
372
373 // Start with inserting the Nodes and get the AutoFormat for the Table
374 SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
375 *pHeadColl = pBodyColl;
376
377 bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );
378
379 if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
380 pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );
381
382 const sal_uInt16 nRowsToRepeat =
383 SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
384 rInsTableOpts.mnRowsToRepeat :
385 0;
386
387 /* Save content node to extract FRAMEDIR from. */
388 const SwContentNode * pContentNd = rPos.GetNode().GetContentNode();
389
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 );
402
403 // Create the Box/Line/Table construct
404 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
405 SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );
406
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)
410 {
411 const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
412 if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
413 {
414 pTableFormat->SetFormatAttr( *pItem );
415 }
416 }
417
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 ));
422
423 // Set USHRT_MAX as the Table's default SSize
424 SwTwips nWidth = USHRT_MAX;
425 if( pColArr )
426 {
427 sal_uInt16 nSttPos = pColArr->front();
428 sal_uInt16 nLastPos = pColArr->back();
429 if( text::HoriOrientation::NONE == eAdjust )
430 {
431 sal_uInt16 nFrameWidth = nLastPos;
432 nLastPos = (*pColArr)[ pColArr->size()-2 ];
433 pTableFormat->SetFormatAttr( SvxLRSpaceItem( nSttPos, nFrameWidth - nLastPos, 0, RES_LR_SPACE ) );
434 }
435 nWidth = nLastPos - nSttPos;
436 }
437 else
438 {
439 nWidth /= nCols;
440 nWidth *= nCols; // to avoid rounding problems
441 }
442 pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
443 if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
444 pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
445
446 // Move the hard PageDesc/PageBreak Attributes if needed
447 SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
448 ->GetContentNode();
449 if( pNextNd && pNextNd->HasSwAttrSet() )
450 {
451 const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
452 if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
453 {
454 pTableFormat->SetFormatAttr( *pItem );
455 pNextNd->ResetAttr( RES_PAGEDESC );
456 pNdSet = pNextNd->GetpSwAttrSet();
457 }
458 const SvxFormatBreakItem* pItem;
459 if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
460 {
461 pTableFormat->SetFormatAttr( *pItem );
462 pNextNd->ResetAttr( RES_BREAK );
463 }
464 }
465
466 SwTable& rNdTable = pTableNd->GetTable();
467 rNdTable.RegisterToFormat( *pTableFormat );
468
469 rNdTable.SetRowsToRepeat( nRowsToRepeat );
470 rNdTable.SetTableModel( bNewModel );
471
472 std::vector<SwTableBoxFormat*> aBoxFormatArr;
473 SwTableBoxFormat* pBoxFormat = nullptr;
474 if( !bDfltBorders && !pTAFormat )
475 {
476 pBoxFormat = MakeTableBoxFormat();
477 pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
478 }
479 else
480 {
481 const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
482 aBoxFormatArr.resize( nBoxArrLen, nullptr );
483 }
484 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
485
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 )
489 {
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 )
494 {
495 SwTableBoxFormat *pBoxF;
496 if( pTAFormat )
497 {
498 sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
499 pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
500 nRows, nCols, nId );
501
502 // Set the Paragraph/Character Attributes if needed
503 if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
504 {
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 );
511 }
512 }
513 else if( bDfltBorders )
514 {
515 sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
516 pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
517 }
518 else
519 pBoxF = pBoxFormat;
520
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 )
524 {
525 nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
526 if( pBoxF->GetFrameSize().GetWidth() != nWidth )
527 {
528 if( pBoxF->HasWriterListeners() ) // Create new Format
529 {
530 SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
531 *pNewFormat = *pBoxF;
532 pBoxF = pNewFormat;
533 }
534 pBoxF->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
535 }
536 }
537
538 SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
539 rBoxes.insert( rBoxes.begin() + i, pBox );
540 aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
541 }
542 }
543 // Insert Frames
544 pTableNd->MakeOwnFrames();
545
546 // To-Do - add 'SwExtraRedlineTable' also ?
547 if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
548 {
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 );
554 }
555
556 getIDocumentState().SetModified();
557 CHECK_TABLE(rNdTable);
558 return &rNdTable;
559 }
560
InsertTable(SwNode & rNd,sal_uInt16 nBoxes,SwTextFormatColl * pContentTextColl,sal_uInt16 nLines,sal_uInt16 nRepeat,SwTextFormatColl * pHeadlineTextColl,const SwAttrSet * pAttrSet)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)
568 {
569 if( !nBoxes )
570 return nullptr;
571
572 // If Lines is given, create the Matrix from Lines and Boxes
573 if( !pHeadlineTextColl || !nLines )
574 pHeadlineTextColl = pContentTextColl;
575
576 SwTableNode * pTableNd = new SwTableNode( rNd );
577 SwEndNode* pEndNd = new SwEndNode( rNd, *pTableNd );
578
579 if( !nLines ) // For the for loop
580 ++nLines;
581
582 SwTextFormatColl* pTextColl = pHeadlineTextColl;
583 for( sal_uInt16 nL = 0; nL < nLines; ++nL )
584 {
585 for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
586 {
587 SwStartNode* pSttNd = new SwStartNode( *pEndNd, SwNodeType::Start,
588 SwTableBoxStartNode );
589 pSttNd->m_pStartOfSection = pTableNd;
590
591 SwTextNode * pTmpNd = new SwTextNode( *pEndNd, pTextColl );
592
593 // #i60422# Propagate some more attributes.
594 const SfxPoolItem* pItem = nullptr;
595 if ( nullptr != pAttrSet )
596 {
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 };
602
603 const sal_uInt16* pIdx = aPropagateItems;
604 while ( *pIdx != 0 )
605 {
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;
610 }
611 }
612
613 new SwEndNode( *pEndNd, *pSttNd );
614 }
615 if ( nL + 1 >= nRepeat )
616 pTextColl = pContentTextColl;
617 }
618 return pTableNd;
619 }
620
621 /**
622 * Text to Table
623 */
TextToTable(const SwInsertTableOptions & rInsTableOpts,const SwPaM & rRange,sal_Unicode cCh,sal_Int16 eAdjust,const SwTableAutoFormat * pTAFormat)624 const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
625 const SwPaM& rRange, sal_Unicode cCh,
626 sal_Int16 eAdjust,
627 const SwTableAutoFormat* pTAFormat )
628 {
629 // See if the selection contains a Table
630 auto [pStt, pEnd] = rRange.StartEnd(); // SwPosition*
631 {
632 SwNodeOffset nCnt = pStt->GetNodeIndex();
633 for( ; nCnt <= pEnd->GetNodeIndex(); ++nCnt )
634 if( !GetNodes()[ nCnt ]->IsTextNode() )
635 return nullptr;
636 }
637
638 if (GetIDocumentUndoRedo().DoesUndo())
639 {
640 GetIDocumentUndoRedo().StartUndo(SwUndoId::TEXTTOTABLE, nullptr);
641 }
642
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);
647
648 // Save first node in the selection if it is a context node
649 SwContentNode * pSttContentNd = pStt->GetNode().GetContentNode();
650
651 SwPaM aOriginal( *pStt, *pEnd );
652 pStt = aOriginal.GetMark();
653 pEnd = aOriginal.GetPoint();
654
655 SwUndoTextToTable* pUndo = nullptr;
656 if( GetIDocumentUndoRedo().DoesUndo() )
657 {
658 pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
659 o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
660 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
661
662 // Do not add splitting the TextNode to the Undo history
663 GetIDocumentUndoRedo().DoUndo( false );
664 }
665
666 ::PaMCorrAbs( aOriginal, *pEnd );
667
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 );
672
673 bool bEndContent = 0 != pEnd->GetContentIndex();
674
675 // Do not split at the End of a Line (except at the End of the Doc)
676 if( bEndContent )
677 {
678 if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
679 || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
680 {
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;
686 }
687 else
688 ++aRg.aEnd;
689 }
690
691 if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
692 {
693 OSL_FAIL( "empty range" );
694 ++aRg.aEnd;
695 }
696
697 // We always use Upper to insert the Table
698 SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
699
700 GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );
701
702 // Create the Box/Line/Table construct
703 SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
704 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
705 SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
706
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 ));
713
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)
718 {
719 const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
720 if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
721 {
722 pTableFormat->SetFormatAttr( *pItem );
723 }
724 }
725
726 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
727 //until after RegisterToFormat is completed
728 bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
729 getIDocumentState().SetEnableSetModified(false);
730
731 SwTableNode* pTableNd = GetNodes().TextToTable(
732 aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
733 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );
734
735 SwTable& rNdTable = pTableNd->GetTable();
736
737 const sal_uInt16 nRowsToRepeat =
738 SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
739 rInsTableOpts.mnRowsToRepeat :
740 0;
741 rNdTable.SetRowsToRepeat(nRowsToRepeat);
742
743 bool bUseBoxFormat = false;
744 if( !pBoxFormat->HasWriterListeners() )
745 {
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;
752 }
753
754 // Set Orientation in the Table's Format
755 pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
756 rNdTable.RegisterToFormat(*pTableFormat);
757
758 if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
759 {
760 sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
761 std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
762 std::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
763 if( bUseBoxFormat )
764 {
765 aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
766 }
767 else
768 {
769 aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
770 }
771
772 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
773
774 SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
775
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 )
780 {
781 SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
782 const SwTableBoxes::size_type nCols = rBoxes.size();
783 for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
784 {
785 SwTableBox* pBox = rBoxes[ i ];
786 bool bChgSz = false;
787
788 if( pTAFormat )
789 {
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
797 {
798 bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
799 pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
800 *pTAFormat, USHRT_MAX, USHRT_MAX, nId );
801 }
802
803 // Set Paragraph/Character Attributes if needed
804 if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
805 {
806 aCharSet.ClearItem();
807 pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
808 SwTableAutoFormatUpdateFlags::Char, nullptr );
809 if( aCharSet.Count() )
810 {
811 SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
812 SwNodeOffset nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
813 for( ; nSttNd < nEndNd; ++nSttNd )
814 {
815 SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
816 if( pNd )
817 {
818 if( pHistory )
819 {
820 SwRegHistory aReg( pNd, *pNd, pHistory );
821 pNd->SetAttr( aCharSet );
822 }
823 else
824 pNd->SetAttr( aCharSet );
825 }
826 }
827 }
828 }
829 }
830 else
831 {
832 sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
833 if( bUseBoxFormat )
834 ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
835 else
836 {
837 bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
838 pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
839 USHRT_MAX, nId );
840 }
841 }
842
843 if( !bUseBoxFormat )
844 {
845 if( bChgSz )
846 pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
847 pBox->ChgFrameFormat( pBoxF );
848 }
849 }
850 }
851
852 if( bUseBoxFormat )
853 {
854 for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
855 {
856 delete (*aBoxFormatArr1)[ i ];
857 }
858 }
859 }
860
861 // Check the boxes for numbers
862 if( IsInsTableFormatNum() )
863 {
864 for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
865 {
866 ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
867 }
868 }
869
870 SwNodeOffset nIdx = pTableNd->GetIndex();
871 aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
872
873 {
874 SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
875 rTmp.DeleteMark();
876 rTmp.GetPoint()->Assign( *pTableNd );
877 SwNodes::GoNext(rTmp.GetPoint());
878 }
879
880 if( pUndo )
881 {
882 GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
883 }
884
885 getIDocumentState().SetEnableSetModified(bEnableSetModified);
886 getIDocumentState().SetModified();
887 getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
888 return &rNdTable;
889 }
890
lcl_RemoveBreaksTable(SwTableNode & rNode,SwTableFormat * const pTableFormat)891 static void lcl_RemoveBreaksTable(SwTableNode & rNode, SwTableFormat *const pTableFormat)
892 {
893 // delete old layout frames, new ones need to be created...
894 rNode.DelFrames(nullptr);
895
896 // remove PageBreaks/PageDesc/ColBreak
897 SwFrameFormat & rFormat(*rNode.GetTable().GetFrameFormat());
898
899 if (const SvxFormatBreakItem* pItem = rFormat.GetItemIfSet(RES_BREAK, false))
900 {
901 if (pTableFormat)
902 {
903 pTableFormat->SetFormatAttr(*pItem);
904 }
905 rFormat.ResetFormatAttr(RES_BREAK);
906 }
907
908 SwFormatPageDesc const*const pPageDescItem(rFormat.GetItemIfSet(RES_PAGEDESC, false));
909 if (pPageDescItem && pPageDescItem->GetPageDesc())
910 {
911 if (pTableFormat)
912 {
913 pTableFormat->SetFormatAttr(*pPageDescItem);
914 }
915 rFormat.ResetFormatAttr(RES_PAGEDESC);
916 }
917 }
918
lcl_RemoveBreaks(SwContentNode & rNode,SwTableFormat * const pTableFormat)919 static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
920 {
921 // delete old layout frames, new ones need to be created...
922 rNode.DelFrames(nullptr);
923
924 if (!rNode.IsTextNode())
925 {
926 return;
927 }
928
929 SwTextNode & rTextNode = *rNode.GetTextNode();
930 // remove PageBreaks/PageDesc/ColBreak
931 SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
932 if (!pSet)
933 return;
934
935 if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
936 {
937 if (pTableFormat)
938 {
939 pTableFormat->SetFormatAttr(*pItem);
940 }
941 rTextNode.ResetAttr(RES_BREAK);
942 pSet = rTextNode.GetpSwAttrSet();
943 }
944
945 const SwFormatPageDesc* pPageDescItem;
946 if (pSet
947 && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
948 && pPageDescItem->GetPageDesc())
949 {
950 if (pTableFormat)
951 {
952 pTableFormat->SetFormatAttr(*pPageDescItem);
953 }
954 rTextNode.ResetAttr(RES_PAGEDESC);
955 }
956 }
957
958 /**
959 * balance lines in table, insert empty boxes so all lines have the size
960 */
961 static void
lcl_BalanceTable(SwTable & rTable,size_t const nMaxBoxes,SwTableNode & rTableNd,SwTableBoxFormat & rBoxFormat,SwTextFormatColl & rTextColl,SwUndoTextToTable * const pUndo,std::vector<sal_uInt16> * const pPositions)962 lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
963 SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
964 SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
965 {
966 for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
967 {
968 SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
969 size_t const nBoxes = pCurrLine->GetTabBoxes().size();
970 if (nMaxBoxes != nBoxes)
971 {
972 rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
973 nullptr, nBoxes, nMaxBoxes - nBoxes);
974
975 if (pUndo)
976 {
977 for (size_t i = nBoxes; i < nMaxBoxes; ++i)
978 {
979 pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
980 }
981 }
982
983 // if the first line is missing boxes, the width array is useless!
984 if (!n && pPositions)
985 {
986 pPositions->clear();
987 }
988 }
989 }
990 }
991
992 static void
lcl_SetTableBoxWidths(SwTable & rTable,size_t const nMaxBoxes,SwTableBoxFormat & rBoxFormat,SwDoc & rDoc,std::vector<sal_uInt16> * const pPositions)993 lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
994 SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
995 std::vector<sal_uInt16> *const pPositions)
996 {
997 if (pPositions && !pPositions->empty())
998 {
999 SwTableLines& rLns = rTable.GetTabLines();
1000 sal_uInt16 nLastPos = 0;
1001 for (size_t n = 0; n < pPositions->size(); ++n)
1002 {
1003 SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
1004 pNewFormat->SetFormatAttr(
1005 SwFormatFrameSize(SwFrameSize::Variable, (*pPositions)[n] - nLastPos));
1006 for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
1007 {
1008 // Have to do an Add here, because the BoxFormat
1009 // is still needed by the caller
1010 pNewFormat->Add(*rLns[nTmpLine]->GetTabBoxes()[n]);
1011 }
1012
1013 nLastPos = (*pPositions)[ n ];
1014 }
1015
1016 // propagate size upwards from format, so the table gets the right size
1017 SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
1018 "who is still registered in the format?");
1019 rBoxFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nLastPos ));
1020 }
1021 else
1022 {
1023 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
1024 rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
1025 }
1026 }
1027
TextToTable(const SwNodeRange & rRange,sal_Unicode cCh,SwTableFormat * pTableFormat,SwTableLineFormat * pLineFormat,SwTableBoxFormat * pBoxFormat,SwTextFormatColl * pTextColl,SwUndoTextToTable * pUndo)1028 SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
1029 SwTableFormat* pTableFormat,
1030 SwTableLineFormat* pLineFormat,
1031 SwTableBoxFormat* pBoxFormat,
1032 SwTextFormatColl* pTextColl,
1033 SwUndoTextToTable* pUndo )
1034 {
1035 if( rRange.aStart >= rRange.aEnd )
1036 return nullptr;
1037
1038 SwTableNode * pTableNd = new SwTableNode( rRange.aStart.GetNode() );
1039 new SwEndNode( rRange.aEnd.GetNode(), *pTableNd );
1040
1041 SwDoc& rDoc = GetDoc();
1042 std::vector<sal_uInt16> aPosArr;
1043 SwTable& rTable = pTableNd->GetTable();
1044 SwTableBox* pBox;
1045 sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
1046
1047 SwNodeIndex aSttIdx( *pTableNd, 1 );
1048 SwNodeIndex aEndIdx( rRange.aEnd, -1 );
1049 for( nLines = 0, nBoxes = 0;
1050 aSttIdx.GetIndex() < aEndIdx.GetIndex();
1051 aSttIdx += SwNodeOffset(2), nLines++, nBoxes = 0 )
1052 {
1053 SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
1054 OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );
1055
1056 if( !nLines && 0x0b == cCh )
1057 {
1058 cCh = 0x09;
1059
1060 // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
1061 SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
1062 if( aFInfo.IsOneLine() ) // only makes sense in this case
1063 {
1064 OUString const& rText(pTextNd->GetText());
1065 for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
1066 {
1067 if (rText[nChPos] == cCh)
1068 {
1069 // sw_redlinehide: no idea if this makes any sense...
1070 TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
1071 aPosArr.push_back( o3tl::narrowing<sal_uInt16>(
1072 aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
1073 }
1074 }
1075
1076 aPosArr.push_back(
1077 o3tl::narrowing<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
1078 aFInfo.GetFrame()->getFramePrintArea().Bottom() :
1079 aFInfo.GetFrame()->getFramePrintArea().Right()) );
1080
1081 }
1082 }
1083
1084 lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);
1085
1086 // Set the TableNode as StartNode for all TextNodes in the Table
1087 pTextNd->m_pStartOfSection = pTableNd;
1088
1089 SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
1090 rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
1091
1092 SwStartNode* pSttNd;
1093 SwPosition aCntPos( aSttIdx, pTextNd, 0);
1094
1095 const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1096 pContentStore->Save(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);
1097
1098 if( T2T_PARA != cCh )
1099 {
1100 for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
1101 {
1102 if (pTextNd->GetText()[nChPos] == cCh)
1103 {
1104 aCntPos.SetContent(nChPos);
1105 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
1106 [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
1107 {
1108 if (!pContentStore->Empty())
1109 {
1110 pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
1111 }
1112 });
1113 SwContentNode *const pNewNd =
1114 pTextNd->SplitContentNode(aCntPos, &restoreFunc);
1115
1116 // Delete separator and correct search string
1117 pTextNd->EraseText( aCntPos, 1 );
1118 nChPos = 0;
1119
1120 // Set the TableNode as StartNode for all TextNodes in the Table
1121 const SwNodeIndex aTmpIdx( aCntPos.GetNode(), -1 );
1122 pSttNd = new SwStartNode( aTmpIdx.GetNode(), SwNodeType::Start,
1123 SwTableBoxStartNode );
1124 new SwEndNode( aCntPos.GetNode(), *pSttNd );
1125 pNewNd->m_pStartOfSection = pSttNd;
1126
1127 // Assign Section to the Box
1128 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1129 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1130 }
1131 else
1132 {
1133 ++nChPos;
1134 }
1135 }
1136 }
1137
1138 // Now for the last substring
1139 if( !pContentStore->Empty())
1140 pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );
1141
1142 pSttNd = new SwStartNode( aCntPos.GetNode(), SwNodeType::Start, SwTableBoxStartNode );
1143 const SwNodeIndex aTmpIdx( aCntPos.GetNode(), 1 );
1144 new SwEndNode( aTmpIdx.GetNode(), *pSttNd );
1145 pTextNd->m_pStartOfSection = pSttNd;
1146
1147 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1148 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1149 if( nMaxBoxes < nBoxes )
1150 nMaxBoxes = nBoxes;
1151 }
1152
1153 lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
1154 pUndo, &aPosArr);
1155 lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, rDoc, &aPosArr);
1156
1157 return pTableNd;
1158 }
1159
TextToTable(const std::vector<std::vector<SwNodeRange>> & rTableNodes)1160 const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
1161 {
1162 if (rTableNodes.empty())
1163 return nullptr;
1164
1165 const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();
1166
1167 if (rFirstRange.empty())
1168 return nullptr;
1169
1170 const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();
1171
1172 if (rLastRange.empty())
1173 return nullptr;
1174
1175 /* Save first node in the selection if it is a content node. */
1176 SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();
1177
1178 const SwNodeRange& rStartRange = *rFirstRange.begin();
1179 const SwNodeRange& rEndRange = *rLastRange.rbegin();
1180
1181 //!!! not necessarily TextNodes !!!
1182 SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
1183 const SwPosition *pStt = aOriginal.GetMark();
1184 SwPosition *pEnd = aOriginal.GetPoint();
1185
1186 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
1187 if (bUndo)
1188 {
1189 // Do not add splitting the TextNode to the Undo history
1190 GetIDocumentUndoRedo().DoUndo(false);
1191 }
1192
1193 ::PaMCorrAbs( aOriginal, *pEnd );
1194
1195 // make sure that the range is on Node Edges
1196 SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() );
1197 if( pStt->GetContentIndex() )
1198 getIDocumentContentOperations().SplitNode( *pStt, false );
1199
1200 bool bEndContent = 0 != pEnd->GetContentIndex();
1201
1202 // Do not split at the End of a Line (except at the End of the Doc)
1203 if( bEndContent )
1204 {
1205 if( pEnd->GetNode().GetContentNode()->Len() != pEnd->GetContentIndex()
1206 || pEnd->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
1207 {
1208 getIDocumentContentOperations().SplitNode( *pEnd, false );
1209 pEnd->Adjust(SwNodeOffset(-1));
1210 // A Node and at the End?
1211 if( pStt->GetNodeIndex() >= pEnd->GetNodeIndex() )
1212 --aRg.aStart;
1213 }
1214 else
1215 ++aRg.aEnd;
1216 }
1217
1218 assert(aRg.aEnd.GetNode() == pEnd->GetNode());
1219 assert(aRg.aStart.GetNode() == pStt->GetNode());
1220 if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
1221 {
1222 OSL_FAIL( "empty range" );
1223 ++aRg.aEnd;
1224 }
1225
1226
1227 {
1228 // TODO: this is not Undo-able - only good enough for file import
1229 IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
1230 SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
1231 SwNodeIndex const* pPrev(&prev);
1232 // pPrev could point to non-textnode now
1233 for (const auto& rRow : rTableNodes)
1234 {
1235 for (const auto& rCell : rRow)
1236 {
1237 assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
1238 SwPaM pam(rCell.aStart, 0, *pPrev,
1239 (pPrev->GetNode().IsContentNode())
1240 ? pPrev->GetNode().GetContentNode()->Len() : 0);
1241 rIDRA.SplitRedline(pam);
1242 pPrev = &rCell.aEnd;
1243 }
1244 }
1245 // another one to break between last cell and node after table
1246 SwPaM pam(pPrev->GetNode(), SwNodeOffset(+1), 0,
1247 pPrev->GetNode(), SwNodeOffset(0),
1248 (pPrev->GetNode().IsContentNode())
1249 ? pPrev->GetNode().GetContentNode()->Len() : 0);
1250 rIDRA.SplitRedline(pam);
1251 }
1252
1253 // We always use Upper to insert the Table
1254 SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
1255
1256 GetIDocumentUndoRedo().DoUndo(bUndo);
1257
1258 // Create the Box/Line/Table construct
1259 SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
1260 SwTableLineFormat* pLineFormat = MakeTableLineFormat();
1261 SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
1262
1263 // All Lines have a left-to-right Fill Order
1264 pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
1265 // The Table's SSize is USHRT_MAX
1266 pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
1267
1268 /* If the first node in the selection is a context node and if it
1269 has an item FRAMEDIR set (no default) propagate the item to the
1270 replacing table. */
1271 if (pSttContentNd)
1272 {
1273 const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
1274 if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
1275 {
1276 pTableFormat->SetFormatAttr( *pItem );
1277 }
1278 }
1279
1280 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
1281 //until after RegisterToFormat is completed
1282 bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
1283 getIDocumentState().SetEnableSetModified(false);
1284
1285 SwTableNode* pTableNd = GetNodes().TextToTable(
1286 rTableNodes, pTableFormat, pLineFormat, pBoxFormat );
1287
1288 SwTable& rNdTable = pTableNd->GetTable();
1289 rNdTable.RegisterToFormat(*pTableFormat);
1290
1291 if( !pBoxFormat->HasWriterListeners() )
1292 {
1293 // The Box's Formats already have the right size, we must only set
1294 // the right Border/AutoFormat.
1295 pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
1296 delete pBoxFormat;
1297 }
1298
1299 SwNodeOffset nIdx = pTableNd->GetIndex();
1300 aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
1301
1302 getIDocumentState().SetEnableSetModified(bEnableSetModified);
1303 getIDocumentState().SetModified();
1304 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1305 return &rNdTable;
1306 }
1307
ExpandRangeForTableBox(const SwNodeRange & rRange,std::optional<SwNodeRange> & rExpandedRange)1308 void SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange, std::optional<SwNodeRange>& rExpandedRange)
1309 {
1310 bool bChanged = false;
1311
1312 SwNodeIndex aNewStart = rRange.aStart;
1313 SwNodeIndex aNewEnd = rRange.aEnd;
1314
1315 SwNodeIndex aEndIndex = rRange.aEnd;
1316 SwNodeIndex aIndex = rRange.aStart;
1317
1318 while (aIndex < aEndIndex)
1319 {
1320 SwNode& rNode = aIndex.GetNode();
1321
1322 if (rNode.IsStartNode())
1323 {
1324 // advance aIndex to the end node of this start node
1325 SwNode * pEndNode = rNode.EndOfSectionNode();
1326 aIndex = *pEndNode;
1327
1328 if (aIndex > aNewEnd)
1329 {
1330 aNewEnd = aIndex;
1331 bChanged = true;
1332 }
1333 }
1334 else if (rNode.IsEndNode())
1335 {
1336 SwNode * pStartNode = rNode.StartOfSectionNode();
1337 if (pStartNode->GetIndex() < aNewStart.GetIndex())
1338 {
1339 aNewStart = *pStartNode;
1340 bChanged = true;
1341 }
1342 }
1343
1344 if (aIndex < aEndIndex)
1345 ++aIndex;
1346 }
1347
1348 SwNode * pNode = &aIndex.GetNode();
1349 while (pNode->IsEndNode() && aIndex < Count() - 1)
1350 {
1351 SwNode * pStartNode = pNode->StartOfSectionNode();
1352 aNewStart = *pStartNode;
1353 aNewEnd = aIndex;
1354 bChanged = true;
1355
1356 ++aIndex;
1357 pNode = &aIndex.GetNode();
1358 }
1359
1360 if (bChanged)
1361 rExpandedRange.emplace(aNewStart, aNewEnd);
1362 }
1363
1364 static void
lcl_SetTableBoxWidths2(SwTable & rTable,size_t const nMaxBoxes,SwTableBoxFormat & rBoxFormat,SwDoc & rDoc)1365 lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
1366 SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
1367 {
1368 // rhbz#820283, fdo#55462: set default box widths so table width is covered
1369 SwTableLines & rLines = rTable.GetTabLines();
1370 for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
1371 {
1372 SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
1373 assert(!rBoxes.empty()); // ensured by convertToTable
1374 size_t const nMissing = nMaxBoxes - rBoxes.size();
1375 if (nMissing)
1376 {
1377 // default width for box at the end of an incomplete line
1378 SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
1379 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
1380 pNewFormat->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable,
1381 nWidth * (nMissing + 1)) );
1382 pNewFormat->Add(*rBoxes.back());
1383 }
1384 }
1385 size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
1386 // default width for all boxes not at the end of an incomplete line
1387 rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
1388 }
1389
TextToTable(const SwNodes::TableRanges_t & rTableNodes,SwTableFormat * pTableFormat,SwTableLineFormat * pLineFormat,SwTableBoxFormat * pBoxFormat)1390 SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
1391 SwTableFormat* pTableFormat,
1392 SwTableLineFormat* pLineFormat,
1393 SwTableBoxFormat* pBoxFormat )
1394 {
1395 if( rTableNodes.empty() )
1396 return nullptr;
1397
1398 SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart.GetNode() );
1399 //insert the end node after the last text node
1400 SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
1401 ++aInsertIndex;
1402
1403 //!! ownership will be transferred in c-tor to SwNodes array.
1404 //!! Thus no real problem here...
1405 new SwEndNode( aInsertIndex.GetNode(), *pTableNd );
1406
1407 SwDoc& rDoc = GetDoc();
1408 SwTable& rTable = pTableNd->GetTable();
1409 SwTableBox* pBox;
1410 sal_uInt16 nLines, nMaxBoxes = 0;
1411
1412 SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
1413 // delete frames of all contained content nodes
1414 for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
1415 {
1416 SwNode* pNode(&aNodeIndex.GetNode());
1417 while (pNode->IsSectionNode()) // could be ToX field in table
1418 {
1419 pNode = pNode->GetNodes()[pNode->GetIndex()+1];
1420 }
1421 if (pNode->IsTableNode())
1422 {
1423 lcl_RemoveBreaksTable(static_cast<SwTableNode&>(*pNode),
1424 (0 == nLines) ? pTableFormat : nullptr);
1425 }
1426 else if (pNode->IsContentNode())
1427 {
1428 lcl_RemoveBreaks(static_cast<SwContentNode&>(*pNode),
1429 (0 == nLines) ? pTableFormat : nullptr);
1430 }
1431 }
1432
1433 nLines = 0;
1434 for( const auto& rRow : rTableNodes )
1435 {
1436 sal_uInt16 nBoxes = 0;
1437 SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
1438 rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
1439
1440 for( const auto& rCell : rRow )
1441 {
1442 SwNodeIndex aCellEndIdx(rCell.aEnd);
1443 ++aCellEndIdx;
1444 SwStartNode* pSttNd = new SwStartNode( rCell.aStart.GetNode(), SwNodeType::Start,
1445 SwTableBoxStartNode );
1446
1447 // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
1448 // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
1449 // So this statement is only executed for the side-effect.
1450 new SwEndNode( aCellEndIdx.GetNode(), *pSttNd );
1451
1452 //set the start node on all node of the current cell
1453 SwNodeIndex aCellNodeIdx = rCell.aStart;
1454 for(;aCellNodeIdx <= rCell.aEnd; ++aCellNodeIdx )
1455 {
1456 aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
1457 //skip start/end node pairs
1458 if( aCellNodeIdx.GetNode().IsStartNode() )
1459 aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
1460 }
1461
1462 // assign Section to the Box
1463 pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
1464 pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
1465 }
1466 if( nMaxBoxes < nBoxes )
1467 nMaxBoxes = nBoxes;
1468
1469 nLines++;
1470 }
1471
1472 lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);
1473
1474 return pTableNd;
1475 }
1476
1477 /**
1478 * Table to Text
1479 */
TableToText(const SwTableNode * pTableNd,sal_Unicode cCh)1480 bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
1481 {
1482 if( !pTableNd )
1483 return false;
1484
1485 // #i34471#
1486 // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
1487 // the table cursor.
1488 SwEditShell* pESh = GetEditShell();
1489 if (pESh && pESh->IsTableMode())
1490 pESh->ClearMark();
1491
1492 SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
1493 std::unique_ptr<SwUndoTableToText> pUndo;
1494 SwNodeRange* pUndoRg = nullptr;
1495 if (GetIDocumentUndoRedo().DoesUndo())
1496 {
1497 GetIDocumentUndoRedo().ClearRedo();
1498 pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
1499 pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
1500 }
1501
1502 const_cast<SwTable*>(&pTableNd->GetTable())->SwitchFormulasToExternalRepresentation();
1503
1504 bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
1505 if( pUndoRg )
1506 {
1507 ++pUndoRg->aStart;
1508 --pUndoRg->aEnd;
1509 pUndo->SetRange( *pUndoRg );
1510 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
1511 delete pUndoRg;
1512 }
1513
1514 if( bRet )
1515 getIDocumentState().SetModified();
1516
1517 return bRet;
1518 }
1519
1520 namespace {
1521
1522 /**
1523 * Use the ForEach method from PtrArray to recreate Text from a Table.
1524 * The Boxes can also contain Lines!
1525 */
1526 struct DelTabPara
1527 {
1528 SwTextNode* pLastNd;
1529 SwNodes& rNds;
1530 SwUndoTableToText* pUndo;
1531 sal_Unicode cCh;
1532
DelTabPara__anone93ec3e60211::DelTabPara1533 DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
1534 pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
1535 };
1536
1537 }
1538
1539 // Forward declare so that the Lines and Boxes can use recursion
1540 static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );
1541
lcl_DelLine(SwTableLine * pLine,DelTabPara * pPara)1542 static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
1543 {
1544 assert(pPara && "The parameters are missing!");
1545 DelTabPara aPara( *pPara );
1546 for( auto& rpBox : pLine->GetTabBoxes() )
1547 lcl_DelBox(rpBox, &aPara );
1548 if( pLine->GetUpper() ) // Is there a parent Box?
1549 // Return the last TextNode
1550 pPara->pLastNd = aPara.pLastNd;
1551 }
1552
lcl_DelBox(SwTableBox * pBox,DelTabPara * pDelPara)1553 static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
1554 {
1555 assert(pDelPara && "The parameters are missing");
1556
1557 // Delete the Box's Lines
1558 if( !pBox->GetTabLines().empty() )
1559 {
1560 for( SwTableLine* pLine : pBox->GetTabLines() )
1561 lcl_DelLine( pLine, pDelPara );
1562 }
1563 else
1564 {
1565 SwDoc& rDoc = pDelPara->rNds.GetDoc();
1566 SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(0),
1567 *pBox->GetSttNd()->EndOfSectionNode() );
1568 // Delete the Section
1569 pDelPara->rNds.SectionUp( &aDelRg );
1570 const SwTextNode* pCurTextNd = nullptr;
1571 if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
1572 pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
1573 if (nullptr != pCurTextNd)
1574 {
1575 // Join the current text node with the last from the previous box if possible
1576 SwNodeOffset nNdIdx = aDelRg.aStart.GetIndex();
1577 --aDelRg.aStart;
1578 if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
1579 {
1580 // Inserting the separator
1581 SwContentIndex aCntIdx( pDelPara->pLastNd,
1582 pDelPara->pLastNd->GetText().getLength());
1583 pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
1584 SwInsertFlags::EMPTYEXPAND );
1585 if( pDelPara->pUndo )
1586 pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
1587 aCntIdx.GetIndex() );
1588
1589 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
1590 const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
1591 pContentStore->Save(rDoc, nNdIdx, SAL_MAX_INT32);
1592
1593 pDelPara->pLastNd->JoinNext();
1594
1595 if( !pContentStore->Empty() )
1596 pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
1597 }
1598 else if( pDelPara->pUndo )
1599 {
1600 ++aDelRg.aStart;
1601 pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
1602 }
1603 }
1604 else if( pDelPara->pUndo )
1605 pDelPara->pUndo->AddBoxPos( rDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
1606 --aDelRg.aEnd;
1607 pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();
1608
1609 // Do not take over the NumberFormatting's adjustment
1610 if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
1611 pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
1612 }
1613 }
1614
TableToText(const SwNodeRange & rRange,sal_Unicode cCh,SwUndoTableToText * pUndo)1615 bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
1616 SwUndoTableToText* pUndo )
1617 {
1618 // Is a Table selected?
1619 if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
1620 return false;
1621 SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
1622 if (nullptr == pTableNd ||
1623 &rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
1624 return false;
1625
1626 // If the Table was alone in a Section, create the Frames via the Table's Upper
1627 std::optional<SwNode2LayoutSaveUpperFrames> oNode2Layout;
1628 SwNode* pFrameNd = FindPrvNxtFrameNode( rRange.aStart.GetNode(), &rRange.aEnd.GetNode() );
1629 SwNodeIndex aFrameIdx( pFrameNd ? *pFrameNd: rRange.aStart.GetNode() );
1630 if( !pFrameNd )
1631 // Collect all Uppers
1632 oNode2Layout.emplace(*pTableNd);
1633
1634 // Delete the Frames
1635 pTableNd->DelFrames();
1636
1637 // "Delete" the Table and merge all Lines/Boxes
1638 DelTabPara aDelPara( *this, cCh, pUndo );
1639 for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
1640 lcl_DelLine( pLine, &aDelPara );
1641
1642 // We just created a TextNode with fitting separator for every TableLine.
1643 // Now we only need to delete the TableSection and create the Frames for the
1644 // new TextNode.
1645 SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );
1646
1647 // If the Table has PageDesc/Break Attributes, carry them over to the
1648 // first Text Node
1649 {
1650 // What about UNDO?
1651 const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
1652 const SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
1653 const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );
1654
1655 if( pBreak || pDesc )
1656 {
1657 SwNodeIndex aIdx( *pTableNd );
1658 SwContentNode* pCNd = GoNext( &aIdx );
1659 if( pBreak )
1660 pCNd->SetAttr( *pBreak );
1661 if( pDesc )
1662 pCNd->SetAttr( *pDesc );
1663 }
1664 }
1665
1666 SectionUp( &aDelRg ); // Delete this Section and by that the Table
1667 // #i28006#
1668 SwNodeOffset nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
1669 if( !pFrameNd )
1670 {
1671 oNode2Layout->RestoreUpperFrames( *this,
1672 aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
1673 oNode2Layout.reset();
1674 }
1675 else
1676 {
1677 SwContentNode *pCNd;
1678 SwSectionNode *pSNd;
1679 while( aDelRg.aStart.GetIndex() < nEnd )
1680 {
1681 pCNd = aDelRg.aStart.GetNode().GetContentNode();
1682 if( nullptr != pCNd )
1683 {
1684 if( pFrameNd->IsContentNode() )
1685 static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
1686 else if( pFrameNd->IsTableNode() )
1687 static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
1688 else if( pFrameNd->IsSectionNode() )
1689 static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
1690 pFrameNd = pCNd;
1691 }
1692 else
1693 {
1694 pSNd = aDelRg.aStart.GetNode().GetSectionNode();
1695 if( pSNd )
1696 {
1697 if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
1698 {
1699 pSNd->MakeOwnFrames(&aFrameIdx, &aDelRg.aEnd);
1700 break;
1701 }
1702 aDelRg.aStart = *pSNd->EndOfSectionNode();
1703 }
1704 }
1705 ++aDelRg.aStart;
1706 }
1707 }
1708
1709 // #i28006# Fly frames have to be restored even if the table was
1710 // #alone in the section
1711 for(sw::SpzFrameFormat* pFly: *GetDoc().GetSpzFrameFormats())
1712 {
1713 SwFrameFormat *const pFormat = pFly;
1714 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1715 SwNode const*const pAnchorNode = rAnchor.GetAnchorNode();
1716 if (pAnchorNode &&
1717 ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
1718 (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
1719 nStt <= pAnchorNode->GetIndex() &&
1720 pAnchorNode->GetIndex() < nEnd )
1721 {
1722 pFormat->MakeFrames();
1723 }
1724 }
1725
1726 return true;
1727 }
1728
1729 /**
1730 * Inserting Columns/Rows
1731 */
InsertCol(const SwCursor & rCursor,sal_uInt16 nCnt,bool bBehind)1732 void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
1733 {
1734 if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
1735 return;
1736
1737 // Find the Boxes via the Layout
1738 SwSelBoxes aBoxes;
1739 ::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
1740
1741 if( !aBoxes.empty() )
1742 InsertCol( aBoxes, nCnt, bBehind );
1743 }
1744
InsertCol(const SwSelBoxes & rBoxes,sal_uInt16 nCnt,bool bBehind,bool bInsertDummy)1745 bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
1746 {
1747 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
1748 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
1749 if( !pTableNd )
1750 return false;
1751
1752 SwTable& rTable = pTableNd->GetTable();
1753 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
1754 return false;
1755
1756 SwTableSortBoxes aTmpLst;
1757 std::unique_ptr<SwUndoTableNdsChg> pUndo;
1758 if (GetIDocumentUndoRedo().DoesUndo())
1759 {
1760 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL, rBoxes, *pTableNd,
1761 0, 0, nCnt, bBehind, false ));
1762 aTmpLst.insert( rTable.GetTabSortBoxes() );
1763 }
1764
1765 bool bRet(false);
1766 {
1767 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
1768
1769 rTable.SwitchFormulasToInternalRepresentation();
1770 bRet = rTable.InsertCol(*this, rBoxes, nCnt, bBehind, bInsertDummy);
1771 if (bRet)
1772 {
1773 getIDocumentState().SetModified();
1774 ::ClearFEShellTabCols(*this, nullptr);
1775 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1776 }
1777 }
1778
1779 if( pUndo && bRet )
1780 {
1781 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
1782 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1783 }
1784 return bRet;
1785 }
1786
InsertRow(const SwCursor & rCursor,sal_uInt16 nCnt,bool bBehind)1787 void SwDoc::InsertRow( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
1788 {
1789 // Find the Boxes via the Layout
1790 SwSelBoxes aBoxes;
1791 GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
1792
1793 if( !aBoxes.empty() )
1794 InsertRow( aBoxes, nCnt, bBehind );
1795 }
1796
InsertRow(const SwSelBoxes & rBoxes,sal_uInt16 nCnt,bool bBehind,bool bInsertDummy)1797 bool SwDoc::InsertRow( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind, bool bInsertDummy )
1798 {
1799 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
1800 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
1801 if( !pTableNd )
1802 return false;
1803
1804 SwTable& rTable = pTableNd->GetTable();
1805 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
1806 return false;
1807
1808 SwTableSortBoxes aTmpLst;
1809 std::unique_ptr<SwUndoTableNdsChg> pUndo;
1810 if (GetIDocumentUndoRedo().DoesUndo())
1811 {
1812 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW,rBoxes, *pTableNd,
1813 0, 0, nCnt, bBehind, false ));
1814 aTmpLst.insert( rTable.GetTabSortBoxes() );
1815 }
1816
1817 bool bRet(false);
1818 {
1819 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
1820 rTable.SwitchFormulasToInternalRepresentation();
1821
1822 bRet = rTable.InsertRow( this, rBoxes, nCnt, bBehind, bInsertDummy );
1823 if (bRet)
1824 {
1825 getIDocumentState().SetModified();
1826 ::ClearFEShellTabCols(*this, nullptr);
1827 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1828 }
1829 }
1830
1831 if( pUndo && bRet )
1832 {
1833 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
1834 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1835 }
1836 return bRet;
1837
1838 }
1839
1840 /**
1841 * Deleting Columns/Rows
1842 */
DeleteRow(const SwCursor & rCursor)1843 void SwDoc::DeleteRow( const SwCursor& rCursor )
1844 {
1845 // Find the Boxes via the Layout
1846 SwSelBoxes aBoxes;
1847 GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
1848 if( ::HasProtectedCells( aBoxes ))
1849 return;
1850
1851 // Remove the Cursor from the to-be-deleted Section.
1852 // The Cursor is placed after the table, except for
1853 // - when there's another Line, we place it in that one
1854 // - when a Line precedes it, we place it in that one
1855 {
1856 SwTableNode* pTableNd = rCursor.GetPointNode().FindTableNode();
1857
1858 if(dynamic_cast<const SwDDETable*>( & pTableNd->GetTable()) != nullptr)
1859 return;
1860
1861 // Find all Boxes/Lines
1862 FndBox_ aFndBox( nullptr, nullptr );
1863 {
1864 FndPara aPara( aBoxes, &aFndBox );
1865 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
1866 }
1867
1868 if( aFndBox.GetLines().empty() )
1869 return;
1870
1871 if (SwEditShell* pESh = GetEditShell())
1872 {
1873 pESh->KillPams();
1874 // FIXME: actually we should be iterating over all Shells!
1875 }
1876
1877 FndBox_* pFndBox = &aFndBox;
1878 while( 1 == pFndBox->GetLines().size() &&
1879 1 == pFndBox->GetLines().front()->GetBoxes().size() )
1880 {
1881 FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
1882 if( pTmp->GetBox()->GetSttNd() )
1883 break; // Else it gets too far
1884 pFndBox = pTmp;
1885 }
1886
1887 SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
1888 SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
1889 while( !pDelBox->GetSttNd() )
1890 {
1891 SwTableLine* pLn = pDelBox->GetTabLines()[
1892 pDelBox->GetTabLines().size()-1 ];
1893 pDelBox = pLn->GetTabBoxes().back();
1894 }
1895 SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
1896 pDelBox );
1897 while( pNextBox &&
1898 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
1899 pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
1900
1901 if( !pNextBox ) // No succeeding Boxes? Then take the preceding one
1902 {
1903 pDelLine = pFndBox->GetLines().front()->GetLine();
1904 pDelBox = pDelLine->GetTabBoxes()[ 0 ];
1905 while( !pDelBox->GetSttNd() )
1906 pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
1907 pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
1908 pDelBox );
1909 while( pNextBox &&
1910 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
1911 pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
1912 }
1913
1914 SwNodeOffset nIdx;
1915 if( pNextBox ) // Place the Cursor here
1916 nIdx = pNextBox->GetSttIdx() + 1;
1917 else // Else after the Table
1918 nIdx = pTableNd->EndOfSectionIndex() + 1;
1919
1920 SwNodeIndex aIdx( GetNodes(), nIdx );
1921 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1922 if( !pCNd )
1923 pCNd = SwNodes::GoNext(&aIdx);
1924
1925 if( pCNd )
1926 {
1927 // Change the Shell's Cursor or the one passed?
1928 SwPaM* pPam = const_cast<SwPaM*>(static_cast<SwPaM const *>(&rCursor));
1929 pPam->GetPoint()->Assign(aIdx);
1930 pPam->SetMark(); // Both want a part of it
1931 pPam->DeleteMark();
1932 }
1933 }
1934
1935 // Thus delete the Rows
1936 GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE, nullptr);
1937 DeleteRowCol( aBoxes );
1938 GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE, nullptr);
1939 }
1940
DeleteCol(const SwCursor & rCursor)1941 void SwDoc::DeleteCol( const SwCursor& rCursor )
1942 {
1943 // Find the Boxes via the Layout
1944 SwSelBoxes aBoxes;
1945 GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
1946 if( ::HasProtectedCells( aBoxes ))
1947 return;
1948
1949 // The Cursors need to be removed from the to-be-deleted range.
1950 // Always place them after/on top of the Table; they are always set
1951 // to the old position via the document position.
1952 if (SwEditShell* pESh = GetEditShell())
1953 {
1954 const SwNode* pNd = rCursor.GetPointNode().FindTableBoxStartNode();
1955 pESh->ParkCursor( *pNd );
1956 }
1957
1958 // Thus delete the Columns
1959 GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
1960 DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
1961 GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
1962 }
1963
DelTable(SwTableNode * const pTableNd)1964 void SwDoc::DelTable(SwTableNode *const pTableNd)
1965 {
1966 {
1967 // tdf#156267 remove DdeBookmarks before deleting nodes
1968 SwPaM aTmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
1969 SwDataChanged aTmp(aTmpPaM);
1970 }
1971
1972 bool bNewTextNd = false;
1973 // Is it alone in a FlyFrame?
1974 SwNodeIndex aIdx( *pTableNd, -1 );
1975 const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode();
1976 if (pSttNd)
1977 {
1978 const SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex() + 1;
1979 const SwNodeOffset nSectEnd = pSttNd->EndOfSectionIndex();
1980 if (nTableEnd == nSectEnd)
1981 {
1982 if (SwFlyStartNode == pSttNd->GetStartNodeType())
1983 {
1984 SwFrameFormat* pFormat = pSttNd->GetFlyFormat();
1985 if (pFormat)
1986 {
1987 // That's the FlyFormat we're looking for
1988 getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
1989 return;
1990 }
1991 }
1992 // No Fly? Thus Header or Footer: always leave a TextNode
1993 // We can forget about Undo then!
1994 bNewTextNd = true;
1995 }
1996 }
1997
1998 // No Fly? Then it is a Header or Footer, so keep always a TextNode
1999 ++aIdx;
2000 if (GetIDocumentUndoRedo().DoesUndo())
2001 {
2002 GetIDocumentUndoRedo().ClearRedo();
2003 SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() );
2004
2005 if (bNewTextNd)
2006 {
2007 const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
2008 GetNodes().MakeTextNode( aTmpIdx.GetNode(),
2009 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
2010 }
2011
2012 // Save the cursors (UNO and otherwise)
2013 SwPaM const* pSavePaM(nullptr);
2014 SwPaM forwardPaM(*pTableNd->EndOfSectionNode());
2015 if (forwardPaM.Move(fnMoveForward, GoInNode))
2016 {
2017 pSavePaM = &forwardPaM;
2018 }
2019 SwPaM backwardPaM(*pTableNd);
2020 if (backwardPaM.Move(fnMoveBackward, GoInNode))
2021 {
2022 if (pSavePaM == nullptr
2023 // try to stay in the same outer table cell
2024 || (forwardPaM.GetPoint()->GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode()
2025 && forwardPaM.GetPoint()->GetNode().StartOfSectionIndex()
2026 < backwardPaM.GetPoint()->GetNode().StartOfSectionIndex()))
2027 {
2028 pSavePaM = &backwardPaM;
2029 }
2030 }
2031 assert(pSavePaM); // due to bNewTextNd this must succeed
2032 {
2033 SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
2034 ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint());
2035 }
2036
2037 // Move hard PageBreaks to the succeeding Node
2038 bool bSavePageBreak = false, bSavePageDesc = false;
2039 SwNodeOffset nNextNd = pTableNd->EndOfSectionIndex()+1;
2040 SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode();
2041 if (pNextNd)
2042 {
2043 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2044 const SfxPoolItem *pItem;
2045 if (SfxItemState::SET == pTableFormat->GetItemState(RES_PAGEDESC,
2046 false, &pItem))
2047 {
2048 pNextNd->SetAttr( *pItem );
2049 bSavePageDesc = true;
2050 }
2051
2052 if (SfxItemState::SET == pTableFormat->GetItemState(RES_BREAK,
2053 false, &pItem))
2054 {
2055 pNextNd->SetAttr( *pItem );
2056 bSavePageBreak = true;
2057 }
2058 }
2059 std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default));
2060 if (bNewTextNd)
2061 pUndo->SetTableDelLastNd();
2062 pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
2063 pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
2064 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2065 }
2066 else
2067 {
2068 if (bNewTextNd)
2069 {
2070 const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
2071 GetNodes().MakeTextNode( aTmpIdx.GetNode(),
2072 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
2073 }
2074
2075 // Save the cursors (UNO and otherwise)
2076 SwPaM const* pSavePaM(nullptr);
2077 SwPaM forwardPaM(*pTableNd->EndOfSectionNode());
2078 if (forwardPaM.Move(fnMoveForward, GoInNode))
2079 {
2080 pSavePaM = &forwardPaM;
2081 }
2082 SwPaM backwardPaM(*pTableNd);
2083 if (backwardPaM.Move(fnMoveBackward, GoInNode))
2084 {
2085 if (pSavePaM == nullptr
2086 // try to stay in the same outer table cell
2087 || (forwardPaM.GetPoint()->GetNode().FindTableNode() != pTableNd->StartOfSectionNode()->FindTableNode()
2088 && forwardPaM.GetPoint()->GetNode().StartOfSectionIndex()
2089 < backwardPaM.GetPoint()->GetNode().StartOfSectionIndex()))
2090 {
2091 pSavePaM = &backwardPaM;
2092 }
2093 }
2094 assert(pSavePaM); // due to bNewTextNd this must succeed
2095 {
2096 SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
2097 ::PaMCorrAbs(tmpPaM, *pSavePaM->GetPoint());
2098 }
2099
2100 // Move hard PageBreaks to the succeeding Node
2101 SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
2102 if (pNextNd)
2103 {
2104 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
2105 const SfxPoolItem *pItem;
2106 if (SfxItemState::SET == pTableFormat->GetItemState(RES_PAGEDESC,
2107 false, &pItem))
2108 {
2109 pNextNd->SetAttr( *pItem );
2110 }
2111
2112 if (SfxItemState::SET == pTableFormat->GetItemState(RES_BREAK,
2113 false, &pItem))
2114 {
2115 pNextNd->SetAttr( *pItem );
2116 }
2117 }
2118
2119 pTableNd->DelFrames();
2120 getIDocumentContentOperations().DeleteSection( pTableNd );
2121 }
2122
2123 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2124 pFEShell->UpdateTableStyleFormatting();
2125
2126 getIDocumentState().SetModified();
2127 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2128 }
2129
DeleteRowCol(const SwSelBoxes & rBoxes,RowColMode const eMode)2130 bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode)
2131 {
2132 if (!(eMode & SwDoc::RowColMode::DeleteProtected)
2133 && ::HasProtectedCells(rBoxes))
2134 {
2135 return false;
2136 }
2137
2138 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
2139 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
2140 if( !pTableNd )
2141 return false;
2142
2143 if (!(eMode & SwDoc::RowColMode::DeleteProtected)
2144 && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr)
2145 {
2146 return false;
2147 }
2148
2149 ::ClearFEShellTabCols(*this, nullptr);
2150 SwSelBoxes aSelBoxes( rBoxes );
2151 SwTable &rTable = pTableNd->GetTable();
2152 tools::Long nMin = 0;
2153 tools::Long nMax = 0;
2154 if( rTable.IsNewModel() )
2155 {
2156 if (eMode & SwDoc::RowColMode::DeleteColumn)
2157 rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
2158 else
2159 rTable.FindSuperfluousRows( aSelBoxes );
2160 }
2161
2162 // Are we deleting the whole Table?
2163 const SwNodeOffset nTmpIdx1 = pTableNd->GetIndex();
2164 const SwNodeOffset nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1;
2165 if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() &&
2166 aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 &&
2167 nTmpIdx2 == pTableNd->EndOfSectionIndex() )
2168 {
2169 DelTable(pTableNd);
2170 return true;
2171 }
2172
2173 std::unique_ptr<SwUndoTableNdsChg> pUndo;
2174 if (GetIDocumentUndoRedo().DoesUndo())
2175 {
2176 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX, aSelBoxes, *pTableNd,
2177 nMin, nMax, 0, false, false ));
2178 }
2179
2180 bool bRet(false);
2181 {
2182 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2183 rTable.SwitchFormulasToInternalRepresentation();
2184
2185 if (rTable.IsNewModel())
2186 {
2187 if (eMode & SwDoc::RowColMode::DeleteColumn)
2188 rTable.PrepareDeleteCol( nMin, nMax );
2189 rTable.FindSuperfluousRows( aSelBoxes );
2190 if (pUndo)
2191 pUndo->ReNewBoxes( aSelBoxes );
2192 }
2193 bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true );
2194 if (bRet)
2195 {
2196 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2197 pFEShell->UpdateTableStyleFormatting();
2198
2199 getIDocumentState().SetModified();
2200 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2201 }
2202 }
2203
2204 if( pUndo && bRet )
2205 {
2206 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2207 }
2208
2209 return bRet;
2210 }
2211
2212 /**
2213 * Split up/merge Boxes in the Table
2214 */
SplitTable(const SwSelBoxes & rBoxes,bool bVert,sal_uInt16 nCnt,bool bSameHeight)2215 bool SwDoc::SplitTable( const SwSelBoxes& rBoxes, bool bVert, sal_uInt16 nCnt,
2216 bool bSameHeight )
2217 {
2218 OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box list" );
2219 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
2220 if( !pTableNd )
2221 return false;
2222
2223 SwTable& rTable = pTableNd->GetTable();
2224 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
2225 return false;
2226
2227 std::vector<SwNodeOffset> aNdsCnts;
2228 SwTableSortBoxes aTmpLst;
2229 std::unique_ptr<SwUndoTableNdsChg> pUndo;
2230 if (GetIDocumentUndoRedo().DoesUndo())
2231 {
2232 pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT, rBoxes, *pTableNd, 0, 0,
2233 nCnt, bVert, bSameHeight ));
2234
2235 aTmpLst.insert( rTable.GetTabSortBoxes() );
2236 if( !bVert )
2237 {
2238 for (size_t n = 0; n < rBoxes.size(); ++n)
2239 {
2240 const SwStartNode* pSttNd = rBoxes[ n ]->GetSttNd();
2241 aNdsCnts.push_back( pSttNd->EndOfSectionIndex() -
2242 pSttNd->GetIndex() );
2243 }
2244 }
2245 }
2246
2247 bool bRet(false);
2248 {
2249 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
2250 rTable.SwitchFormulasToInternalRepresentation();
2251
2252 if (bVert)
2253 bRet = rTable.SplitCol(*this, rBoxes, nCnt);
2254 else
2255 bRet = rTable.SplitRow(*this, rBoxes, nCnt, bSameHeight);
2256
2257 if (bRet)
2258 {
2259 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
2260 pFEShell->UpdateTableStyleFormatting();
2261
2262 getIDocumentState().SetModified();
2263 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2264 }
2265 }
2266
2267 if( pUndo && bRet )
2268 {
2269 if( bVert )
2270 pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
2271 else
2272 pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
2273 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2274 }
2275
2276 return bRet;
2277 }
2278
MergeTable(SwPaM & rPam)2279 TableMergeErr SwDoc::MergeTable( SwPaM& rPam )
2280 {
2281 // Check if the current cursor's Point/Mark are inside a Table
2282 SwTableNode* pTableNd = rPam.GetPointNode().FindTableNode();
2283 if( !pTableNd )
2284 return TableMergeErr::NoSelection;
2285 SwTable& rTable = pTableNd->GetTable();
2286 if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr )
2287 return TableMergeErr::NoSelection;
2288 TableMergeErr nRet = TableMergeErr::NoSelection;
2289 if( !rTable.IsNewModel() )
2290 {
2291 nRet =::CheckMergeSel( rPam );
2292 if( TableMergeErr::Ok != nRet )
2293 return nRet;
2294 nRet = TableMergeErr::NoSelection;
2295 }
2296
2297 // #i33394#
2298 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE, nullptr );
2299
2300 RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
2301 getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
2302
2303 std::unique_ptr<SwUndoTableMerge> pUndo;
2304 if (GetIDocumentUndoRedo().DoesUndo())
2305 pUndo.reset(new SwUndoTableMerge( rPam ));
2306
2307 // Find the Boxes via the Layout
2308 SwSelBoxes aBoxes;
2309 SwSelBoxes aMerged;
2310 SwTableBox* pMergeBox;
2311
2312 if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo.get() ) )
2313 { // No cells found to merge
2314 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
2315 if( pUndo )
2316 {
2317 pUndo.reset();
2318 SwUndoId nLastUndoId(SwUndoId::EMPTY);
2319 if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
2320 && (SwUndoId::REDLINE == nLastUndoId))
2321 {
2322 // FIXME: why is this horrible cleanup necessary?
2323 SwUndoRedline *const pU = dynamic_cast<SwUndoRedline*>(
2324 GetUndoManager().RemoveLastUndo());
2325 if (pU && pU->GetRedlSaveCount())
2326 {
2327 SwEditShell *const pEditShell(GetEditShell());
2328 assert(pEditShell);
2329 ::sw::UndoRedoContext context(*this, *pEditShell);
2330 static_cast<SfxUndoAction *>(pU)->UndoWithContext(context);
2331 }
2332 delete pU;
2333 }
2334 }
2335 }
2336 else
2337 {
2338 // The PaMs need to be removed from the to-be-deleted range. Thus always place
2339 // them at the end of/on top of the Table; it's always set to the old position via
2340 // the Document Position.
2341 // For a start remember an index for the temporary position, because we cannot
2342 // access it after GetMergeSel
2343 {
2344 rPam.DeleteMark();
2345 rPam.GetPoint()->Assign(*pMergeBox->GetSttNd());
2346 rPam.SetMark();
2347 rPam.DeleteMark();
2348
2349 SwPaM* pTmp = &rPam;
2350 while( &rPam != ( pTmp = pTmp->GetNext() ))
2351 for( int i = 0; i < 2; ++i )
2352 pTmp->GetBound( static_cast<bool>(i) ) = *rPam.GetPoint();
2353
2354 if (SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(&rPam))
2355 {
2356 // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
2357 // to not contain the soon to-be-deleted SwTableBox so if the rPam
2358 // is queried via a11y it doesn't claim the deleted cell still
2359 // exists
2360 pTableCursor->NewTableSelection();
2361 }
2362 }
2363
2364 // Merge them
2365 pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
2366
2367 if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo.get() ))
2368 {
2369 nRet = TableMergeErr::Ok;
2370
2371 getIDocumentState().SetModified();
2372 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2373 if( pUndo )
2374 {
2375 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
2376 }
2377 }
2378
2379 rPam.GetPoint()->Assign( *pMergeBox->GetSttNd() );
2380 rPam.Move();
2381
2382 ::ClearFEShellTabCols(*this, nullptr);
2383 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
2384 }
2385 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE, nullptr );
2386 return nRet;
2387 }
2388
SwTableNode(const SwNode & rWhere)2389 SwTableNode::SwTableNode( const SwNode& rWhere )
2390 : SwStartNode( rWhere, SwNodeType::Table )
2391 {
2392 m_pTable.reset(new SwTable);
2393 }
2394
~SwTableNode()2395 SwTableNode::~SwTableNode()
2396 {
2397 // Notify UNO wrappers
2398 GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
2399 DelFrames();
2400 m_pTable->SetTableNode(this); // set this so that ~SwDDETable can read it!
2401 m_pTable.reset();
2402 }
2403
MakeFrame(SwFrame * pSib)2404 SwTabFrame *SwTableNode::MakeFrame( SwFrame* pSib )
2405 {
2406 return new SwTabFrame( *m_pTable, pSib );
2407 }
2408
2409 /**
2410 * Creates all Views from the Document for the preceding Node. The resulting ContentFrames
2411 * are added to the corresponding Layout.
2412 */
MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)2413 void SwTableNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
2414 {
2415 if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
2416 return;
2417
2418 SwFrame *pFrame;
2419 SwContentNode * pNode = rIdx.GetNode().GetContentNode();
2420
2421 assert(pNode && "No ContentNode or CopyNode and new Node is identical");
2422
2423 bool bBefore = rIdx < GetIndex();
2424
2425 SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
2426
2427 while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
2428 {
2429 if ( ( pFrame->getRootFrame()->HasMergedParas() &&
2430 !pNode->IsCreateFrameWhenHidingRedlines() ) ||
2431 // tdf#153819 table deletion with change tracking:
2432 // table node without frames in Hide Changes mode
2433 !pFrame->GetUpper() )
2434 {
2435 continue;
2436 }
2437 SwFrame *pNew = pNode->MakeFrame( pFrame );
2438 // Will the Node receive Frames before or after?
2439 if ( bBefore )
2440 // The new one precedes me
2441 pNew->Paste( pFrame->GetUpper(), pFrame );
2442 else
2443 // The new one succeeds me
2444 pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
2445 }
2446 }
2447
2448 /**
2449 * Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
2450 */
MakeOwnFrames(SwPosition * pIdxBehind)2451 void SwTableNode::MakeOwnFrames(SwPosition* pIdxBehind)
2452 {
2453 SwNode *pNd = GetNodes().FindPrvNxtFrameNode( *this, EndOfSectionNode() );
2454 if( !pNd )
2455 {
2456 if (pIdxBehind)
2457 pIdxBehind->Assign(*this);
2458 return;
2459 }
2460 if (pIdxBehind)
2461 pIdxBehind->Assign(*pNd);
2462
2463 SwFrame *pFrame( nullptr );
2464 SwLayoutFrame *pUpper( nullptr );
2465 SwNode2Layout aNode2Layout( *pNd, GetIndex() );
2466 while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, *this )) )
2467 {
2468 if (pUpper->getRootFrame()->HasMergedParas()
2469 && !IsCreateFrameWhenHidingRedlines())
2470 {
2471 continue;
2472 }
2473 SwTabFrame* pNew = MakeFrame( pUpper );
2474 pNew->Paste( pUpper, pFrame );
2475 // #i27138#
2476 // notify accessibility paragraphs objects about changed
2477 // CONTENT_FLOWS_FROM/_TO relation.
2478 // Relation CONTENT_FLOWS_FROM for next paragraph will change
2479 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
2480 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2481 {
2482 SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
2483 if ( pViewShell && pViewShell->GetLayout() &&
2484 pViewShell->GetLayout()->IsAnyShellAccessible() )
2485 {
2486 auto pNext = pNew->FindNextCnt( true );
2487 auto pPrev = pNew->FindPrevCnt();
2488 pViewShell->InvalidateAccessibleParaFlowRelation(
2489 pNext ? pNext->DynCastTextFrame() : nullptr,
2490 pPrev ? pPrev->DynCastTextFrame() : nullptr );
2491 }
2492 }
2493 #endif
2494 pNew->RegistFlys();
2495 }
2496 }
2497
DelFrames(SwRootFrame const * const pLayout)2498 void SwTableNode::DelFrames(SwRootFrame const*const pLayout)
2499 {
2500 /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
2501 The TabFrames are attached to the FrameFormat of the SwTable.
2502 We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
2503
2504 SwIterator<SwTabFrame,SwFormat> aIter( *(m_pTable->GetFrameFormat()) );
2505 SwTabFrame *pFrame = aIter.First();
2506 while ( pFrame )
2507 {
2508 bool bAgain = false;
2509 {
2510 if (!pFrame->IsFollow() && (!pLayout || pLayout == pFrame->getRootFrame()))
2511 {
2512 while ( pFrame->HasFollow() )
2513 pFrame->JoinAndDelFollows();
2514 // #i27138#
2515 // notify accessibility paragraphs objects about changed
2516 // CONTENT_FLOWS_FROM/_TO relation.
2517 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
2518 // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
2519 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2520 if (!GetDoc().IsInDtor())
2521 {
2522 SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
2523 if ( pViewShell && pViewShell->GetLayout() &&
2524 pViewShell->GetLayout()->IsAnyShellAccessible() )
2525 {
2526 auto pNext = pFrame->FindNextCnt( true );
2527 auto pPrev = pFrame->FindPrevCnt();
2528 pViewShell->InvalidateAccessibleParaFlowRelation(
2529 pNext ? pNext->DynCastTextFrame() : nullptr,
2530 pPrev ? pPrev->DynCastTextFrame() : nullptr );
2531 }
2532 }
2533 #endif
2534 if (pFrame->GetUpper())
2535 pFrame->Cut();
2536 SwFrame::DestroyFrame(pFrame);
2537 bAgain = true;
2538 }
2539 }
2540 pFrame = bAgain ? aIter.First() : aIter.Next();
2541 }
2542 }
2543
SetNewTable(std::unique_ptr<SwTable> pNewTable,bool bNewFrames)2544 void SwTableNode::SetNewTable( std::unique_ptr<SwTable> pNewTable, bool bNewFrames )
2545 {
2546 DelFrames();
2547 m_pTable->SetTableNode(this);
2548 m_pTable = std::move(pNewTable);
2549 if( bNewFrames )
2550 {
2551 MakeOwnFrames();
2552 }
2553 }
2554
RemoveRedlines()2555 void SwTableNode::RemoveRedlines()
2556 {
2557 SwDoc& rDoc = GetDoc();
2558 SwTable& rTable = GetTable();
2559 rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc, rTable, true, RedlineType::Any);
2560 }
2561
dumpAsXml(xmlTextWriterPtr pWriter) const2562 void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter) const
2563 {
2564 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableNode"));
2565 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
2566 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
2567
2568 if (m_pTable)
2569 {
2570 m_pTable->dumpAsXml(pWriter);
2571 }
2572
2573 // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
2574 }
2575
GetTabCols(SwTabCols & rFill,const SwCellFrame * pBoxFrame)2576 void SwDoc::GetTabCols( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
2577 {
2578 OSL_ENSURE( pBoxFrame, "pBoxFrame needs to be specified!" );
2579 if( !pBoxFrame )
2580 return;
2581
2582 SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2583 const SwTableBox* pBox = pBoxFrame->GetTabBox();
2584
2585 // Set fixed points, LeftMin in Document coordinates, all others relative
2586 SwRectFnSet aRectFnSet(pTab);
2587 const SwPageFrame* pPage = pTab->FindPageFrame();
2588 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
2589 aRectFnSet.GetLeft(pPage->getFrameArea());
2590 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
2591 aRectFnSet.GetLeft(pPage->getFrameArea());
2592
2593 rFill.SetLeftMin ( nLeftMin );
2594 rFill.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
2595 rFill.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
2596 rFill.SetRightMax( nRightMax - nLeftMin );
2597
2598 pTab->GetTable()->GetTabCols( rFill, pBox );
2599 }
2600
2601 // Here are some little helpers used in SwDoc::GetTabRows
2602
2603 #define ROWFUZZY 25
2604
2605 namespace {
2606
2607 struct FuzzyCompare
2608 {
2609 bool operator() ( tools::Long s1, tools::Long s2 ) const;
2610 };
2611
2612 }
2613
operator ()(tools::Long s1,tools::Long s2) const2614 bool FuzzyCompare::operator() ( tools::Long s1, tools::Long s2 ) const
2615 {
2616 return ( s1 < s2 && std::abs( s1 - s2 ) > ROWFUZZY );
2617 }
2618
lcl_IsFrameInColumn(const SwCellFrame & rFrame,SwSelBoxes const & rBoxes)2619 static bool lcl_IsFrameInColumn( const SwCellFrame& rFrame, SwSelBoxes const & rBoxes )
2620 {
2621 for (size_t i = 0; i < rBoxes.size(); ++i)
2622 {
2623 if ( rFrame.GetTabBox() == rBoxes[ i ] )
2624 return true;
2625 }
2626
2627 return false;
2628 }
2629
GetTabRows(SwTabCols & rFill,const SwCellFrame * pBoxFrame)2630 void SwDoc::GetTabRows( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
2631 {
2632 OSL_ENSURE( pBoxFrame, "GetTabRows called without pBoxFrame" );
2633
2634 // Make code robust:
2635 if ( !pBoxFrame )
2636 return;
2637
2638 // #i39552# Collection of the boxes of the current
2639 // column has to be done at the beginning of this function, because
2640 // the table may be formatted in ::GetTableSel.
2641 SwDeletionChecker aDelCheck( pBoxFrame );
2642
2643 SwSelBoxes aBoxes;
2644 const SwContentFrame* pContent = ::GetCellContent( *pBoxFrame );
2645 if ( pContent && pContent->IsTextFrame() )
2646 {
2647 const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
2648 const SwCursor aTmpCursor( aPos, nullptr );
2649 ::GetTableSel( aTmpCursor, aBoxes, SwTableSearchType::Col );
2650 }
2651
2652 // Make code robust:
2653 if ( aDelCheck.HasBeenDeleted() )
2654 {
2655 OSL_FAIL( "Current box has been deleted during GetTabRows()" );
2656 return;
2657 }
2658
2659 // Make code robust:
2660 const SwTabFrame* pTab = pBoxFrame->FindTabFrame();
2661 OSL_ENSURE( pTab, "GetTabRows called without a table" );
2662 if ( !pTab )
2663 return;
2664
2665 const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
2666
2667 // Set fixed points, LeftMin in Document coordinates, all others relative
2668 SwRectFnSet aRectFnSet(pTab);
2669 const SwPageFrame* pPage = pTab->FindPageFrame();
2670 const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
2671 pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
2672 pTab->GetPrtTop() - pPage->getFrameArea().Top() );
2673 const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
2674 const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
2675 const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
2676
2677 rFill.SetLeftMin( nLeftMin );
2678 rFill.SetLeft( nLeft );
2679 rFill.SetRight( nRight );
2680 rFill.SetRightMax( nRightMax );
2681
2682 typedef std::map< tools::Long, std::pair< tools::Long, long >, FuzzyCompare > BoundaryMap;
2683 BoundaryMap aBoundaries;
2684 BoundaryMap::iterator aIter;
2685 std::pair< tools::Long, long > aPair;
2686
2687 typedef std::map< tools::Long, bool > HiddenMap;
2688 HiddenMap aHidden;
2689 HiddenMap::iterator aHiddenIter;
2690
2691 while ( pFrame && pTab->IsAnLower( pFrame ) )
2692 {
2693 if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
2694 {
2695 // upper and lower borders of current cell frame:
2696 tools::Long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
2697 tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
2698
2699 // get boundaries for nUpperBorder:
2700 aIter = aBoundaries.find( nUpperBorder );
2701 if ( aIter == aBoundaries.end() )
2702 {
2703 aPair.first = nUpperBorder; aPair.second = LONG_MAX;
2704 aBoundaries[ nUpperBorder ] = aPair;
2705 }
2706
2707 // get boundaries for nLowerBorder:
2708 aIter = aBoundaries.find( nLowerBorder );
2709 if ( aIter == aBoundaries.end() )
2710 {
2711 aPair.first = nUpperBorder; aPair.second = LONG_MAX;
2712 }
2713 else
2714 {
2715 nLowerBorder = (*aIter).first;
2716 tools::Long nNewLowerBorderUpperBoundary = std::max( (*aIter).second.first, nUpperBorder );
2717 aPair.first = nNewLowerBorderUpperBoundary; aPair.second = LONG_MAX;
2718 }
2719 aBoundaries[ nLowerBorder ] = aPair;
2720
2721 // calculate hidden flags for entry nUpperBorder/nLowerBorder:
2722 tools::Long nTmpVal = nUpperBorder;
2723 for ( sal_uInt8 i = 0; i < 2; ++i )
2724 {
2725 aHiddenIter = aHidden.find( nTmpVal );
2726 if ( aHiddenIter == aHidden.end() )
2727 aHidden[ nTmpVal ] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes );
2728 else
2729 {
2730 if ( aHidden[ nTmpVal ] &&
2731 lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes ) )
2732 aHidden[ nTmpVal ] = false;
2733 }
2734 nTmpVal = nLowerBorder;
2735 }
2736 }
2737
2738 pFrame = pFrame->GetNextLayoutLeaf();
2739 }
2740
2741 // transfer calculated values from BoundaryMap and HiddenMap into rFill:
2742 size_t nIdx = 0;
2743 for ( const auto& rEntry : aBoundaries )
2744 {
2745 const tools::Long nTabTop = aRectFnSet.GetPrtTop(*pTab);
2746 const tools::Long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop );
2747 const std::pair< tools::Long, long > aTmpPair = rEntry.second;
2748 const tools::Long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
2749 const tools::Long nSecond = aTmpPair.second;
2750
2751 aHiddenIter = aHidden.find( rEntry.first );
2752 const bool bHidden = aHiddenIter != aHidden.end() && (*aHiddenIter).second;
2753 rFill.Insert( nKey, nFirst, nSecond, bHidden, nIdx++ );
2754 }
2755
2756 // delete first and last entry
2757 OSL_ENSURE( rFill.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
2758 // #i60818# There may be only one entry in rFill. Make
2759 // code robust by checking count of rFill.
2760 if ( rFill.Count() ) rFill.Remove( 0 );
2761 if ( rFill.Count() ) rFill.Remove( rFill.Count() - 1 );
2762 rFill.SetLastRowAllowedToChange( !pTab->HasFollowFlowLine() );
2763 }
2764
SetTabCols(const SwTabCols & rNew,bool bCurRowOnly,const SwCellFrame * pBoxFrame)2765 void SwDoc::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly,
2766 const SwCellFrame* pBoxFrame )
2767 {
2768 const SwTableBox* pBox = nullptr;
2769 SwTabFrame *pTab = nullptr;
2770
2771 if( pBoxFrame )
2772 {
2773 pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2774 pBox = pBoxFrame->GetTabBox();
2775 }
2776 else
2777 {
2778 OSL_ENSURE( false, "must specify pBoxFrame" );
2779 return ;
2780 }
2781
2782 // If the Table is still using relative values (USHRT_MAX)
2783 // we need to switch to absolute ones.
2784 SwTable& rTab = *pTab->GetTable();
2785 const SwFormatFrameSize& rTableFrameSz = rTab.GetFrameFormat()->GetFrameSize();
2786 SwRectFnSet aRectFnSet(pTab);
2787 // #i17174# - With fix for #i9040# the shadow size is taken
2788 // from the table width. Thus, add its left and right size to current table
2789 // printing area width in order to get the correct table size attribute.
2790 SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
2791 {
2792 SvxShadowItem aShadow( rTab.GetFrameFormat()->GetShadow() );
2793 nPrtWidth += aShadow.CalcShadowSpace( SvxShadowItemSide::LEFT ) +
2794 aShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
2795 }
2796 if( nPrtWidth != rTableFrameSz.GetWidth() )
2797 {
2798 SwFormatFrameSize aSz( rTableFrameSz );
2799 aSz.SetWidth( nPrtWidth );
2800 rTab.GetFrameFormat()->SetFormatAttr( aSz );
2801 }
2802
2803 SwTabCols aOld( rNew.Count() );
2804
2805 const SwPageFrame* pPage = pTab->FindPageFrame();
2806 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
2807 aRectFnSet.GetLeft(pPage->getFrameArea());
2808 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
2809 aRectFnSet.GetLeft(pPage->getFrameArea());
2810
2811 // Set fixed points, LeftMin in Document coordinates, all others relative
2812 aOld.SetLeftMin ( nLeftMin );
2813 aOld.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
2814 aOld.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
2815 aOld.SetRightMax( nRightMax - nLeftMin );
2816
2817 rTab.GetTabCols( aOld, pBox );
2818 SetTabCols(rTab, rNew, aOld, pBox, bCurRowOnly );
2819 }
2820
SetTabRows(const SwTabCols & rNew,bool bCurColOnly,const SwCellFrame * pBoxFrame)2821 void SwDoc::SetTabRows( const SwTabCols &rNew, bool bCurColOnly,
2822 const SwCellFrame* pBoxFrame )
2823 {
2824 SwTabFrame *pTab = nullptr;
2825
2826 if( pBoxFrame )
2827 {
2828 pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
2829 }
2830 else
2831 {
2832 OSL_ENSURE( false, "must specify pBoxFrame" );
2833 return ;
2834 }
2835
2836 // If the Table is still using relative values (USHRT_MAX)
2837 // we need to switch to absolute ones.
2838 SwRectFnSet aRectFnSet(pTab);
2839 SwTabCols aOld( rNew.Count() );
2840
2841 // Set fixed points, LeftMin in Document coordinates, all others relative
2842 const SwPageFrame* pPage = pTab->FindPageFrame();
2843
2844 aOld.SetRight( aRectFnSet.GetHeight(pTab->getFramePrintArea()) );
2845 tools::Long nLeftMin;
2846 if ( aRectFnSet.IsVert() )
2847 {
2848 nLeftMin = pTab->GetPrtLeft() - pPage->getFrameArea().Left();
2849 aOld.SetLeft ( LONG_MAX );
2850 aOld.SetRightMax( aOld.GetRight() );
2851
2852 }
2853 else
2854 {
2855 nLeftMin = pTab->GetPrtTop() - pPage->getFrameArea().Top();
2856 aOld.SetLeft ( 0 );
2857 aOld.SetRightMax( LONG_MAX );
2858 }
2859 aOld.SetLeftMin ( nLeftMin );
2860
2861 GetTabRows( aOld, pBoxFrame );
2862
2863 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR, nullptr );
2864
2865 // check for differences between aOld and rNew:
2866 const size_t nCount = rNew.Count();
2867 const SwTable* pTable = pTab->GetTable();
2868 OSL_ENSURE( pTable, "My colleague told me, this couldn't happen" );
2869
2870 for ( size_t i = 0; i <= nCount; ++i )
2871 {
2872 const size_t nIdxStt = aRectFnSet.IsVert() ? nCount - i : i - 1;
2873 const size_t nIdxEnd = aRectFnSet.IsVert() ? nCount - i - 1 : i;
2874
2875 const tools::Long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
2876 const tools::Long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
2877 const tools::Long nOldRowHeight = nOldRowEnd - nOldRowStart;
2878
2879 const tools::Long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
2880 const tools::Long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
2881 const tools::Long nNewRowHeight = nNewRowEnd - nNewRowStart;
2882
2883 const tools::Long nDiff = nNewRowHeight - nOldRowHeight;
2884 if ( std::abs( nDiff ) >= ROWFUZZY )
2885 {
2886 // For the old table model pTextFrame and pLine will be set for every box.
2887 // For the new table model pTextFrame will be set if the box is not covered,
2888 // but the pLine will be set if the box is not an overlapping box
2889 // In the new table model the row height can be adjusted,
2890 // when both variables are set.
2891 const SwTextFrame* pTextFrame = nullptr;
2892 const SwTableLine* pLine = nullptr;
2893
2894 // Iterate over all SwCellFrames with Bottom = nOldPos
2895 const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
2896 while ( pFrame && pTab->IsAnLower( pFrame ) )
2897 {
2898 if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
2899 {
2900 const tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
2901 const sal_uLong nTabTop = aRectFnSet.GetPrtTop(*pTab);
2902 if ( std::abs( aRectFnSet.YInc( nTabTop, nOldRowEnd ) - nLowerBorder ) <= ROWFUZZY )
2903 {
2904 if ( !bCurColOnly || pFrame == pBoxFrame )
2905 {
2906 const SwFrame* pContent = ::GetCellContent( static_cast<const SwCellFrame&>(*pFrame) );
2907
2908 if ( pContent && pContent->IsTextFrame() )
2909 {
2910 const SwTableBox* pBox = static_cast<const SwCellFrame*>(pFrame)->GetTabBox();
2911 const sal_Int32 nRowSpan = pBox->getRowSpan();
2912 if( nRowSpan > 0 ) // Not overlapped
2913 pTextFrame = static_cast<const SwTextFrame*>(pContent);
2914 if( nRowSpan < 2 ) // Not overlapping for row height
2915 pLine = pBox->GetUpper();
2916 if( pLine && pTextFrame ) // always for old table model
2917 {
2918 // The new row height must not to be calculated from an overlapping box
2919 SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
2920 const tools::Long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
2921 if( nNewSize != aNew.GetHeight() )
2922 {
2923 aNew.SetHeight( nNewSize );
2924 if ( SwFrameSize::Variable == aNew.GetHeightSizeType() )
2925 aNew.SetHeightSizeType( SwFrameSize::Minimum );
2926 // This position must not be in an overlapped box
2927 const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
2928 const SwCursor aTmpCursor( aPos, nullptr );
2929 SetRowHeight( aTmpCursor, aNew );
2930 // For the new table model we're done, for the old one
2931 // there might be another (sub)row to adjust...
2932 if( pTable->IsNewModel() )
2933 break;
2934 }
2935 pLine = nullptr;
2936 }
2937 }
2938 }
2939 }
2940 }
2941 pFrame = pFrame->GetNextLayoutLeaf();
2942 }
2943 }
2944 }
2945
2946 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR, nullptr );
2947
2948 ::ClearFEShellTabCols(*this, nullptr);
2949 }
2950
2951 /**
2952 * Direct access for UNO
2953 */
SetTabCols(SwTable & rTab,const SwTabCols & rNew,const SwTabCols & rOld,const SwTableBox * pStart,bool bCurRowOnly)2954 void SwDoc::SetTabCols(SwTable& rTab, const SwTabCols &rNew, const SwTabCols &rOld,
2955 const SwTableBox *pStart, bool bCurRowOnly )
2956 {
2957 if (GetIDocumentUndoRedo().DoesUndo())
2958 {
2959 GetIDocumentUndoRedo().AppendUndo(
2960 std::make_unique<SwUndoAttrTable>( *rTab.GetTableNode(), true ));
2961 }
2962 rTab.SetTabCols( rNew, rOld, pStart, bCurRowOnly );
2963 ::ClearFEShellTabCols(*this, nullptr);
2964 getIDocumentState().SetModified();
2965 }
2966
SetRowsToRepeat(SwTable & rTable,sal_uInt16 nSet)2967 void SwDoc::SetRowsToRepeat( SwTable &rTable, sal_uInt16 nSet )
2968 {
2969 if( nSet == rTable.GetRowsToRepeat() )
2970 return;
2971
2972 if (GetIDocumentUndoRedo().DoesUndo())
2973 {
2974 GetIDocumentUndoRedo().AppendUndo(
2975 std::make_unique<SwUndoTableHeadline>(rTable, rTable.GetRowsToRepeat(), nSet) );
2976 }
2977
2978 rTable.SetRowsToRepeat(nSet);
2979 rTable.GetFrameFormat()->CallSwClientNotify(sw::TableHeadingChange());
2980 getIDocumentState().SetModified();
2981 }
2982
AddToUndoHistory(const SwContentNode & rNd)2983 void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
2984 {
2985 if( m_pHistory )
2986 m_pHistory->AddColl(rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text);
2987 }
2988
AddBox(const SwTableBox & rBox)2989 void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
2990 {
2991 m_aPositionArr.push_back(m_nWidth);
2992 SwTableBox* p = const_cast<SwTableBox*>(&rBox);
2993 m_Boxes.push_back(p);
2994 m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
2995 }
2996
GetBoxOfPos(const SwTableBox & rBox)2997 const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
2998 {
2999 const SwTableBox* pRet = nullptr;
3000
3001 if( !m_aPositionArr.empty() )
3002 {
3003 std::vector<sal_uInt16>::size_type n;
3004 for( n = 0; n < m_aPositionArr.size(); ++n )
3005 if( m_aPositionArr[ n ] == m_nWidth )
3006 break;
3007 else if( m_aPositionArr[ n ] > m_nWidth )
3008 {
3009 if( n )
3010 --n;
3011 break;
3012 }
3013
3014 if( n >= m_aPositionArr.size() )
3015 --n;
3016
3017 m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
3018 pRet = m_Boxes[ n ];
3019 }
3020 return pRet;
3021 }
3022
Resize(sal_uInt16 nOffset,sal_uInt16 nOldWidth)3023 bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
3024 {
3025 if( !m_aPositionArr.empty() )
3026 {
3027 std::vector<sal_uInt16>::size_type n;
3028 for( n = 0; n < m_aPositionArr.size(); ++n )
3029 {
3030 if( m_aPositionArr[ n ] == nOffset )
3031 break;
3032 else if( m_aPositionArr[ n ] > nOffset )
3033 {
3034 if( n )
3035 --n;
3036 break;
3037 }
3038 }
3039
3040 m_aPositionArr.erase( m_aPositionArr.begin(), m_aPositionArr.begin() + n );
3041 m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
3042
3043 size_t nArrSize = m_aPositionArr.size();
3044 if (nArrSize)
3045 {
3046 if (nOldWidth == 0)
3047 throw o3tl::divide_by_zero();
3048
3049 // Adapt the positions to the new Size
3050 for( n = 0; n < nArrSize; ++n )
3051 {
3052 sal_uLong nSize = m_nWidth;
3053 nSize *= ( m_aPositionArr[ n ] - nOffset );
3054 nSize /= nOldWidth;
3055 m_aPositionArr[ n ] = sal_uInt16( nSize );
3056 }
3057 }
3058 }
3059 return !m_aPositionArr.empty();
3060 }
3061
sw_Line_CollectBox(const SwTableLine * & rpLine,void * pPara)3062 bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
3063 {
3064 SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
3065 if( pSplPara->IsGetValues() )
3066 for( const auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
3067 sw_Box_CollectBox(rpBox, pSplPara );
3068 else
3069 for( auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
3070 sw_BoxSetSplitBoxFormats(rpBox, pSplPara );
3071 return true;
3072 }
3073
sw_Box_CollectBox(const SwTableBox * pBox,SwCollectTableLineBoxes * pSplPara)3074 void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
3075 {
3076 auto nLen = pBox->GetTabLines().size();
3077 if( nLen )
3078 {
3079 // Continue with the actual Line
3080 if( pSplPara->IsGetFromTop() )
3081 nLen = 0;
3082 else
3083 --nLen;
3084
3085 const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
3086 sw_Line_CollectBox( pLn, pSplPara );
3087 }
3088 else
3089 pSplPara->AddBox( *pBox );
3090 }
3091
sw_BoxSetSplitBoxFormats(SwTableBox * pBox,SwCollectTableLineBoxes * pSplPara)3092 void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
3093 {
3094 auto nLen = pBox->GetTabLines().size();
3095 if( nLen )
3096 {
3097 // Continue with the actual Line
3098 if( pSplPara->IsGetFromTop() )
3099 nLen = 0;
3100 else
3101 --nLen;
3102
3103 const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
3104 sw_Line_CollectBox( pLn, pSplPara );
3105 }
3106 else
3107 {
3108 const SwTableBox* pSrcBox = pSplPara->GetBoxOfPos( *pBox );
3109 SwFrameFormat* pFormat = pSrcBox->GetFrameFormat();
3110
3111 if( SplitTable_HeadlineOption::BorderCopy == pSplPara->GetMode() )
3112 {
3113 const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
3114 if( !rBoxItem.GetTop() )
3115 {
3116 SvxBoxItem aNew( rBoxItem );
3117 aNew.SetLine( pFormat->GetBox().GetBottom(), SvxBoxItemLine::TOP );
3118 if( aNew != rBoxItem )
3119 pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
3120 }
3121 }
3122 else
3123 {
3124 SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE,
3125 RES_PROTECT, RES_PROTECT,
3126 RES_VERT_ORIENT, RES_VERT_ORIENT,
3127 RES_BACKGROUND, RES_SHADOW>
3128 aTmpSet( pFormat->GetDoc()->GetAttrPool() );
3129 aTmpSet.Put( pFormat->GetAttrSet() );
3130 if( aTmpSet.Count() )
3131 pBox->ClaimFrameFormat()->SetFormatAttr( aTmpSet );
3132
3133 if( SplitTable_HeadlineOption::BoxAttrAllCopy == pSplPara->GetMode() )
3134 {
3135 SwNodeIndex aIdx( *pSrcBox->GetSttNd(), 1 );
3136 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
3137 if( !pCNd )
3138 pCNd = SwNodes::GoNext(&aIdx);
3139 aIdx = *pBox->GetSttNd();
3140 SwContentNode* pDNd = SwNodes::GoNext(&aIdx);
3141
3142 // If the Node is alone in the Section
3143 if( SwNodeOffset(2) == pDNd->EndOfSectionIndex() -
3144 pDNd->StartOfSectionIndex() )
3145 {
3146 pSplPara->AddToUndoHistory( *pDNd );
3147 pDNd->ChgFormatColl( pCNd->GetFormatColl() );
3148 }
3149 }
3150
3151 // note conditional template
3152 pBox->GetSttNd()->CheckSectionCondColl();
3153 }
3154 }
3155 }
3156
3157 /**
3158 * Splits a Table in the top-level Line which contains the Index.
3159 * All succeeding top-level Lines go into a new Table/Node.
3160 *
3161 * @param bCalcNewSize true
3162 * Calculate the new Size for both from the
3163 * Boxes' Max; but only if Size is using absolute
3164 * values (USHRT_MAX)
3165 */
SplitTable(const SwPosition & rPos,SplitTable_HeadlineOption eHdlnMode,bool bCalcNewSize)3166 void SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
3167 bool bCalcNewSize )
3168 {
3169 SwNode* pNd = &rPos.GetNode();
3170 SwTableNode* pTNd = pNd->FindTableNode();
3171 if( !pTNd || pNd->IsTableNode() )
3172 return;
3173
3174 if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
3175 return;
3176
3177 SwTable& rTable = pTNd->GetTable();
3178 rTable.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
3179
3180 SwHistory aHistory;
3181 {
3182 SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
3183 // Find top-level Line
3184 SwTableBox* pBox = rTable.GetTableBox(nSttIdx);
3185 sal_uInt16 nSplitLine = 0;
3186 if(pBox)
3187 {
3188 SwTableLine* pLine = pBox->GetUpper();
3189 while(pLine->GetUpper())
3190 pLine = pLine->GetUpper()->GetUpper();
3191
3192 // pLine contains the top-level Line now
3193 nSplitLine = rTable.GetTabLines().GetPos(pLine);
3194 }
3195 rTable.Split(GetUniqueTableName(), nSplitLine, GetIDocumentUndoRedo().DoesUndo() ? &aHistory : nullptr);
3196 }
3197
3198 // Find Lines for the Layout update
3199 FndBox_ aFndBox( nullptr, nullptr );
3200 aFndBox.SetTableLines( rTable );
3201 aFndBox.DelFrames( rTable );
3202
3203 SwTableNode* pNew = GetNodes().SplitTable( rPos.GetNode(), false, bCalcNewSize );
3204
3205 if( pNew )
3206 {
3207 std::unique_ptr<SwSaveRowSpan> pSaveRowSp = pNew->GetTable().CleanUpTopRowSpan( rTable.GetTabLines().size() );
3208 SwUndoSplitTable* pUndo = nullptr;
3209 if (GetIDocumentUndoRedo().DoesUndo())
3210 {
3211 pUndo = new SwUndoSplitTable(
3212 *pNew, std::move(pSaveRowSp), eHdlnMode, bCalcNewSize);
3213 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3214 if( aHistory.Count() )
3215 pUndo->SaveFormula( aHistory );
3216 }
3217
3218 switch( eHdlnMode )
3219 {
3220 // Set the lower Border of the preceding Line to
3221 // the upper Border of the current one
3222 case SplitTable_HeadlineOption::BorderCopy:
3223 {
3224 SwCollectTableLineBoxes aPara( false, eHdlnMode );
3225 SwTableLine* pLn = rTable.GetTabLines()[
3226 rTable.GetTabLines().size() - 1 ];
3227 for( const auto& rpBox : pLn->GetTabBoxes() )
3228 sw_Box_CollectBox(rpBox, &aPara );
3229
3230 aPara.SetValues( true );
3231 pLn = pNew->GetTable().GetTabLines()[ 0 ];
3232 for( auto& rpBox : pLn->GetTabBoxes() )
3233 sw_BoxSetSplitBoxFormats(rpBox, &aPara );
3234
3235 // Switch off repeating Header
3236 pNew->GetTable().SetRowsToRepeat( 0 );
3237 }
3238 break;
3239
3240 // Take over the Attributes of the first Line to the new one
3241 case SplitTable_HeadlineOption::BoxAttrCopy:
3242 case SplitTable_HeadlineOption::BoxAttrAllCopy:
3243 {
3244 SwHistory* pHst = nullptr;
3245 if( SplitTable_HeadlineOption::BoxAttrAllCopy == eHdlnMode && pUndo )
3246 pHst = pUndo->GetHistory();
3247
3248 SwCollectTableLineBoxes aPara( true, eHdlnMode, pHst );
3249 SwTableLine* pLn = rTable.GetTabLines()[ 0 ];
3250 for( const auto& rpBox : pLn->GetTabBoxes() )
3251 sw_Box_CollectBox(rpBox, &aPara );
3252
3253 aPara.SetValues( true );
3254 pLn = pNew->GetTable().GetTabLines()[ 0 ];
3255 for( auto& rpBox : pLn->GetTabBoxes() )
3256 sw_BoxSetSplitBoxFormats(rpBox, &aPara );
3257 }
3258 break;
3259
3260 case SplitTable_HeadlineOption::ContentCopy:
3261 rTable.CopyHeadlineIntoTable( *pNew );
3262 if( pUndo )
3263 pUndo->SetTableNodeOffset( pNew->GetIndex() );
3264 break;
3265
3266 case SplitTable_HeadlineOption::NONE:
3267 // Switch off repeating the Header
3268 pNew->GetTable().SetRowsToRepeat( 0 );
3269 break;
3270 }
3271
3272 // And insert Frames
3273 pNew->MakeOwnFrames();
3274
3275 // Insert a paragraph between the Table
3276 GetNodes().MakeTextNode( *pNew,
3277 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
3278 }
3279
3280 // Update Layout
3281 aFndBox.MakeFrames( rTable );
3282
3283 // TL_CHART2: need to inform chart of probably changed cell names
3284 UpdateCharts( rTable.GetFrameFormat()->GetName() );
3285
3286 // update table style formatting of both the tables
3287 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
3288 {
3289 pFEShell->UpdateTableStyleFormatting(pTNd);
3290 pFEShell->UpdateTableStyleFormatting(pNew);
3291 }
3292
3293 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3294 }
3295
lcl_ChgTableSize(SwTable & rTable)3296 static bool lcl_ChgTableSize( SwTable& rTable )
3297 {
3298 // The Attribute must not be set via the Modify or else all Boxes are
3299 // set back to 0.
3300 // So lock the Format.
3301 SwFrameFormat* pFormat = rTable.GetFrameFormat();
3302 SwFormatFrameSize aTableMaxSz( pFormat->GetFrameSize() );
3303
3304 if( USHRT_MAX == aTableMaxSz.GetWidth() )
3305 return false;
3306
3307 bool bLocked = pFormat->IsModifyLocked();
3308 pFormat->LockModify();
3309
3310 aTableMaxSz.SetWidth( 0 );
3311
3312 SwTableLines& rLns = rTable.GetTabLines();
3313 for( auto pLn : rLns )
3314 {
3315 SwTwips nMaxLnWidth = 0;
3316 SwTableBoxes& rBoxes = pLn->GetTabBoxes();
3317 for( auto pBox : rBoxes )
3318 nMaxLnWidth += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
3319
3320 if( nMaxLnWidth > aTableMaxSz.GetWidth() )
3321 aTableMaxSz.SetWidth( nMaxLnWidth );
3322 }
3323 pFormat->SetFormatAttr( aTableMaxSz );
3324 if( !bLocked ) // Release the Lock if appropriate
3325 pFormat->UnlockModify();
3326
3327 return true;
3328 }
3329
3330 namespace {
3331
3332 class SplitTable_Para
3333 {
3334 std::map<SwFrameFormat const*, SwFrameFormat*> m_aSrcDestMap;
3335 SwTableNode* m_pNewTableNode;
3336 SwTable& m_rOldTable;
3337
3338 public:
SplitTable_Para(SwTableNode * pNew,SwTable & rOld)3339 SplitTable_Para(SwTableNode* pNew, SwTable& rOld)
3340 : m_pNewTableNode(pNew)
3341 , m_rOldTable(rOld)
3342 {}
GetDestFormat(SwFrameFormat * pSrcFormat) const3343 SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
3344 {
3345 auto it = m_aSrcDestMap.find(pSrcFormat);
3346 return it == m_aSrcDestMap.end() ? nullptr : it->second;
3347 }
3348
InsertSrcDest(SwFrameFormat const * pSrcFormat,SwFrameFormat * pDestFormat)3349 void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
3350 {
3351 m_aSrcDestMap[pSrcFormat] = pDestFormat;
3352 }
3353
ChgBox(SwTableBox * pBox)3354 void ChgBox( SwTableBox* pBox )
3355 {
3356 m_rOldTable.GetTabSortBoxes().erase(pBox);
3357 m_pNewTableNode->GetTable().GetTabSortBoxes().insert(pBox);
3358 }
3359 };
3360
3361 }
3362
3363 static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara );
3364
lcl_SplitTable_CpyLine(SwTableLine * pLn,SplitTable_Para * pPara)3365 static void lcl_SplitTable_CpyLine( SwTableLine* pLn, SplitTable_Para* pPara )
3366 {
3367 SwFrameFormat *pSrcFormat = pLn->GetFrameFormat();
3368 SwTableLineFormat* pDestFormat = static_cast<SwTableLineFormat*>( pPara->GetDestFormat( pSrcFormat ) );
3369 if( pDestFormat == nullptr )
3370 {
3371 pPara->InsertSrcDest( pSrcFormat, pLn->ClaimFrameFormat() );
3372 }
3373 else
3374 pLn->ChgFrameFormat( pDestFormat );
3375
3376 for( auto& rpBox : pLn->GetTabBoxes() )
3377 lcl_SplitTable_CpyBox(rpBox, pPara );
3378 }
3379
lcl_SplitTable_CpyBox(SwTableBox * pBox,SplitTable_Para * pPara)3380 static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara )
3381 {
3382 SwFrameFormat *pSrcFormat = pBox->GetFrameFormat();
3383 SwTableBoxFormat* pDestFormat = static_cast<SwTableBoxFormat*>(pPara->GetDestFormat( pSrcFormat ));
3384 if( pDestFormat == nullptr )
3385 {
3386 pPara->InsertSrcDest( pSrcFormat, pBox->ClaimFrameFormat() );
3387 }
3388 else
3389 pBox->ChgFrameFormat( pDestFormat );
3390
3391 if( pBox->GetSttNd() )
3392 pPara->ChgBox( pBox );
3393 else
3394 for( SwTableLine* pLine : pBox->GetTabLines() )
3395 lcl_SplitTable_CpyLine( pLine, pPara );
3396 }
3397
SplitTable(SwNode & rPos,bool bAfter,bool bCalcNewSize)3398 SwTableNode* SwNodes::SplitTable( SwNode& rPos, bool bAfter,
3399 bool bCalcNewSize )
3400 {
3401 SwNode* pNd = &rPos;
3402 SwTableNode* pTNd = pNd->FindTableNode();
3403 if( !pTNd || pNd->IsTableNode() )
3404 return nullptr;
3405
3406 SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
3407
3408 // Find this Box/top-level line
3409 SwTable& rTable = pTNd->GetTable();
3410 SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
3411 if( !pBox )
3412 return nullptr;
3413
3414 SwTableLine* pLine = pBox->GetUpper();
3415 while( pLine->GetUpper() )
3416 pLine = pLine->GetUpper()->GetUpper();
3417
3418 // pLine now contains the top-level line
3419 sal_uInt16 nLinePos = rTable.GetTabLines().GetPos( pLine );
3420 if( USHRT_MAX == nLinePos ||
3421 ( bAfter ? ++nLinePos >= rTable.GetTabLines().size() : !nLinePos ))
3422 return nullptr; // Not found or last Line!
3423
3424 // Find the first Box of the succeeding Line
3425 SwTableLine* pNextLine = rTable.GetTabLines()[ nLinePos ];
3426 pBox = pNextLine->GetTabBoxes()[0];
3427 while( !pBox->GetSttNd() )
3428 pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
3429
3430 // Insert an EndNode and TableNode into the Nodes Array
3431 SwTableNode * pNewTableNd;
3432 {
3433 SwEndNode* pOldTableEndNd = pTNd->EndOfSectionNode()->GetEndNode();
3434 assert(pOldTableEndNd && "Where is the EndNode?");
3435
3436 new SwEndNode( *pBox->GetSttNd(), *pTNd );
3437 pNewTableNd = new SwTableNode( *pBox->GetSttNd() );
3438 pNewTableNd->GetTable().SetTableModel( rTable.IsNewModel() );
3439
3440 pOldTableEndNd->m_pStartOfSection = pNewTableNd;
3441 pNewTableNd->m_pEndOfSection = pOldTableEndNd;
3442
3443 SwNode* pBoxNd = const_cast<SwStartNode*>(pBox->GetSttNd()->GetStartNode());
3444 do {
3445 OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
3446 pBoxNd->m_pStartOfSection = pNewTableNd;
3447 pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
3448 } while( pBoxNd != pOldTableEndNd );
3449 }
3450
3451 {
3452 // Move the Lines
3453 SwTable& rNewTable = pNewTableNd->GetTable();
3454 rNewTable.GetTabLines().insert( rNewTable.GetTabLines().begin(),
3455 rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
3456
3457 /* From the back (bottom right) to the front (top left) deregister all Boxes from the
3458 Chart Data Provider. The Modify event is triggered in the calling function.
3459 TL_CHART2: */
3460 SwChartDataProvider *pPCD = rTable.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
3461 if( pPCD )
3462 {
3463 for (SwTableLines::size_type k = nLinePos; k < rTable.GetTabLines().size(); ++k)
3464 {
3465 const SwTableLines::size_type nLineIdx = (rTable.GetTabLines().size() - 1) - k + nLinePos;
3466 const SwTableBoxes::size_type nBoxCnt = rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes().size();
3467 for (SwTableBoxes::size_type j = 0; j < nBoxCnt; ++j)
3468 {
3469 const SwTableBoxes::size_type nIdx = nBoxCnt - 1 - j;
3470 pPCD->DeleteBox( &rTable, *rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes()[nIdx] );
3471 }
3472 }
3473 }
3474
3475 // Delete
3476 sal_uInt16 nDeleted = rTable.GetTabLines().size() - nLinePos;
3477 rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
3478
3479 // Move the affected Boxes. Make the Formats unique and correct the StartNodes
3480 SplitTable_Para aPara( pNewTableNd, rTable );
3481 for( SwTableLine* pNewLine : rNewTable.GetTabLines() )
3482 lcl_SplitTable_CpyLine( pNewLine, &aPara );
3483 rTable.CleanUpBottomRowSpan( nDeleted );
3484 }
3485
3486 {
3487 // Copy the Table FrameFormat
3488 SwFrameFormat* pOldTableFormat = rTable.GetFrameFormat();
3489 SwFrameFormat* pNewTableFormat = pOldTableFormat->GetDoc()->MakeTableFrameFormat(
3490 pOldTableFormat->GetDoc()->GetUniqueTableName(),
3491 pOldTableFormat->GetDoc()->GetDfltFrameFormat() );
3492
3493 *pNewTableFormat = *pOldTableFormat;
3494 pNewTableNd->GetTable().RegisterToFormat( *pNewTableFormat );
3495
3496 pNewTableNd->GetTable().SetTableStyleName(rTable.GetTableStyleName());
3497
3498 // Calculate a new Size?
3499 // lcl_ChgTableSize: Only execute the second call if the first call was
3500 // successful, thus has an absolute Size
3501 if( bCalcNewSize && lcl_ChgTableSize( rTable ) )
3502 lcl_ChgTableSize( pNewTableNd->GetTable() );
3503 }
3504
3505 // TL_CHART2: need to inform chart of probably changed cell names
3506 rTable.UpdateCharts();
3507
3508 return pNewTableNd; // That's it!
3509 }
3510
3511 /**
3512 * rPos needs to be in the Table that remains
3513 *
3514 * @param bWithPrev merge the current Table with the preceding
3515 * or succeeding one
3516 */
MergeTable(const SwPosition & rPos,bool bWithPrev)3517 bool SwDoc::MergeTable( const SwPosition& rPos, bool bWithPrev )
3518 {
3519 SwTableNode* pTableNd = rPos.GetNode().FindTableNode(), *pDelTableNd;
3520 if( !pTableNd )
3521 return false;
3522
3523 SwNodes& rNds = GetNodes();
3524 if( bWithPrev )
3525 pDelTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
3526 else
3527 pDelTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
3528 if( !pDelTableNd )
3529 return false;
3530
3531 if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr ||
3532 dynamic_cast<const SwDDETable*>( &pDelTableNd->GetTable() ) != nullptr)
3533 return false;
3534
3535 // Delete HTML Layout
3536 pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3537 pDelTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3538
3539 // Both Tables are present; we can start
3540 SwUndoMergeTable* pUndo = nullptr;
3541 std::unique_ptr<SwHistory> pHistory;
3542 if (GetIDocumentUndoRedo().DoesUndo())
3543 {
3544 pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev );
3545 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3546 pHistory.reset(new SwHistory);
3547 }
3548
3549 // Adapt all "TableFormulas"
3550 pTableNd->GetTable().Merge(pDelTableNd->GetTable(), pHistory.get());
3551
3552 // The actual merge
3553 bool bRet = rNds.MergeTable( bWithPrev ? *pTableNd : *pDelTableNd, !bWithPrev );
3554
3555 if( pHistory )
3556 {
3557 if( pHistory->Count() )
3558 pUndo->SaveFormula( *pHistory );
3559 pHistory.reset();
3560 }
3561 if( bRet )
3562 {
3563 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
3564 pFEShell->UpdateTableStyleFormatting();
3565
3566 getIDocumentState().SetModified();
3567 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3568 }
3569 return bRet;
3570 }
3571
MergeTable(SwNode & rPos,bool bWithPrev)3572 bool SwNodes::MergeTable( SwNode& rPos, bool bWithPrev )
3573 {
3574 SwTableNode* pDelTableNd = rPos.GetTableNode();
3575 OSL_ENSURE( pDelTableNd, "Where did the TableNode go?" );
3576
3577 SwTableNode* pTableNd = (*this)[ rPos.GetIndex() - 1]->FindTableNode();
3578 OSL_ENSURE( pTableNd, "Where did the TableNode go?" );
3579
3580 if( !pDelTableNd || !pTableNd )
3581 return false;
3582
3583 pDelTableNd->DelFrames();
3584
3585 SwTable& rDelTable = pDelTableNd->GetTable();
3586 SwTable& rTable = pTableNd->GetTable();
3587
3588 // Find Lines for the Layout update
3589 FndBox_ aFndBox( nullptr, nullptr );
3590 aFndBox.SetTableLines( rTable );
3591 aFndBox.DelFrames( rTable );
3592
3593 // TL_CHART2:
3594 // tell the charts about the table to be deleted and have them use their own data
3595 GetDoc().getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable );
3596
3597 // Sync the TableFormat's Width
3598 {
3599 const SwFormatFrameSize& rTableSz = rTable.GetFrameFormat()->GetFrameSize();
3600 const SwFormatFrameSize& rDelTableSz = rDelTable.GetFrameFormat()->GetFrameSize();
3601 if( rTableSz != rDelTableSz )
3602 {
3603 // The needs correction
3604 if( bWithPrev )
3605 rDelTable.GetFrameFormat()->SetFormatAttr( rTableSz );
3606 else
3607 rTable.GetFrameFormat()->SetFormatAttr( rDelTableSz );
3608 }
3609 }
3610
3611 if( !bWithPrev )
3612 {
3613 // Transfer all Attributes of the succeeding Table to the preceding one
3614 // We do this, because the succeeding one is deleted when deleting the Node
3615 rTable.SetRowsToRepeat( rDelTable.GetRowsToRepeat() );
3616 rTable.SetTableChgMode( rDelTable.GetTableChgMode() );
3617
3618 rTable.GetFrameFormat()->LockModify();
3619 *rTable.GetFrameFormat() = *rDelTable.GetFrameFormat();
3620 // Also switch the Name
3621 rTable.GetFrameFormat()->SetFormatName( rDelTable.GetFrameFormat()->GetName() );
3622 rTable.GetFrameFormat()->UnlockModify();
3623 }
3624
3625 // Move the Lines and Boxes
3626 SwTableLines::size_type nOldSize = rTable.GetTabLines().size();
3627 rTable.GetTabLines().insert( rTable.GetTabLines().begin() + nOldSize,
3628 rDelTable.GetTabLines().begin(), rDelTable.GetTabLines().end() );
3629 rDelTable.GetTabLines().clear();
3630
3631 rTable.GetTabSortBoxes().insert( rDelTable.GetTabSortBoxes() );
3632 rDelTable.GetTabSortBoxes().clear();
3633
3634 // The preceding Table always remains, while the succeeding one is deleted
3635 SwEndNode* pTableEndNd = pDelTableNd->EndOfSectionNode();
3636 pTableNd->m_pEndOfSection = pTableEndNd;
3637
3638 SwNodeIndex aIdx( *pDelTableNd, 1 );
3639
3640 SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
3641 do {
3642 OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
3643 pBoxNd->m_pStartOfSection = pTableNd;
3644 pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
3645 } while( pBoxNd != pTableEndNd );
3646 pBoxNd->m_pStartOfSection = pTableNd;
3647
3648 aIdx -= SwNodeOffset(2);
3649 DelNodes( aIdx, SwNodeOffset(2) );
3650
3651 // tweak the conditional styles at the first inserted Line
3652 const SwTableLine* pFirstLn = rTable.GetTabLines()[ nOldSize ];
3653 sw_LineSetHeadCondColl( pFirstLn );
3654
3655 // Clean up the Borders
3656 if( nOldSize )
3657 {
3658 SwGCLineBorder aPara( rTable );
3659 aPara.nLinePos = --nOldSize;
3660 pFirstLn = rTable.GetTabLines()[ nOldSize ];
3661 sw_GC_Line_Border( pFirstLn, &aPara );
3662 }
3663
3664 // Update Layout
3665 aFndBox.MakeFrames( rTable );
3666
3667 return true;
3668 }
3669
3670 namespace {
3671
3672 // Use the PtrArray's ForEach method
3673 struct SetAFormatTabPara
3674 {
3675 SwTableAutoFormat& rTableFormat;
3676 SwUndoTableAutoFormat* pUndo;
3677 sal_uInt16 nEndBox, nCurBox;
3678 sal_uInt8 nAFormatLine, nAFormatBox;
3679 bool bSingleRowTable;
3680
SetAFormatTabPara__anone93ec3e60511::SetAFormatTabPara3681 explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
3682 : rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
3683 nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
3684 {}
3685 };
3686
3687 }
3688
3689 // Forward declare so that the Lines and Boxes can use recursion
3690 static bool lcl_SetAFormatBox(FndBox_ &, SetAFormatTabPara *pSetPara, bool bResetDirect);
3691 static bool lcl_SetAFormatLine(FndLine_ &, SetAFormatTabPara *pPara, bool bResetDirect);
3692
lcl_SetAFormatLine(FndLine_ & rLine,SetAFormatTabPara * pPara,bool bResetDirect)3693 static bool lcl_SetAFormatLine(FndLine_ & rLine, SetAFormatTabPara *pPara, bool bResetDirect)
3694 {
3695 for (auto const& it : rLine.GetBoxes())
3696 {
3697 lcl_SetAFormatBox(*it, pPara, bResetDirect);
3698 }
3699 return true;
3700 }
3701
lcl_SetAFormatBox(FndBox_ & rBox,SetAFormatTabPara * pSetPara,bool bResetDirect)3702 static bool lcl_SetAFormatBox(FndBox_ & rBox, SetAFormatTabPara *pSetPara, bool bResetDirect)
3703 {
3704 if (!rBox.GetUpper()->GetUpper()) // Box on first level?
3705 {
3706 if( !pSetPara->nCurBox )
3707 pSetPara->nAFormatBox = 0;
3708 else if( pSetPara->nCurBox == pSetPara->nEndBox )
3709 pSetPara->nAFormatBox = 3;
3710 else //Even column(1) or Odd column(2)
3711 pSetPara->nAFormatBox = static_cast<sal_uInt8>(1 + ((pSetPara->nCurBox-1) & 1));
3712 }
3713
3714 if (rBox.GetBox()->GetSttNd())
3715 {
3716 SwTableBox* pSetBox = rBox.GetBox();
3717 if (!pSetBox->HasDirectFormatting() || bResetDirect)
3718 {
3719 if (bResetDirect)
3720 pSetBox->SetDirectFormatting(false);
3721
3722 SwDoc* pDoc = pSetBox->GetFrameFormat()->GetDoc();
3723 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet(pDoc->GetAttrPool());
3724 SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
3725 sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
3726 const bool bSingleRowTable = pSetPara->bSingleRowTable;
3727 const bool bSingleColTable = pSetPara->nEndBox == 0;
3728 pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aCharSet, SwTableAutoFormatUpdateFlags::Char, nullptr);
3729 pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aBoxSet, SwTableAutoFormatUpdateFlags::Box, pDoc->GetNumberFormatter());
3730
3731 if (aCharSet.Count())
3732 {
3733 SwNodeOffset nSttNd = pSetBox->GetSttIdx()+1;
3734 SwNodeOffset nEndNd = pSetBox->GetSttNd()->EndOfSectionIndex();
3735 for (; nSttNd < nEndNd; ++nSttNd)
3736 {
3737 SwContentNode* pNd = pDoc->GetNodes()[ nSttNd ]->GetContentNode();
3738 if (pNd)
3739 pNd->SetAttr(aCharSet);
3740 }
3741 }
3742
3743 if (aBoxSet.Count())
3744 {
3745 if (pSetPara->pUndo && SfxItemState::SET == aBoxSet.GetItemState(RES_BOXATR_FORMAT))
3746 pSetPara->pUndo->SaveBoxContent( *pSetBox );
3747
3748 pSetBox->ClaimFrameFormat()->SetFormatAttr(aBoxSet);
3749 }
3750 }
3751 }
3752 else
3753 {
3754 // Not sure how this situation can occur, but apparently we have some kind of table in table.
3755 // I am guessing at how to best handle singlerow in this situation.
3756 const bool bOrigSingleRowTable = pSetPara->bSingleRowTable;
3757 pSetPara->bSingleRowTable = rBox.GetLines().size() == 1;
3758 for (auto const& rpFndLine : rBox.GetLines())
3759 {
3760 lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
3761 }
3762 pSetPara->bSingleRowTable = bOrigSingleRowTable;
3763 }
3764
3765 if (!rBox.GetUpper()->GetUpper()) // a BaseLine
3766 ++pSetPara->nCurBox;
3767 return true;
3768 }
3769
SetTableAutoFormat(const SwSelBoxes & rBoxes,const SwTableAutoFormat & rNew,bool bResetDirect,bool const isSetStyleName)3770 bool SwDoc::SetTableAutoFormat(const SwSelBoxes& rBoxes, const SwTableAutoFormat& rNew, bool bResetDirect, bool const isSetStyleName)
3771 {
3772 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
3773 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
3774 if( !pTableNd )
3775 return false;
3776
3777 // Find all Boxes/Lines
3778 FndBox_ aFndBox( nullptr, nullptr );
3779 {
3780 FndPara aPara( rBoxes, &aFndBox );
3781 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
3782 }
3783 if( aFndBox.GetLines().empty() )
3784 return false;
3785
3786 SwTable &table = pTableNd->GetTable();
3787 table.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
3788
3789 FndBox_* pFndBox = &aFndBox;
3790 while( 1 == pFndBox->GetLines().size() &&
3791 1 == pFndBox->GetLines().front()->GetBoxes().size())
3792 {
3793 pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
3794 }
3795
3796 if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
3797 pFndBox = pFndBox->GetUpper()->GetUpper();
3798
3799 // Disable Undo, but first store parameters
3800 SwUndoTableAutoFormat* pUndo = nullptr;
3801 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
3802 if (bUndo)
3803 {
3804 pUndo = new SwUndoTableAutoFormat( *pTableNd, rNew );
3805 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
3806 GetIDocumentUndoRedo().DoUndo(false);
3807 }
3808
3809 if (isSetStyleName)
3810 { // tdf#98226 do this here where undo can record it
3811 pTableNd->GetTable().SetTableStyleName(rNew.GetName());
3812 }
3813
3814 rNew.RestoreTableProperties(table);
3815
3816 SetAFormatTabPara aPara( rNew );
3817 FndLines_t& rFLns = pFndBox->GetLines();
3818 aPara.bSingleRowTable = rFLns.size() == 1;
3819
3820 for (FndLines_t::size_type n = 0; n < rFLns.size(); ++n)
3821 {
3822 FndLine_* pLine = rFLns[n].get();
3823
3824 // Set Upper to 0 (thus simulate BaseLine)
3825 FndBox_* pSaveBox = pLine->GetUpper();
3826 pLine->SetUpper( nullptr );
3827
3828 if( !n )
3829 aPara.nAFormatLine = 0;
3830 else if (static_cast<size_t>(n+1) == rFLns.size())
3831 aPara.nAFormatLine = 3;
3832 else
3833 aPara.nAFormatLine = static_cast<sal_uInt8>(1 + ((n-1) & 1 ));
3834
3835 aPara.nAFormatBox = 0;
3836 aPara.nCurBox = 0;
3837 aPara.nEndBox = pLine->GetBoxes().size()-1;
3838 aPara.pUndo = pUndo;
3839 for (auto const& it : pLine->GetBoxes())
3840 {
3841 lcl_SetAFormatBox(*it, &aPara, bResetDirect);
3842 }
3843
3844 pLine->SetUpper( pSaveBox );
3845 }
3846
3847 if( pUndo )
3848 {
3849 GetIDocumentUndoRedo().DoUndo(bUndo);
3850 }
3851
3852 getIDocumentState().SetModified();
3853 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3854
3855 return true;
3856 }
3857
3858 /**
3859 * Find out who has the Attributes
3860 */
GetTableAutoFormat(const SwSelBoxes & rBoxes,SwTableAutoFormat & rGet)3861 bool SwDoc::GetTableAutoFormat( const SwSelBoxes& rBoxes, SwTableAutoFormat& rGet )
3862 {
3863 OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
3864 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
3865 if( !pTableNd )
3866 return false;
3867
3868 // Find all Boxes/Lines
3869 FndBox_ aFndBox( nullptr, nullptr );
3870 {
3871 FndPara aPara( rBoxes, &aFndBox );
3872 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
3873 }
3874 if( aFndBox.GetLines().empty() )
3875 return false;
3876
3877 // Store table properties
3878 SwTable &table = pTableNd->GetTable();
3879 rGet.StoreTableProperties(table);
3880
3881 FndBox_* pFndBox = &aFndBox;
3882 while( 1 == pFndBox->GetLines().size() &&
3883 1 == pFndBox->GetLines().front()->GetBoxes().size())
3884 {
3885 pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
3886 }
3887
3888 if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
3889 pFndBox = pFndBox->GetUpper()->GetUpper();
3890
3891 FndLines_t& rFLns = pFndBox->GetLines();
3892
3893 sal_uInt16 aLnArr[4];
3894 aLnArr[0] = 0;
3895 aLnArr[1] = 1 < rFLns.size() ? 1 : 0;
3896 aLnArr[2] = 2 < rFLns.size() ? 2 : aLnArr[1];
3897 aLnArr[3] = rFLns.size() - 1;
3898
3899 for( sal_uInt8 nLine = 0; nLine < 4; ++nLine )
3900 {
3901 FndLine_& rLine = *rFLns[ aLnArr[ nLine ] ];
3902
3903 sal_uInt16 aBoxArr[4];
3904 aBoxArr[0] = 0;
3905 aBoxArr[1] = 1 < rLine.GetBoxes().size() ? 1 : 0;
3906 aBoxArr[2] = 2 < rLine.GetBoxes().size() ? 2 : aBoxArr[1];
3907 aBoxArr[3] = rLine.GetBoxes().size() - 1;
3908
3909 for( sal_uInt8 nBox = 0; nBox < 4; ++nBox )
3910 {
3911 SwTableBox* pFBox = rLine.GetBoxes()[ aBoxArr[ nBox ] ]->GetBox();
3912 // Always apply to the first ones
3913 while( !pFBox->GetSttNd() )
3914 pFBox = pFBox->GetTabLines()[0]->GetTabBoxes()[0];
3915
3916 sal_uInt8 nPos = nLine * 4 + nBox;
3917 SwNodeIndex aIdx( *pFBox->GetSttNd(), 1 );
3918 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
3919 if( !pCNd )
3920 pCNd = SwNodes::GoNext(&aIdx);
3921
3922 if( pCNd )
3923 rGet.UpdateFromSet( nPos, pCNd->GetSwAttrSet(),
3924 SwTableAutoFormatUpdateFlags::Char, nullptr );
3925 rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
3926 SwTableAutoFormatUpdateFlags::Box,
3927 GetNumberFormatter() );
3928 }
3929 }
3930
3931 return true;
3932 }
3933
GetTableStyles()3934 SwTableAutoFormatTable& SwDoc::GetTableStyles()
3935 {
3936 if (!m_pTableStyles)
3937 {
3938 m_pTableStyles.reset(new SwTableAutoFormatTable);
3939 m_pTableStyles->Load();
3940 }
3941 return *m_pTableStyles;
3942 }
3943
GetUniqueTableName() const3944 OUString SwDoc::GetUniqueTableName() const
3945 {
3946 if( IsInMailMerge())
3947 {
3948 OUString newName = "MailMergeTable"
3949 + DateTimeToOUString( DateTime( DateTime::SYSTEM ) )
3950 + OUString::number( mpTableFrameFormatTable->size() + 1 );
3951 return newName;
3952 }
3953
3954 const OUString aName(SwResId(STR_TABLE_DEFNAME));
3955
3956 const size_t nFlagSize = ( mpTableFrameFormatTable->size() / 8 ) + 2;
3957
3958 std::unique_ptr<sal_uInt8[]> pSetFlags( new sal_uInt8[ nFlagSize ] );
3959 memset( pSetFlags.get(), 0, nFlagSize );
3960
3961 for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
3962 {
3963 const SwTableFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
3964 if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
3965 pFormat->GetName().startsWith( aName ) )
3966 {
3967 // Get number and set the Flag
3968 const sal_Int32 nNmLen = aName.getLength();
3969 size_t nNum = o3tl::toInt32(pFormat->GetName().subView( nNmLen ));
3970 if( nNum-- && nNum < mpTableFrameFormatTable->size() )
3971 pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
3972 }
3973 }
3974
3975 // All numbers are flagged properly, thus calculate the right number
3976 size_t nNum = mpTableFrameFormatTable->size();
3977 for( size_t n = 0; n < nFlagSize; ++n )
3978 {
3979 auto nTmp = pSetFlags[ n ];
3980 if( nTmp != 0xFF )
3981 {
3982 // Calculate the number
3983 nNum = n * 8;
3984 while( nTmp & 1 )
3985 {
3986 ++nNum;
3987 nTmp >>= 1;
3988 }
3989 break;
3990 }
3991 }
3992
3993 return aName + OUString::number( ++nNum );
3994 }
3995
FindTableFormatByName(const OUString & rName,bool bAll) const3996 SwTableFormat* SwDoc::FindTableFormatByName( const OUString& rName, bool bAll ) const
3997 {
3998 const SwFormat* pRet = nullptr;
3999 if( bAll )
4000 pRet = mpTableFrameFormatTable->FindFormatByName( rName );
4001 else
4002 {
4003 auto [it, itEnd] = mpTableFrameFormatTable->findRangeByName(rName);
4004 // Only the ones set in the Doc
4005 for( ; it != itEnd; ++it )
4006 {
4007 const SwFrameFormat* pFormat = *it;
4008 if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
4009 pFormat->GetName() == rName )
4010 {
4011 pRet = pFormat;
4012 break;
4013 }
4014 }
4015 }
4016 return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
4017 }
4018
SetColRowWidthHeight(SwTableBox & rCurrentBox,TableChgWidthHeightType eType,SwTwips nAbsDiff,SwTwips nRelDiff)4019 void SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
4020 SwTwips nAbsDiff, SwTwips nRelDiff )
4021 {
4022 SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
4023 std::unique_ptr<SwUndo> pUndo;
4024
4025 pTableNd->GetTable().SwitchFormulasToInternalRepresentation();
4026 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
4027 bool bRet = false;
4028 switch( extractPosition(eType) )
4029 {
4030 case TableChgWidthHeightType::ColLeft:
4031 case TableChgWidthHeightType::ColRight:
4032 case TableChgWidthHeightType::CellLeft:
4033 case TableChgWidthHeightType::CellRight:
4034 {
4035 bRet = pTableNd->GetTable().SetColWidth( rCurrentBox,
4036 eType, nAbsDiff, nRelDiff,
4037 bUndo ? &pUndo : nullptr );
4038 }
4039 break;
4040 case TableChgWidthHeightType::RowBottom:
4041 case TableChgWidthHeightType::CellTop:
4042 case TableChgWidthHeightType::CellBottom:
4043 bRet = pTableNd->GetTable().SetRowHeight( rCurrentBox,
4044 eType, nAbsDiff, nRelDiff,
4045 bUndo ? &pUndo : nullptr );
4046 break;
4047 default: break;
4048 }
4049
4050 GetIDocumentUndoRedo().DoUndo(bUndo); // SetColWidth can turn it off
4051 if( pUndo )
4052 {
4053 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4054 }
4055
4056 if( bRet )
4057 {
4058 getIDocumentState().SetModified();
4059 }
4060 }
4061
IsNumberFormat(const OUString & aString,sal_uInt32 & F_Index,double & fOutNumber)4062 bool SwDoc::IsNumberFormat( const OUString& aString, sal_uInt32& F_Index, double& fOutNumber )
4063 {
4064 if( aString.getLength() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
4065 return false;
4066
4067 // remove any comment anchor marks
4068 return GetNumberFormatter()->IsNumberFormat(
4069 aString.replaceAll(OUStringChar(CH_TXTATR_INWORD), u""), F_Index, fOutNumber);
4070 }
4071
ChkBoxNumFormat(SwTableBox & rBox,bool bCallUpdate)4072 void SwDoc::ChkBoxNumFormat( SwTableBox& rBox, bool bCallUpdate )
4073 {
4074 // Optimization: If the Box says it's Text, it remains Text
4075 const SwTableBoxNumFormat* pNumFormatItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT,
4076 false );
4077 if( pNumFormatItem && GetNumberFormatter()->IsTextFormat(pNumFormatItem->GetValue()) )
4078 return ;
4079
4080 std::unique_ptr<SwUndoTableNumFormat> pUndo;
4081
4082 bool bIsEmptyTextNd;
4083 bool bChgd = true;
4084 sal_uInt32 nFormatIdx;
4085 double fNumber;
4086 if( rBox.HasNumContent( fNumber, nFormatIdx, bIsEmptyTextNd ) )
4087 {
4088 if( !rBox.IsNumberChanged() )
4089 bChgd = false;
4090 else
4091 {
4092 if (GetIDocumentUndoRedo().DoesUndo())
4093 {
4094 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
4095 pUndo.reset(new SwUndoTableNumFormat( rBox ));
4096 pUndo->SetNumFormat( nFormatIdx, fNumber );
4097 }
4098
4099 SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
4100 SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( GetAttrPool() );
4101
4102 bool bLockModify = true;
4103 bool bSetNumberFormat = IsInsTableFormatNum();
4104 const bool bForceNumberFormat = IsInsTableFormatNum() && IsInsTableChangeNumFormat();
4105
4106 // if the user forced a number format in this cell previously,
4107 // keep it, unless the user set that she wants the full number
4108 // format recognition
4109 if( pNumFormatItem && !bForceNumberFormat )
4110 {
4111 sal_uLong nOldNumFormat = pNumFormatItem->GetValue();
4112 SvNumberFormatter* pNumFormatr = GetNumberFormatter();
4113
4114 SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIdx );
4115 if( nFormatType == pNumFormatr->GetType( nOldNumFormat ) || SvNumFormatType::NUMBER == nFormatType )
4116 {
4117 // Current and specified NumFormat match
4118 // -> keep old Format
4119 nFormatIdx = nOldNumFormat;
4120 bSetNumberFormat = true;
4121 }
4122 else
4123 {
4124 // Current and specified NumFormat do not match
4125 // -> insert as Text
4126 bLockModify = bSetNumberFormat = false;
4127 }
4128 }
4129
4130 if( bSetNumberFormat || bForceNumberFormat )
4131 {
4132 pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
4133
4134 aBoxSet.Put( SwTableBoxValue( fNumber ));
4135 aBoxSet.Put( SwTableBoxNumFormat( nFormatIdx ));
4136 }
4137
4138 // It's not enough to only reset the Formula.
4139 // Make sure that the Text is formatted accordingly
4140 if( !bSetNumberFormat && !bIsEmptyTextNd && pNumFormatItem )
4141 {
4142 // Just resetting Attributes is not enough
4143 // Make sure that the Text is formatted accordingly
4144 pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
4145 }
4146
4147 if( bLockModify ) pBoxFormat->LockModify();
4148 pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
4149 if( bLockModify ) pBoxFormat->UnlockModify();
4150
4151 if( bSetNumberFormat )
4152 pBoxFormat->SetFormatAttr( aBoxSet );
4153 }
4154 }
4155 else
4156 {
4157 // It's not a number
4158 SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
4159 if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false ) ||
4160 SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, false ) )
4161 {
4162 if (GetIDocumentUndoRedo().DoesUndo())
4163 {
4164 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
4165 pUndo.reset(new SwUndoTableNumFormat( rBox ));
4166 }
4167
4168 pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
4169
4170 // Remove all number formats
4171 sal_uInt16 nWhich1 = RES_BOXATR_FORMULA;
4172 if( !bIsEmptyTextNd )
4173 {
4174 nWhich1 = RES_BOXATR_FORMAT;
4175
4176 // Just resetting Attributes is not enough
4177 // Make sure that the Text is formatted accordingly
4178 pBoxFormat->SetFormatAttr( *GetDfltAttr( nWhich1 ));
4179 }
4180 pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
4181 }
4182 else
4183 bChgd = false;
4184 }
4185
4186 if( !bChgd )
4187 return;
4188
4189 if( pUndo )
4190 {
4191 pUndo->SetBox( rBox );
4192 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
4193 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
4194 }
4195
4196 const SwTableNode* pTableNd = rBox.GetSttNd()->FindTableNode();
4197 if( bCallUpdate )
4198 {
4199 getIDocumentFieldsAccess().UpdateTableFields(&pTableNd->GetTable());
4200
4201 // TL_CHART2: update charts (when cursor leaves cell and
4202 // automatic update is enabled)
4203 if (AUTOUPD_FIELD_AND_CHARTS == GetDocumentSettingManager().getFieldUpdateFlags(true))
4204 pTableNd->GetTable().UpdateCharts();
4205 }
4206 getIDocumentState().SetModified();
4207 }
4208
SetTableBoxFormulaAttrs(SwTableBox & rBox,const SfxItemSet & rSet)4209 void SwDoc::SetTableBoxFormulaAttrs( SwTableBox& rBox, const SfxItemSet& rSet )
4210 {
4211 if (GetIDocumentUndoRedo().DoesUndo())
4212 {
4213 GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoTableNumFormat>(rBox, &rSet) );
4214 }
4215
4216 SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
4217 if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
4218 {
4219 pBoxFormat->LockModify();
4220 pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
4221 pBoxFormat->UnlockModify();
4222 }
4223 else if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE ))
4224 {
4225 pBoxFormat->LockModify();
4226 pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
4227 pBoxFormat->UnlockModify();
4228 }
4229 pBoxFormat->SetFormatAttr( rSet );
4230 getIDocumentState().SetModified();
4231 }
4232
ClearLineNumAttrs(SwPosition const & rPos)4233 void SwDoc::ClearLineNumAttrs( SwPosition const & rPos )
4234 {
4235 SwPaM aPam(rPos);
4236 aPam.Move(fnMoveBackward);
4237 SwContentNode *pNode = aPam.GetPointContentNode();
4238 if ( nullptr == pNode )
4239 return ;
4240 if( !pNode->IsTextNode() )
4241 return;
4242
4243 SwTextNode * pTextNode = pNode->GetTextNode();
4244 if (!(pTextNode && pTextNode->IsNumbered()
4245 && pTextNode->GetText().isEmpty()))
4246 return;
4247
4248 SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1>
4249 rSet( pTextNode->GetDoc().GetAttrPool() );
4250 pTextNode->SwContentNode::GetAttr( rSet );
4251 const SfxStringItem* pFormatItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, false );
4252 if ( !pFormatItem )
4253 return;
4254
4255 SwUndoDelNum * pUndo;
4256 if( GetIDocumentUndoRedo().DoesUndo() )
4257 {
4258 GetIDocumentUndoRedo().ClearRedo();
4259 pUndo = new SwUndoDelNum( aPam );
4260 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
4261 }
4262 else
4263 pUndo = nullptr;
4264 SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
4265 aRegH.RegisterInModify( pTextNode , *pTextNode );
4266 if ( pUndo )
4267 pUndo->AddNode( *pTextNode );
4268 std::unique_ptr<SfxStringItem> pNewItem(pFormatItem->Clone());
4269 pNewItem->SetValue(OUString());
4270 rSet.Put( std::move(pNewItem) );
4271 pTextNode->SetAttr( rSet );
4272 }
4273
ClearBoxNumAttrs(SwNode & rNode)4274 void SwDoc::ClearBoxNumAttrs( SwNode& rNode )
4275 {
4276 SwStartNode* pSttNd = rNode.FindSttNodeByType( SwTableBoxStartNode );
4277 if( nullptr == pSttNd ||
4278 SwNodeOffset(2) != pSttNd->EndOfSectionIndex() - pSttNd->GetIndex())
4279 return;
4280
4281 SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
4282 GetTableBox( pSttNd->GetIndex() );
4283
4284 const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
4285 const SwTableBoxNumFormat* pFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false );
4286 if( !pFormatItem ||
4287 SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
4288 SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
4289 return;
4290
4291 if (GetIDocumentUndoRedo().DoesUndo())
4292 {
4293 GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoTableNumFormat>(*pBox));
4294 }
4295
4296 SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
4297
4298 // Keep TextFormats!
4299 sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
4300 if( pFormatItem && GetNumberFormatter()->IsTextFormat(
4301 pFormatItem->GetValue() ))
4302 nWhich1 = RES_BOXATR_FORMULA;
4303 else
4304 // Just resetting Attributes is not enough
4305 // Make sure that the Text is formatted accordingly
4306 pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
4307
4308 pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
4309 getIDocumentState().SetModified();
4310 }
4311
4312 /**
4313 * Copies a Table from the same or another Doc into itself
4314 * We create a new Table or an existing one is filled with the Content.
4315 * We either fill in the Content from a certain Box or a certain TableSelection
4316 *
4317 * This method is called by edglss.cxx/fecopy.cxx
4318 */
InsCopyOfTable(SwPosition & rInsPos,const SwSelBoxes & rBoxes,const SwTable * pCpyTable,bool bCpyName,bool bCorrPos,const OUString & rStyleName)4319 bool SwDoc::InsCopyOfTable( SwPosition& rInsPos, const SwSelBoxes& rBoxes,
4320 const SwTable* pCpyTable, bool bCpyName, bool bCorrPos, const OUString& rStyleName )
4321 {
4322 bool bRet;
4323
4324 const SwTableNode* pSrcTableNd = pCpyTable
4325 ? pCpyTable->GetTableNode()
4326 : rBoxes[ 0 ]->GetSttNd()->FindTableNode();
4327
4328 SwTableNode * pInsTableNd = rInsPos.GetNode().FindTableNode();
4329
4330 bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
4331 if( !pCpyTable && !pInsTableNd )
4332 {
4333 std::unique_ptr<SwUndoCpyTable> pUndo;
4334 if (bUndo)
4335 {
4336 GetIDocumentUndoRedo().ClearRedo();
4337 pUndo.reset(new SwUndoCpyTable(*this));
4338 }
4339
4340 {
4341 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
4342 bRet = pSrcTableNd->GetTable().MakeCopy( *this, rInsPos, rBoxes,
4343 bCpyName, rStyleName );
4344 }
4345
4346 if( pUndo && bRet )
4347 {
4348 pInsTableNd = GetNodes()[ rInsPos.GetNodeIndex() - 1 ]->FindTableNode();
4349
4350 pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
4351 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4352 }
4353 }
4354 else
4355 {
4356 RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
4357 if( getIDocumentRedlineAccess().IsRedlineOn() )
4358 getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
4359 RedlineFlags::ShowInsert |
4360 RedlineFlags::ShowDelete );
4361
4362 std::unique_ptr<SwUndoTableCpyTable> pUndo;
4363 if (bUndo)
4364 {
4365 GetIDocumentUndoRedo().ClearRedo();
4366 pUndo.reset(new SwUndoTableCpyTable(*this));
4367 GetIDocumentUndoRedo().DoUndo(false);
4368 }
4369
4370 rtl::Reference<SwDoc> xCpyDoc(&const_cast<SwDoc&>(pSrcTableNd->GetDoc()));
4371 bool bDelCpyDoc = xCpyDoc == this;
4372
4373 if( bDelCpyDoc )
4374 {
4375 // Copy the Table into a temporary Doc
4376 xCpyDoc = new SwDoc;
4377
4378 SwPosition aPos( xCpyDoc->GetNodes().GetEndOfContent() );
4379 if( !pSrcTableNd->GetTable().MakeCopy( *xCpyDoc, aPos, rBoxes, true ))
4380 {
4381 xCpyDoc.clear();
4382
4383 if( pUndo )
4384 {
4385 GetIDocumentUndoRedo().DoUndo(bUndo);
4386 }
4387 return false;
4388 }
4389 aPos.Adjust(SwNodeOffset(-1)); // Set to the Table's EndNode
4390 pSrcTableNd = aPos.GetNode().FindTableNode();
4391 }
4392
4393 const SwStartNode* pSttNd = rInsPos.GetNode().FindTableBoxStartNode();
4394
4395 rInsPos.nContent.Assign( nullptr, 0 );
4396
4397 // no complex into complex, but copy into or from new model is welcome
4398 if( ( !pSrcTableNd->GetTable().IsTableComplex() || pInsTableNd->GetTable().IsNewModel() )
4399 && ( bDelCpyDoc || !rBoxes.empty() ) )
4400 {
4401 // Copy the Table "relatively"
4402 const SwSelBoxes* pBoxes;
4403 SwSelBoxes aBoxes;
4404
4405 if( bDelCpyDoc )
4406 {
4407 SwTableBox* pBox = pInsTableNd->GetTable().GetTableBox(
4408 pSttNd->GetIndex() );
4409 OSL_ENSURE( pBox, "Box is not in this Table" );
4410 aBoxes.insert( pBox );
4411 pBoxes = &aBoxes;
4412 }
4413 else
4414 pBoxes = &rBoxes;
4415
4416 // Copy Table to the selected Lines
4417 bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
4418 *pBoxes, pUndo.get() );
4419 }
4420 else
4421 {
4422 SwNodeIndex aNdIdx( *pSttNd, 1 );
4423 bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
4424 aNdIdx, pUndo.get() );
4425 }
4426
4427 xCpyDoc.clear();
4428
4429 if( pUndo )
4430 {
4431 // If the Table could not be copied, delete the Undo object
4432 GetIDocumentUndoRedo().DoUndo(bUndo);
4433 if( bRet || !pUndo->IsEmpty() )
4434 {
4435 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
4436 }
4437 }
4438
4439 if( bCorrPos )
4440 {
4441 rInsPos.Assign( *pSttNd );
4442 SwNodes::GoNext(&rInsPos);
4443 }
4444 getIDocumentRedlineAccess().SetRedlineFlags( eOld );
4445 }
4446
4447 if( bRet )
4448 {
4449 getIDocumentState().SetModified();
4450 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
4451 }
4452 return bRet;
4453 }
4454
UnProtectTableCells(SwTable & rTable)4455 bool SwDoc::UnProtectTableCells( SwTable& rTable )
4456 {
4457 bool bChgd = false;
4458 std::unique_ptr<SwUndoAttrTable> pUndo;
4459 if (GetIDocumentUndoRedo().DoesUndo())
4460 pUndo.reset(new SwUndoAttrTable( *rTable.GetTableNode() ));
4461
4462 SwTableSortBoxes& rSrtBox = rTable.GetTabSortBoxes();
4463 for (size_t i = rSrtBox.size(); i; )
4464 {
4465 SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
4466 if( pBoxFormat->GetProtect().IsContentProtected() )
4467 {
4468 pBoxFormat->ResetFormatAttr( RES_PROTECT );
4469 bChgd = true;
4470 }
4471 }
4472
4473 if( pUndo && bChgd )
4474 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4475 return bChgd;
4476 }
4477
UnProtectCells(const OUString & rName)4478 void SwDoc::UnProtectCells( const OUString& rName )
4479 {
4480 SwTableFormat* pFormat = FindTableFormatByName( rName );
4481 if( pFormat )
4482 {
4483 bool bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
4484 if( bChgd )
4485 getIDocumentState().SetModified();
4486 }
4487 }
4488
UnProtectCells(const SwSelBoxes & rBoxes)4489 bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
4490 {
4491 bool bChgd = false;
4492 if( !rBoxes.empty() )
4493 {
4494 std::unique_ptr<SwUndoAttrTable> pUndo;
4495 if (GetIDocumentUndoRedo().DoesUndo())
4496 pUndo.reset(new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() ));
4497
4498 std::map<SwFrameFormat*, SwTableBoxFormat*> aFormatsMap;
4499 for (size_t i = rBoxes.size(); i; )
4500 {
4501 SwTableBox* pBox = rBoxes[ --i ];
4502 SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
4503 if( pBoxFormat->GetProtect().IsContentProtected() )
4504 {
4505 std::map<SwFrameFormat*, SwTableBoxFormat*>::const_iterator const it =
4506 aFormatsMap.find(pBoxFormat);
4507 if (aFormatsMap.end() != it)
4508 pBox->ChgFrameFormat(it->second);
4509 else
4510 {
4511 SwTableBoxFormat *const pNewBoxFormat(
4512 static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()));
4513 pNewBoxFormat->ResetFormatAttr( RES_PROTECT );
4514 aFormatsMap.insert(std::make_pair(pBoxFormat, pNewBoxFormat));
4515 }
4516 bChgd = true;
4517 }
4518 }
4519
4520 if( pUndo && bChgd )
4521 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
4522 }
4523 return bChgd;
4524 }
4525
UnProtectTables(const SwPaM & rPam)4526 void SwDoc::UnProtectTables( const SwPaM& rPam )
4527 {
4528 GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
4529
4530 bool bChgd = false, bHasSel = rPam.HasMark() ||
4531 rPam.GetNext() != &rPam;
4532 sw::TableFrameFormats& rFormats = *GetTableFrameFormats();
4533 SwTable* pTable;
4534 const SwTableNode* pTableNd;
4535 for( auto n = rFormats.size(); n ; )
4536 if( nullptr != (pTable = SwTable::FindTable( rFormats[ --n ])) &&
4537 nullptr != (pTableNd = pTable->GetTableNode() ) &&
4538 pTableNd->GetNodes().IsDocNodes() )
4539 {
4540 SwNodeOffset nTableIdx = pTableNd->GetIndex();
4541
4542 // Check whether the Table is within the Selection
4543 if( bHasSel )
4544 {
4545 bool bFound = false;
4546 SwPaM* pTmp = const_cast<SwPaM*>(&rPam);
4547 do {
4548 auto [pStt, pEnd] = pTmp->StartEnd(); // SwPosition*
4549 bFound = pStt->GetNodeIndex() < nTableIdx &&
4550 nTableIdx < pEnd->GetNodeIndex();
4551
4552 } while( !bFound && &rPam != ( pTmp = pTmp->GetNext() ) );
4553 if( !bFound )
4554 continue; // Continue searching
4555 }
4556
4557 // Lift the protection
4558 bChgd |= UnProtectTableCells( *pTable );
4559 }
4560
4561 GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
4562 if( bChgd )
4563 getIDocumentState().SetModified();
4564 }
4565
HasTableAnyProtection(const SwPosition * pPos,const OUString * pTableName,bool * pFullTableProtection)4566 bool SwDoc::HasTableAnyProtection( const SwPosition* pPos,
4567 const OUString* pTableName,
4568 bool* pFullTableProtection )
4569 {
4570 bool bHasProtection = false;
4571 SwTable* pTable = nullptr;
4572 if( pTableName )
4573 pTable = SwTable::FindTable( FindTableFormatByName( *pTableName ) );
4574 else if( pPos )
4575 {
4576 SwTableNode* pTableNd = pPos->GetNode().FindTableNode();
4577 if( pTableNd )
4578 pTable = &pTableNd->GetTable();
4579 }
4580
4581 if( pTable )
4582 {
4583 SwTableSortBoxes& rSrtBox = pTable->GetTabSortBoxes();
4584 for (size_t i = rSrtBox.size(); i; )
4585 {
4586 SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
4587 if( pBoxFormat->GetProtect().IsContentProtected() )
4588 {
4589 if( !bHasProtection )
4590 {
4591 bHasProtection = true;
4592 if( !pFullTableProtection )
4593 break;
4594 *pFullTableProtection = true;
4595 }
4596 }
4597 else if( bHasProtection && pFullTableProtection )
4598 {
4599 *pFullTableProtection = false;
4600 break;
4601 }
4602 }
4603 }
4604 return bHasProtection;
4605 }
4606
MakeTableStyle(const OUString & rName,bool bBroadcast)4607 SwTableAutoFormat* SwDoc::MakeTableStyle(const OUString& rName, bool bBroadcast)
4608 {
4609 SwTableAutoFormat aTableFormat(rName);
4610 GetTableStyles().AddAutoFormat(aTableFormat);
4611 SwTableAutoFormat* pTableFormat = GetTableStyles().FindAutoFormat(rName);
4612
4613 getIDocumentState().SetModified();
4614
4615 if (GetIDocumentUndoRedo().DoesUndo())
4616 {
4617 GetIDocumentUndoRedo().AppendUndo(
4618 std::make_unique<SwUndoTableStyleMake>(rName, *this));
4619 }
4620
4621 if (bBroadcast)
4622 BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetCreated);
4623
4624 return pTableFormat;
4625 }
4626
DelTableStyle(const OUString & rName,bool bBroadcast)4627 std::unique_ptr<SwTableAutoFormat> SwDoc::DelTableStyle(const OUString& rName, bool bBroadcast)
4628 {
4629 if (bBroadcast)
4630 BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetErased);
4631
4632 std::unique_ptr<SwTableAutoFormat> pReleasedFormat = GetTableStyles().ReleaseAutoFormat(rName);
4633
4634 std::vector<SwTable*> vAffectedTables;
4635 if (pReleasedFormat)
4636 {
4637 size_t nTableCount = GetTableFrameFormatCount(true);
4638 for (size_t i=0; i < nTableCount; ++i)
4639 {
4640 SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
4641 SwTable* pTable = SwTable::FindTable(pFrameFormat);
4642 if (pTable->GetTableStyleName() == pReleasedFormat->GetName())
4643 {
4644 pTable->SetTableStyleName(u""_ustr);
4645 vAffectedTables.push_back(pTable);
4646 }
4647 }
4648
4649 getIDocumentState().SetModified();
4650
4651 if (GetIDocumentUndoRedo().DoesUndo())
4652 {
4653 GetIDocumentUndoRedo().AppendUndo(
4654 std::make_unique<SwUndoTableStyleDelete>(std::move(pReleasedFormat), std::move(vAffectedTables), *this));
4655 }
4656 }
4657
4658 return pReleasedFormat;
4659 }
4660
ChgTableStyle(const OUString & rName,const SwTableAutoFormat & rNewFormat)4661 void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
4662 {
4663 SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
4664 if (!pFormat)
4665 return;
4666
4667 SwTableAutoFormat aOldFormat = *pFormat;
4668 *pFormat = rNewFormat;
4669 pFormat->SetName(rName);
4670
4671 size_t nTableCount = GetTableFrameFormatCount(true);
4672 for (size_t i=0; i < nTableCount; ++i)
4673 {
4674 SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
4675 SwTable* pTable = SwTable::FindTable(pFrameFormat);
4676 if (pTable->GetTableStyleName() == rName)
4677 if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
4678 pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
4679 }
4680
4681 getIDocumentState().SetModified();
4682
4683 if (GetIDocumentUndoRedo().DoesUndo())
4684 {
4685 GetIDocumentUndoRedo().AppendUndo(
4686 std::make_unique<SwUndoTableStyleUpdate>(*pFormat, aOldFormat, *this));
4687 }
4688 }
4689
4690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4691