xref: /core/sw/source/core/docnode/ndtbl.cxx (revision 12e48f91)
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