resolves tdf#164985 Incorrect bookmarks list in bookmark dialog after
[LibreOffice.git] / sw / source / core / table / swtable.cxx
bloba7e4359d1a43cc89ec82d230fe4b9516090f7f57
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <libxml/xmlwriter.h>
22 #include <hintids.hxx>
23 #include <hints.hxx>
24 #include <editeng/lrspitem.hxx>
25 #include <editeng/shaditem.hxx>
26 #include <editeng/adjustitem.hxx>
27 #include <editeng/colritem.hxx>
28 #include <osl/diagnose.h>
29 #include <sfx2/linkmgr.hxx>
30 #include <fmtfsize.hxx>
31 #include <fmtornt.hxx>
32 #include <fmtpdsc.hxx>
33 #include <fldbas.hxx>
34 #include <fmtfld.hxx>
35 #include <frmatr.hxx>
36 #include <doc.hxx>
37 #include <IDocumentLinksAdministration.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <IDocumentFieldsAccess.hxx>
40 #include <docary.hxx>
41 #include <frame.hxx>
42 #include <swtable.hxx>
43 #include <ndtxt.hxx>
44 #include <tabcol.hxx>
45 #include <tabfrm.hxx>
46 #include <cellfrm.hxx>
47 #include <rowfrm.hxx>
48 #include <swserv.hxx>
49 #include <expfld.hxx>
50 #include <mdiexp.hxx>
51 #include <cellatr.hxx>
52 #include <txatbase.hxx>
53 #include <htmltbl.hxx>
54 #include <swtblfmt.hxx>
55 #include <ndindex.hxx>
56 #include <tblrwcl.hxx>
57 #include <shellres.hxx>
58 #include <viewsh.hxx>
59 #include <redline.hxx>
60 #include <vector>
61 #include <calbck.hxx>
62 #include <o3tl/string_view.hxx>
63 #include <svl/numformat.hxx>
64 #include <txtfld.hxx>
65 #include <rolbck.hxx>
67 #ifdef DBG_UTIL
68 #define CHECK_TABLE(t) (t).CheckConsistency();
69 #else
70 #define CHECK_TABLE(t)
71 #endif
73 using namespace com::sun::star;
76 #define COLFUZZY 20
78 static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
79 bool bChgAlign, SwNodeOffset nNdPos );
81 void SwTableBox::setRowSpan( sal_Int32 nNewRowSpan )
83 mnRowSpan = nNewRowSpan;
86 bool SwTableBox::getDummyFlag() const
88 return mbDummyFlag;
91 void SwTableBox::setDummyFlag( bool bDummy )
93 mbDummyFlag = bDummy;
96 //JP 15.09.98: Bug 55741 - Keep tabs (front and rear)
97 static void lcl_TabToBlankAtSttEnd( OUString& rText )
99 sal_Unicode c;
100 sal_Int32 n;
102 for( n = 0; n < rText.getLength() && ' ' >= ( c = rText[n] ); ++n )
103 if( '\x9' == c )
104 rText = rText.replaceAt( n, 1, u" " );
105 for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); )
106 if( '\x9' == c )
107 rText = rText.replaceAt( n, 1, u" " );
110 static void lcl_DelTabsAtSttEnd( OUString& rText )
112 sal_Unicode c;
113 sal_Int32 n;
114 OUStringBuffer sBuff(rText);
116 for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n )
118 if( '\x9' == c )
119 sBuff.remove( n--, 1 );
121 for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); )
123 if( '\x9' == c )
124 sBuff.remove( n, 1 );
126 rText = sBuff.makeStringAndClear();
129 void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd,
130 SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat,
131 SwTableBox* pBox,
132 sal_uInt16 nInsPos, sal_uInt16 nCnt )
134 OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" );
135 SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
136 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
137 if( !pCNd )
138 pCNd = SwNodes::GoNext(&aIdx);
139 assert(pCNd && "Box with no content node");
141 if( pCNd->IsTextNode() )
143 if( pCNd->GetpSwAttrSet() )
145 SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() );
146 if(pCNd->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT))
148 SwFormatAutoFormat format = aAttrSet.Get(RES_PARATR_LIST_AUTOFMT);
149 const std::shared_ptr<SfxItemSet>& handle = format.GetStyleHandle();
150 aAttrSet.Put(*handle);
152 if( pBox->GetSaveNumFormatColor() )
154 if( pBox->GetSaveUserColor() )
155 aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR ));
156 else
157 aAttrSet.ClearItem( RES_CHRATR_COLOR );
159 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
160 static_cast<SwTextNode*>(pCNd)->GetTextColl(),
161 &aAttrSet, nInsPos, nCnt );
163 else
164 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
165 static_cast<SwTextNode*>(pCNd)->GetTextColl(),
166 pCNd->GetpSwAttrSet(), nInsPos, nCnt );
168 else
169 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
170 rDoc.GetDfltTextFormatColl(), nullptr,
171 nInsPos, nCnt );
173 sal_Int32 nRowSpan = pBox->getRowSpan();
174 if( nRowSpan != 1 )
176 SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
177 for( sal_uInt16 i = 0; i < nCnt; ++i )
179 pBox = rTableBoxes[ i + nInsPos ];
180 pBox->setRowSpan( nRowSpan );
185 SwTable::SwTable()
186 : SwClient( nullptr ),
187 m_pTableNode( nullptr ),
188 m_nGraphicsThatResize( 0 ),
189 m_nRowsToRepeat( 1 ),
190 m_bModifyLocked( false ),
191 m_bNewModel( true )
193 // default value set in the options
194 m_eTableChgMode = GetTableChgDefaultMode();
197 SwTable::SwTable( const SwTable& rTable )
198 : SwClient( rTable.GetFrameFormat() ),
199 m_pTableNode( nullptr ),
200 m_eTableChgMode( rTable.m_eTableChgMode ),
201 m_nGraphicsThatResize( 0 ),
202 m_nRowsToRepeat( rTable.GetRowsToRepeat() ),
203 maTableStyleName(rTable.maTableStyleName),
204 m_bModifyLocked( false ),
205 m_bNewModel( rTable.m_bNewModel )
209 void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes )
211 for (size_t n = 0; n < rSortCntBoxes.size(); ++n)
213 rSortCntBoxes[ n ]->m_pStartNode = nullptr;
217 SwTable::~SwTable()
219 if( m_xRefObj.is() )
221 SwDoc* pDoc = GetFrameFormat()->GetDoc();
222 if( !pDoc->IsInDtor() ) // then remove from the list
223 pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() );
225 m_xRefObj->Closed();
228 // the table can be deleted if it's the last client of the FrameFormat
229 SwTableFormat* pFormat = GetFrameFormat();
230 pFormat->Remove(*this); // remove
232 if( !pFormat->HasWriterListeners() )
233 pFormat->GetDoc()->DelTableFrameFormat( pFormat ); // and delete
235 // Delete the pointers from the SortArray of the boxes. The objects
236 // are preserved and are deleted by the lines/boxes arrays dtor.
237 // Note: unfortunately not enough, pointers to the StartNode of the
238 // section need deletion.
239 DelBoxNode(m_TabSortContentBoxes);
240 m_TabSortContentBoxes.clear();
243 namespace
246 template<class T>
247 T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD)
249 assert(nD != 0);
250 return nD == 0 ? static_cast<T>(nA*nM) : static_cast<T>((nA*nM)/nD);
255 static void FormatInArr( std::vector<SwFormat*>& rFormatArr, SwFormat* pBoxFormat )
257 std::vector<SwFormat*>::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat );
258 if ( it == rFormatArr.end() )
259 rFormatArr.push_back( pBoxFormat );
262 static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
263 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr );
265 static void lcl_ModifyLines( SwTableLines &rLines, const tools::Long nOld,
266 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr, const bool bCheckSum )
268 for ( auto &rLine : rLines)
269 ::lcl_ModifyBoxes( rLine->GetTabBoxes(), nOld, nNew, rFormatArr );
270 if( bCheckSum )
272 for(SwFormat* pFormat : rFormatArr)
274 const SwTwips nBox = lcl_MulDiv64<SwTwips>(pFormat->GetFrameSize().GetWidth(), nNew, nOld);
275 SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 );
276 pFormat->LockModify();
277 pFormat->SetFormatAttr( aNewBox );
278 pFormat->UnlockModify();
283 static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
284 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr )
286 sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths
287 sal_uInt64 nOriginalSum = 0; // Sum of original widths
288 for ( size_t i = 0; i < rBoxes.size(); ++i )
290 SwTableBox &rBox = *rBoxes[i];
291 if ( !rBox.GetTabLines().empty() )
293 // For SubTables the rounding problem will not be solved :-(
294 ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false );
296 // Adjust the box
297 SwFrameFormat *pFormat = rBox.GetFrameFormat();
298 sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth();
299 nOriginalSum += nBox;
300 nBox = lcl_MulDiv64<sal_uInt64>(nBox, nNew, nOld);
301 const sal_uInt64 nWishedSum = lcl_MulDiv64<sal_uInt64>(nOriginalSum, nNew, nOld) - nSum;
302 if( nWishedSum > 0 )
304 if( nBox == nWishedSum )
305 FormatInArr( rFormatArr, pFormat );
306 else
308 nBox = nWishedSum;
309 pFormat = rBox.ClaimFrameFormat();
310 SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 );
311 pFormat->LockModify();
312 pFormat->SetFormatAttr( aNewBox );
313 pFormat->UnlockModify();
316 else {
317 OSL_FAIL( "Rounding error" );
319 nSum += nBox;
323 void SwTable::SwClientNotify(const SwModify&, const SfxHint& rHint)
325 if(rHint.GetId() == SfxHintId::SwAutoFormatUsedHint)
327 auto& rAutoFormatUsedHint = static_cast<const sw::AutoFormatUsedHint&>(rHint);
328 rAutoFormatUsedHint.CheckNode(GetTableNode());
330 else if (rHint.GetId() == SfxHintId::SwAttrSetChange)
332 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
333 // catch SSize changes, to adjust the lines/boxes
334 const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
335 if (pChangeHint->m_pOld && pChangeHint->m_pNew
336 && (pNewSize = pChangeHint->m_pNew->GetChgSet()->GetItemIfSet(
337 RES_FRM_SIZE,
338 false)))
340 pOldSize = &pChangeHint->m_pOld->GetChgSet()->GetFrameSize();
342 if (pOldSize && pNewSize && !m_bModifyLocked)
343 AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
345 else if (rHint.GetId() == SfxHintId::SwObjectDying)
347 auto pDyingHint = static_cast<const sw::ObjectDyingHint*>(&rHint);
348 CheckRegistration( *pDyingHint );
350 else if (rHint.GetId() == SfxHintId::SwLegacyModify)
352 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
353 // catch SSize changes, to adjust the lines/boxes
354 const sal_uInt16 nWhich = pLegacy->GetWhich();
355 if (nWhich == RES_FRM_SIZE)
357 const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
358 pOldSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pOld);
359 pNewSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew);
360 if (pOldSize && pNewSize && !m_bModifyLocked)
361 AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
366 void SwTable::AdjustWidths( const tools::Long nOld, const tools::Long nNew )
368 std::vector<SwFormat*> aFormatArr;
369 aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() );
370 ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true );
373 static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos )
375 for ( size_t i = 0; i < rToFill.Count(); ++i )
377 if ( std::abs(static_cast<tools::Long>(nPos) - rToFill[i]) <= COLFUZZY )
379 rToFill.SetHidden( i, false );
380 break;
385 static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox,
386 const SwFrameFormat *pTabFormat, const bool bHidden,
387 const bool bRefreshHidden )
389 const tools::Long nWish = pTabFormat->GetFrameSize().GetWidth();
390 OSL_ENSURE(nWish, "weird <= 0 width frmfrm");
392 // The value for the left edge of the box is calculated from the
393 // widths of the previous boxes.
394 tools::Long nPos = 0;
395 tools::Long nLeftMin = 0;
396 tools::Long nRightMax = 0;
397 if (nWish != 0) //fdo#33012 0 width frmfmt
399 SwTwips nSum = 0;
400 const SwTableBox *pCur = pBox;
401 const SwTableLine *pLine = pBox->GetUpper();
402 const tools::Long nAct = rToFill.GetRight() - rToFill.GetLeft(); // +1 why?
404 while ( pLine )
406 const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
407 for ( size_t i = 0; i < rBoxes.size(); ++i )
409 const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
410 nSum += nWidth;
411 const tools::Long nTmp = lcl_MulDiv64<tools::Long>(nSum, nAct, nWish);
413 if (rBoxes[i] != pCur)
415 if ( pLine == pBox->GetUpper() || 0 == nLeftMin )
416 nLeftMin = nTmp - nPos;
417 nPos = nTmp;
419 else
421 nSum -= nWidth;
422 if ( 0 == nRightMax )
423 nRightMax = nTmp - nPos;
424 break;
427 pCur = pLine->GetUpper();
428 pLine = pCur ? pCur->GetUpper() : nullptr;
432 bool bInsert = !bRefreshHidden;
433 for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j )
435 tools::Long nCmp = rToFill[j];
436 if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
437 (nPos <= (nCmp + COLFUZZY)) )
439 bInsert = false; // Already has it.
441 else if ( nPos < nCmp )
443 bInsert = false;
444 rToFill.Insert( nPos, bHidden, j );
447 if ( bInsert )
448 rToFill.Insert( nPos, bHidden, rToFill.Count() );
449 else if ( bRefreshHidden )
450 ::lcl_RefreshHidden( rToFill, nPos );
452 if ( !bHidden || bRefreshHidden )
453 return;
455 // calculate minimum/maximum values for the existing entries:
456 nLeftMin = nPos - nLeftMin;
457 nRightMax = nPos + nRightMax;
459 // check if nPos is entry:
460 bool bFoundPos = false;
461 bool bFoundMax = false;
462 for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j )
464 SwTabColsEntry& rEntry = rToFill.GetEntry( j );
465 tools::Long nCmp = rToFill[j];
467 if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
468 (nPos <= (nCmp + COLFUZZY)) )
470 // check if nLeftMin is > old minimum for entry nPos:
471 const tools::Long nOldMin = rEntry.nMin;
472 if ( nLeftMin > nOldMin )
473 rEntry.nMin = nLeftMin;
474 // check if nRightMin is < old maximum for entry nPos:
475 const tools::Long nOldMax = rEntry.nMax;
476 if ( nRightMax < nOldMax )
477 rEntry.nMax = nRightMax;
479 bFoundPos = true;
481 else if ( (nRightMax >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
482 (nRightMax <= (nCmp + COLFUZZY)) )
484 // check if nPos is > old minimum for entry nRightMax:
485 const tools::Long nOldMin = rEntry.nMin;
486 if ( nPos > nOldMin )
487 rEntry.nMin = nPos;
489 bFoundMax = true;
494 static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill,
495 const SwFrameFormat *pTabFormat, bool bRefreshHidden )
497 if ( !pBox->GetTabLines().empty() )
499 const SwTableLines &rLines = pBox->GetTabLines();
500 for ( size_t i = 0; i < rLines.size(); ++i )
502 const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
503 for ( size_t j = 0; j < rBoxes.size(); ++j )
504 ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden);
507 else
508 ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden );
511 static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill,
512 const SwFrameFormat *pTabFormat )
514 for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i )
516 const SwTableBox *pBox = pLine->GetTabBoxes()[i];
517 if ( pBox->GetSttNd() )
518 ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, true, false );
519 else
520 for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j )
521 ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat );
525 void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart,
526 bool bRefreshHidden, bool bCurRowOnly ) const
528 // Optimization: if bHidden is set, we only update the Hidden Array.
529 if ( bRefreshHidden )
531 // remove corrections
532 for ( size_t i = 0; i < rToFill.Count(); ++i )
534 SwTabColsEntry& rEntry = rToFill.GetEntry( i );
535 rEntry.nPos -= rToFill.GetLeft();
536 rEntry.nMin -= rToFill.GetLeft();
537 rEntry.nMax -= rToFill.GetLeft();
540 // All are hidden, so add the visible ones.
541 for ( size_t i = 0; i < rToFill.Count(); ++i )
542 rToFill.SetHidden( i, true );
544 else
546 rToFill.Remove( 0, rToFill.Count() );
549 // Insertion cases:
550 // 1. All boxes which are inferior to Line which is superior to the Start,
551 // as well as their inferior boxes if present.
552 // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors.
553 // 3. Apply 2. to the Line superior to the chain of boxes,
554 // until the Line's superior is not a box but the table.
555 // Only those boxes are inserted that don't contain further rows. The insertion
556 // function takes care to avoid duplicates. In order to achieve this, we work
557 // with some degree of fuzzyness (to avoid rounding errors).
558 // Only the left edge of the boxes are inserted.
559 // Finally, the first entry is removed again, because it's already
560 // covered by the border.
561 // 4. Scan the table again and insert _all_ boxes, this time as hidden.
563 const SwFrameFormat *pTabFormat = GetFrameFormat();
565 // 1.
566 const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
568 for ( size_t i = 0; i < rBoxes.size(); ++i )
569 ::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden );
571 // 2. and 3.
572 const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
573 pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
574 while ( pLine )
576 const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
577 for ( size_t k = 0; k < rBoxes2.size(); ++k )
578 ::lcl_SortedTabColInsert( rToFill, rBoxes2[k],
579 pTabFormat, false, bRefreshHidden );
580 pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
583 if ( !bRefreshHidden )
585 // 4.
586 if ( !bCurRowOnly )
588 for ( size_t i = 0; i < m_aLines.size(); ++i )
589 ::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat );
592 rToFill.Remove( 0 );
595 // Now the coordinates are relative to the left table border - i.e.
596 // relative to SwTabCols.nLeft. However, they are expected
597 // relative to the left document border, i.e. SwTabCols.nLeftMin.
598 // So all values need to be extended by nLeft.
599 for ( size_t i = 0; i < rToFill.Count(); ++i )
601 SwTabColsEntry& rEntry = rToFill.GetEntry( i );
602 rEntry.nPos += rToFill.GetLeft();
603 rEntry.nMin += rToFill.GetLeft();
604 rEntry.nMax += rToFill.GetLeft();
608 // Structure for parameter passing
609 struct Parm
611 const SwTabCols &rNew;
612 const SwTabCols &rOld;
613 tools::Long nNewWish,
614 nOldWish;
615 std::deque<SwTableBox*> aBoxArr;
616 SwShareBoxFormats aShareFormats;
618 Parm( const SwTabCols &rN, const SwTabCols &rO )
619 : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0)
623 static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm );
625 static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm )
627 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
628 for ( size_t i = rBoxes.size(); i > 0; )
630 --i;
631 ::lcl_ProcessBoxSet( rBoxes[i], rParm );
635 static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm )
637 if ( !pBox->GetTabLines().empty() )
639 SwTableLines &rLines = pBox->GetTabLines();
640 for ( size_t i = rLines.size(); i > 0; )
642 --i;
643 lcl_ProcessLine( rLines[i], rParm );
646 else
648 // Search the old TabCols for the current position (calculate from
649 // left and right edge). Adjust the box if the values differ from
650 // the new TabCols. If the adjusted edge has no neighbour we also
651 // adjust all superior boxes.
653 const tools::Long nOldAct = rParm.rOld.GetRight() -
654 rParm.rOld.GetLeft(); // +1 why?
656 // The value for the left edge of the box is calculated from the
657 // widths of the previous boxes plus the left edge.
658 tools::Long nLeft = rParm.rOld.GetLeft();
659 const SwTableBox *pCur = pBox;
660 const SwTableLine *pLine = pBox->GetUpper();
662 while ( pLine )
664 const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
665 for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i)
667 nLeft += lcl_MulDiv64<tools::Long>(
668 rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(),
669 nOldAct, rParm.nOldWish);
671 pCur = pLine->GetUpper();
672 pLine = pCur ? pCur->GetUpper() : nullptr;
674 tools::Long nLeftDiff = 0;
675 tools::Long nRightDiff = 0;
676 if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this.
678 // Right edge is left edge plus width.
679 const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
680 pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
681 nOldAct, rParm.nOldWish);
682 const tools::Long nRight = nLeft + nWidth;
683 size_t nLeftPos = 0;
684 size_t nRightPos = 0;
685 bool bFoundLeftPos = false;
686 bool bFoundRightPos = false;
687 for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
689 if ( nLeft >= (rParm.rOld[i] - COLFUZZY) &&
690 nLeft <= (rParm.rOld[i] + COLFUZZY) )
692 nLeftPos = i;
693 bFoundLeftPos = true;
695 else if ( nRight >= (rParm.rOld[i] - COLFUZZY) &&
696 nRight <= (rParm.rOld[i] + COLFUZZY) )
698 nRightPos = i;
699 bFoundRightPos = true;
702 nLeftDiff = bFoundLeftPos ?
703 rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0;
704 nRightDiff= bFoundRightPos ?
705 rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0;
707 else // The first box.
709 nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft();
710 if ( rParm.rOld.Count() )
712 // Calculate the difference to the edge touching the first box.
713 const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
714 pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
715 nOldAct, rParm.nOldWish);
716 const tools::Long nTmp = nWidth + rParm.rOld.GetLeft();
717 for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
719 if ( nTmp >= (rParm.rOld[i] - COLFUZZY) &&
720 nTmp <= (rParm.rOld[i] + COLFUZZY) )
722 nRightDiff = rParm.rNew[i] - rParm.rOld[i];
723 break;
729 if( pBox->getRowSpan() == 1 )
731 const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox );
732 SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes();
733 if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 )
734 nLeftDiff = 0;
735 if( nPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) &&
736 rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 )
737 nRightDiff = 0;
739 else
740 nLeftDiff = nRightDiff = 0;
742 if ( nLeftDiff || nRightDiff )
744 // The difference is the actual difference amount. For stretched
745 // tables, it does not make sense to adjust the attributes of the
746 // boxes by this amount. The difference amount needs to be converted
747 // accordingly.
748 tools::Long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why?
749 nLeftDiff *= rParm.nNewWish;
750 nLeftDiff /= nTmp;
751 nRightDiff *= rParm.nNewWish;
752 nRightDiff /= nTmp;
753 tools::Long nDiff = nLeftDiff + nRightDiff;
755 // Adjust the box and all superiors by the difference amount.
756 while ( pBox )
758 SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
759 aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
760 if ( aFormatFrameSize.GetWidth() < 0 )
761 aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() );
762 rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
764 // The outer cells of the last row are responsible to adjust a surrounding cell.
765 // Last line check:
766 if ( pBox->GetUpper()->GetUpper() &&
767 pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back())
769 pBox = nullptr;
771 else
773 // Middle cell check:
774 if ( pBox != pBox->GetUpper()->GetTabBoxes().front() )
775 nDiff = nRightDiff;
777 if ( pBox != pBox->GetUpper()->GetTabBoxes().back() )
778 nDiff -= nRightDiff;
780 pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr;
787 static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque<SwTableBox*> &rBoxArr,
788 bool bBefore )
790 if ( !pBox->GetTabLines().empty() )
792 const SwTableLines &rLines = pBox->GetTabLines();
793 for ( size_t i = 0; i < rLines.size(); ++i )
795 const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
796 for ( size_t j = 0; j < rBoxes.size(); ++j )
797 ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore );
800 else if ( bBefore )
801 rBoxArr.push_front( pBox );
802 else
803 rBoxArr.push_back( pBox );
806 static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm );
808 static void lcl_AdjustLines( SwTableLines &rLines, const tools::Long nDiff, Parm &rParm )
810 for ( size_t i = 0; i < rLines.size(); ++i )
812 SwTableBox *pBox = rLines[i]->GetTabBoxes()
813 [rLines[i]->GetTabBoxes().size()-1];
814 lcl_AdjustBox( pBox, nDiff, rParm );
818 static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm )
820 if ( !pBox->GetTabLines().empty() )
821 ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm );
823 // Adjust the size of the box.
824 SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
825 aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
827 rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
830 void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld,
831 const SwTableBox *pStart, bool bCurRowOnly )
833 CHECK_TABLE( *this )
835 SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // delete HTML-Layout
837 // FME: Made rOld const. The caller is responsible for passing correct
838 // values of rOld. Therefore we do not have to call GetTabCols anymore:
839 //GetTabCols( rOld, pStart );
841 Parm aParm( rNew, rOld );
843 OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.");
845 // Convert the edges. We need to adjust the size of the table and some boxes.
846 // For the size adjustment, we must not make use of the Modify, since that'd
847 // adjust all boxes, which we really don't want.
848 SwFrameFormat *pFormat = GetFrameFormat();
849 aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth();
850 if ( (rOld.GetLeft() != rNew.GetLeft()) ||
851 (rOld.GetRight()!= rNew.GetRight()) )
853 LockModify();
855 SvxLRSpaceItem aLR( pFormat->GetLRSpace() );
856 SvxShadowItem aSh( pFormat->GetShadow() );
858 SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT );
859 SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT );
861 aLR.SetLeft(SvxIndentValue::twips(rNew.GetLeft() - nShLeft));
862 aLR.SetRight(SvxIndentValue::twips(rNew.GetRightMax() - rNew.GetRight() - nShRight));
863 pFormat->SetFormatAttr( aLR );
865 // The alignment of the table needs to be adjusted accordingly.
866 // This is done by preserving the exact positions that have been
867 // set by the user.
868 SwFormatHoriOrient aOri( pFormat->GetHoriOrient() );
869 if( text::HoriOrientation::NONE != aOri.GetHoriOrient() &&
870 text::HoriOrientation::CENTER != aOri.GetHoriOrient() )
872 const bool bLeftDist = rNew.GetLeft() != nShLeft;
873 const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax();
874 if(!bLeftDist && !bRightDist)
875 aOri.SetHoriOrient( text::HoriOrientation::FULL );
876 else if(!bRightDist && rNew.GetLeft() > nShLeft )
877 aOri.SetHoriOrient( text::HoriOrientation::RIGHT );
878 else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax())
879 aOri.SetHoriOrient( text::HoriOrientation::LEFT );
880 else
882 // if an automatic table hasn't (really) changed size, then leave it as auto.
883 const tools::Long nOldWidth = rOld.GetRight() - rOld.GetLeft();
884 const tools::Long nNewWidth = rNew.GetRight() - rNew.GetLeft();
885 if (aOri.GetHoriOrient() != text::HoriOrientation::FULL
886 || std::abs(nOldWidth - nNewWidth) > COLFUZZY)
888 aOri.SetHoriOrient(text::HoriOrientation::LEFT_AND_WIDTH);
892 pFormat->SetFormatAttr( aOri );
894 const tools::Long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why?
895 tools::Long nTabDiff = 0;
897 if ( rOld.GetLeft() != rNew.GetLeft() )
899 nTabDiff = rOld.GetLeft() - rNew.GetLeft();
900 nTabDiff *= aParm.nOldWish;
901 nTabDiff /= nAct;
903 if ( rOld.GetRight() != rNew.GetRight() )
905 tools::Long nDiff = rNew.GetRight() - rOld.GetRight();
906 nDiff *= aParm.nOldWish;
907 nDiff /= nAct;
908 nTabDiff += nDiff;
909 if( !IsNewModel() )
910 ::lcl_AdjustLines( GetTabLines(), nDiff, aParm );
913 // Adjust the size of the table, watch out for stretched tables.
914 if ( nTabDiff )
916 aParm.nNewWish += nTabDiff;
917 if ( aParm.nNewWish < 0 )
918 aParm.nNewWish = USHRT_MAX; // Oops! Have to roll back.
919 SwFormatFrameSize aSz( pFormat->GetFrameSize() );
920 if ( aSz.GetWidth() != aParm.nNewWish )
922 aSz.SetWidth( aParm.nNewWish );
923 aSz.SetWidthPercent( 0 );
924 pFormat->SetFormatAttr( aSz );
927 UnlockModify();
930 if( IsNewModel() )
931 NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly );
932 else
934 if ( bCurRowOnly )
936 // To adjust the current row, we need to process all its boxes,
937 // similar to the filling of the TabCols (see GetTabCols()).
938 // Unfortunately we again have to take care to adjust the boxes
939 // from back to front, respectively from outer to inner.
940 // The best way to achieve this is probably to track the boxes
941 // in a PtrArray.
942 const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
943 for ( size_t i = 0; i < rBoxes.size(); ++i )
944 ::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false );
946 const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
947 pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
948 const SwTableBox *pExcl = pStart->GetUpper()->GetUpper();
949 while ( pLine )
951 const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
952 bool bBefore = true;
953 for ( size_t i = 0; i < rBoxes2.size(); ++i )
955 if ( rBoxes2[i] != pExcl )
956 ::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore );
957 else
958 bBefore = false;
960 pExcl = pLine->GetUpper();
961 pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
963 // After we've inserted a bunch of boxes (hopefully all and in
964 // correct order), we just need to process them in reverse order.
965 for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j )
967 SwTableBox *pBox = aParm.aBoxArr[j];
968 ::lcl_ProcessBoxSet( pBox, aParm );
971 else
973 // Adjusting the entire table is 'easy'. All boxes without lines are
974 // adjusted, as are their superiors. Of course we need to process
975 // in reverse order to prevent fooling ourselves!
976 SwTableLines &rLines = GetTabLines();
977 for ( size_t i = rLines.size(); i > 0; )
979 --i;
980 ::lcl_ProcessLine( rLines[i], aParm );
985 #ifdef DBG_UTIL
986 CheckBoxWidth(GetTabLines(), *GetFrameFormat());
987 #endif
990 typedef std::pair<sal_uInt16, sal_uInt16> ColChange;
991 typedef std::list< ColChange > ChangeList;
993 static void lcl_AdjustWidthsInLine( SwTableLine* pLine, ChangeList& rOldNew,
994 Parm& rParm, sal_uInt16 nColFuzzy )
996 ChangeList::iterator pCurr = rOldNew.begin();
997 if( pCurr == rOldNew.end() )
998 return;
999 const size_t nCount = pLine->GetTabBoxes().size();
1000 SwTwips nBorder = 0;
1001 SwTwips nRest = 0;
1002 for( size_t i = 0; i < nCount; ++i )
1004 SwTableBox* pBox = pLine->GetTabBoxes()[i];
1005 SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1006 SwTwips nNewWidth = nWidth - nRest;
1007 nRest = 0;
1008 nBorder += nWidth;
1009 if( pCurr != rOldNew.end() && nBorder + nColFuzzy >= pCurr->first )
1011 nBorder -= nColFuzzy;
1012 while( pCurr != rOldNew.end() && nBorder > pCurr->first )
1013 ++pCurr;
1014 if( pCurr != rOldNew.end() )
1016 nBorder += nColFuzzy;
1017 if( nBorder + nColFuzzy >= pCurr->first )
1019 if( pCurr->second == pCurr->first )
1020 nRest = 0;
1021 else
1022 nRest = pCurr->second - nBorder;
1023 nNewWidth += nRest;
1024 ++pCurr;
1028 if( nNewWidth != nWidth )
1030 if( nNewWidth < 0 )
1032 nRest += 1 - nNewWidth;
1033 nNewWidth = 1;
1035 SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
1036 aFormatFrameSize.SetWidth( nNewWidth );
1037 rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
1042 static void lcl_CalcNewWidths( std::vector<sal_uInt16> &rSpanPos, ChangeList& rChanges,
1043 SwTableLine* pLine, tools::Long nWish, tools::Long nWidth, bool bTop )
1045 if( rChanges.empty() )
1047 rSpanPos.clear();
1048 return;
1050 if( rSpanPos.empty() )
1052 rChanges.clear();
1053 return;
1055 std::vector<sal_uInt16> aNewSpanPos;
1056 ChangeList::iterator pCurr = rChanges.begin();
1057 ChangeList aNewChanges { *pCurr }; // Nullposition
1058 std::vector<sal_uInt16>::iterator pSpan = rSpanPos.begin();
1059 sal_uInt16 nCurr = 0;
1060 SwTwips nOrgSum = 0;
1061 bool bRowSpan = false;
1062 sal_uInt16 nRowSpanCount = 0;
1063 const size_t nCount = pLine->GetTabBoxes().size();
1064 for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
1066 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
1067 SwTwips nCurrWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
1068 const sal_Int32 nRowSpan = pBox->getRowSpan();
1069 const bool bCurrRowSpan = bTop ? nRowSpan < 0 :
1070 ( nRowSpan > 1 || nRowSpan < -1 );
1071 if( bRowSpan || bCurrRowSpan )
1072 aNewSpanPos.push_back( nRowSpanCount );
1073 bRowSpan = bCurrRowSpan;
1074 nOrgSum += nCurrWidth;
1075 const sal_uInt16 nPos = lcl_MulDiv64<sal_uInt16>(
1076 lcl_MulDiv64<sal_uInt64>(nOrgSum, nWidth, nWish),
1077 nWish, nWidth);
1078 while( pCurr != rChanges.end() && pCurr->first < nPos )
1080 ++nCurr;
1081 ++pCurr;
1083 bool bNew = true;
1084 if( pCurr != rChanges.end() && pCurr->first <= nPos &&
1085 pCurr->first != pCurr->second )
1087 pSpan = std::find_if(pSpan, rSpanPos.end(),
1088 [nCurr](const sal_uInt16 nSpan) { return nSpan >= nCurr; });
1089 if( pSpan != rSpanPos.end() && *pSpan == nCurr )
1091 aNewChanges.push_back( *pCurr );
1092 ++nRowSpanCount;
1093 bNew = false;
1096 if( bNew )
1098 ColChange aTmp( nPos, nPos );
1099 aNewChanges.push_back( aTmp );
1100 ++nRowSpanCount;
1104 pCurr = aNewChanges.begin();
1105 ChangeList::iterator pLast = pCurr;
1106 ChangeList::iterator pLeftMove = pCurr;
1107 while( pCurr != aNewChanges.end() )
1109 if( pLeftMove == pCurr )
1111 while( ++pLeftMove != aNewChanges.end() && pLeftMove->first <= pLeftMove->second )
1114 if( pCurr->second == pCurr->first )
1116 if( pLeftMove != aNewChanges.end() && pCurr->second > pLeftMove->second )
1118 if( pLeftMove->first == pLast->first )
1119 pCurr->second = pLeftMove->second;
1120 else
1122 pCurr->second = lcl_MulDiv64<sal_uInt16>(
1123 pCurr->first - pLast->first,
1124 pLeftMove->second - pLast->second,
1125 pLeftMove->first - pLast->first) + pLast->second;
1128 pLast = pCurr;
1129 ++pCurr;
1131 else if( pCurr->second > pCurr->first )
1133 pLast = pCurr;
1134 ++pCurr;
1135 ChangeList::iterator pNext = pCurr;
1136 while( pNext != pLeftMove && pNext->second == pNext->first &&
1137 pNext->second < pLast->second )
1138 ++pNext;
1139 while( pCurr != pNext )
1141 if( pNext == aNewChanges.end() || pNext->first == pLast->first )
1142 pCurr->second = pLast->second;
1143 else
1145 pCurr->second = lcl_MulDiv64<sal_uInt16>(
1146 pCurr->first - pLast->first,
1147 pNext->second - pLast->second,
1148 pNext->first - pLast->first) + pLast->second;
1150 ++pCurr;
1152 pLast = pCurr;
1154 else
1156 pLast = pCurr;
1157 ++pCurr;
1161 rChanges.swap(aNewChanges);
1162 rSpanPos.swap(aNewSpanPos);
1165 void SwTable::NewSetTabCols( Parm &rParm, const SwTabCols &rNew,
1166 const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly )
1168 #if OSL_DEBUG_LEVEL > 1
1169 static int nCallCount = 0;
1170 ++nCallCount;
1171 #endif
1172 // First step: evaluate which lines have been moved/which widths changed
1173 ChangeList aOldNew;
1174 const tools::Long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
1175 const tools::Long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
1176 if( nNewWidth < 1 || nOldWidth < 1 )
1177 return;
1178 for( size_t i = 0; i <= rOld.Count(); ++i )
1180 tools::Long nNewPos;
1181 tools::Long nOldPos;
1182 if( i == rOld.Count() )
1184 nOldPos = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
1185 nNewPos = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
1187 else
1189 nOldPos = rOld[i] - rParm.rOld.GetLeft();
1190 nNewPos = rNew[i] - rParm.rNew.GetLeft();
1192 nNewPos = lcl_MulDiv64<tools::Long>(nNewPos, rParm.nNewWish, nNewWidth);
1193 nOldPos = lcl_MulDiv64<tools::Long>(nOldPos, rParm.nOldWish, nOldWidth);
1194 if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 )
1196 ColChange aChg( o3tl::narrowing<sal_uInt16>(nOldPos), o3tl::narrowing<sal_uInt16>(nNewPos) );
1197 aOldNew.push_back( aChg );
1200 // Finished first step
1201 int nCount = aOldNew.size();
1202 if( !nCount )
1203 return; // no change, nothing to do
1204 SwTableLines &rLines = GetTabLines();
1205 if( bCurRowOnly )
1207 const SwTableLine* pCurrLine = pStart->GetUpper();
1208 sal_uInt16 nCurr = rLines.GetPos( pCurrLine );
1209 if( nCurr >= USHRT_MAX )
1210 return;
1212 ColChange aChg( 0, 0 );
1213 aOldNew.push_front( aChg );
1214 std::vector<sal_uInt16> aRowSpanPos;
1215 if (nCurr > 0)
1217 ChangeList aCopy;
1218 sal_uInt16 nPos = 0;
1219 for( const auto& rCop : aOldNew )
1221 aCopy.push_back( rCop );
1222 aRowSpanPos.push_back( nPos++ );
1224 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
1225 rParm.nOldWish, nOldWidth, true );
1226 sal_uInt16 j = nCurr;
1227 while (!aRowSpanPos.empty() && j > 0)
1229 j = o3tl::sanitizing_dec(j);
1230 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[j],
1231 rParm.nOldWish, nOldWidth, true );
1232 lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
1234 aRowSpanPos.clear();
1236 if( nCurr+1 < o3tl::narrowing<sal_uInt16>(rLines.size()) )
1238 ChangeList aCopy;
1239 sal_uInt16 nPos = 0;
1240 for( const auto& rCop : aOldNew )
1242 aCopy.push_back( rCop );
1243 aRowSpanPos.push_back( nPos++ );
1245 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
1246 rParm.nOldWish, nOldWidth, false );
1247 bool bGoOn = !aRowSpanPos.empty();
1248 sal_uInt16 j = nCurr;
1249 while( bGoOn )
1251 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j],
1252 rParm.nOldWish, nOldWidth, false );
1253 lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
1254 bGoOn = !aRowSpanPos.empty() && j+1 < o3tl::narrowing<sal_uInt16>(rLines.size());
1257 ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY );
1259 else
1261 for( size_t i = 0; i < rLines.size(); ++i )
1262 ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY );
1264 CHECK_TABLE( *this )
1267 // return the pointer of the box specified.
1268 static bool lcl_IsValidRowName( std::u16string_view rStr )
1270 bool bIsValid = true;
1271 size_t nLen = rStr.size();
1272 for( size_t i = 0; i < nLen && bIsValid; ++i )
1274 const sal_Unicode cChar = rStr[i];
1275 if (cChar < '0' || cChar > '9')
1276 bIsValid = false;
1278 return bIsValid;
1281 // #i80314#
1282 // add 3rd parameter and its handling
1283 sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart,
1284 const bool bPerformValidCheck )
1286 sal_uInt16 nRet = 0;
1287 if( bFirstPart ) // true == column; false == row
1289 sal_Int32 nPos = 0;
1290 // the first one uses letters for addressing!
1291 bool bFirst = true;
1292 sal_uInt32 num = 0;
1293 bool overflow = false;
1294 while (nPos<rStr.getLength())
1296 sal_Unicode cChar = rStr[nPos];
1297 if ((cChar<'A' || cChar>'Z') && (cChar<'a' || cChar>'z'))
1298 break;
1299 cChar -= 'A';
1300 if( cChar >= 26 )
1301 cChar -= 'a' - '[';
1302 if( bFirst )
1303 bFirst = false;
1304 else
1305 ++num;
1306 num = num * 52 + cChar;
1307 if (num > SAL_MAX_UINT16) {
1308 overflow = true;
1310 ++nPos;
1312 nRet = overflow ? SAL_MAX_UINT16 : num;
1313 rStr = rStr.copy( nPos ); // Remove char from String
1315 else
1317 const sal_Int32 nPos = rStr.indexOf( "." );
1318 if ( nPos<0 )
1320 nRet = 0;
1321 if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) )
1323 nRet = o3tl::narrowing<sal_uInt16>(rStr.toInt32());
1325 rStr.clear();
1327 else
1329 nRet = 0;
1330 const std::u16string_view aText( rStr.subView( 0, nPos ) );
1331 if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) )
1333 nRet = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(aText));
1335 rStr = rStr.copy( nPos+1 );
1338 return nRet;
1341 // #i80314#
1342 // add 2nd parameter and its handling
1343 const SwTableBox* SwTable::GetTableBox( const OUString& rName,
1344 const bool bPerformValidCheck ) const
1346 const SwTableBox* pBox = nullptr;
1347 const SwTableLine* pLine;
1348 const SwTableLines* pLines;
1350 sal_uInt16 nLine, nBox;
1351 OUString aNm( rName );
1352 while( !aNm.isEmpty() )
1354 nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck );
1355 // first box ?
1356 if( !pBox )
1357 pLines = &GetTabLines();
1358 else
1360 pLines = &pBox->GetTabLines();
1361 if( nBox )
1362 --nBox;
1365 nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck );
1367 // determine line
1368 if( !nLine || nLine > pLines->size() )
1369 return nullptr;
1370 pLine = (*pLines)[ nLine-1 ];
1372 // determine box
1373 const SwTableBoxes* pBoxes = &pLine->GetTabBoxes();
1374 if( nBox >= pBoxes->size() )
1375 return nullptr;
1376 pBox = (*pBoxes)[ nBox ];
1379 // check if the box found has any contents
1380 if( pBox && !pBox->GetSttNd() )
1382 OSL_FAIL( "Box without content, looking for the next one!" );
1383 // "drop this" until the first box
1384 while( !pBox->GetTabLines().empty() )
1385 pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
1387 return pBox;
1390 SwTableBox* SwTable::GetTableBox( SwNodeOffset nSttIdx )
1392 // For optimizations, don't always process the entire SortArray.
1393 // Converting text to table, tries certain conditions
1394 // to ask for a table box of a table that is not yet having a format
1395 if(!GetFrameFormat())
1396 return nullptr;
1397 SwTableBox* pRet = nullptr;
1398 SwNodes& rNds = GetFrameFormat()->GetDoc()->GetNodes();
1399 SwNodeOffset nIndex = nSttIdx + 1;
1400 SwContentNode* pCNd = nullptr;
1401 SwTableNode* pTableNd = nullptr;
1403 while ( nIndex < rNds.Count() )
1405 pTableNd = rNds[ nIndex ]->GetTableNode();
1406 if ( pTableNd )
1407 break;
1409 pCNd = rNds[ nIndex ]->GetContentNode();
1410 if ( pCNd )
1411 break;
1413 ++nIndex;
1416 if ( pCNd || pTableNd )
1418 sw::BroadcastingModify* pModify = pCNd;
1419 // #144862# Better handling of table in table
1420 if ( pTableNd && pTableNd->GetTable().GetFrameFormat() )
1421 pModify = pTableNd->GetTable().GetFrameFormat();
1423 SwFrame* pFrame = pModify ? SwIterator<SwFrame,sw::BroadcastingModify>(*pModify).First() : nullptr;
1424 while ( pFrame && !pFrame->IsCellFrame() )
1425 pFrame = pFrame->GetUpper();
1426 if ( pFrame )
1427 pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1430 // In case the layout doesn't exist yet or anything else goes wrong.
1431 if ( !pRet )
1433 for (size_t n = m_TabSortContentBoxes.size(); n; )
1435 if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx)
1437 return m_TabSortContentBoxes[ n ];
1441 return pRet;
1444 bool SwTable::IsTableComplex() const
1446 // Returns true for complex tables, i.e. tables that contain nestings,
1447 // like containing boxes not part of the first line, e.g. results of
1448 // splits/merges which lead to more complex structures.
1449 for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n)
1451 if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper())
1453 return true;
1456 return false;
1459 SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes,
1460 SwTableBox *pUp )
1461 : SwClient( pFormat )
1462 , m_pUpper( pUp )
1463 , m_eRedlineType( RedlineType::None )
1465 m_aBoxes.reserve( nBoxes );
1468 SwTableLine::~SwTableLine()
1470 for (size_t i = 0; i < m_aBoxes.size(); ++i)
1472 delete m_aBoxes[i];
1474 // the TabelleLine can be deleted if it's the last client of the FrameFormat
1475 sw::BroadcastingModify* pMod = GetFrameFormat();
1476 pMod->Remove(*this); // remove,
1477 if( !pMod->HasWriterListeners() )
1478 delete pMod; // and delete
1481 SwTableLineFormat* SwTableLine::ClaimFrameFormat()
1483 // This method makes sure that this object is an exclusive SwTableLine client
1484 // of an SwTableLineFormat object
1485 // If other SwTableLine objects currently listen to the same SwTableLineFormat as
1486 // this one, something needs to be done
1487 SwTableLineFormat *pRet = GetFrameFormat();
1488 SwIterator<SwTableLine,SwFormat> aIter( *pRet );
1489 for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() )
1491 if ( pLast != this )
1493 // found another SwTableLine that is a client of the current Format
1494 // create a new Format as a copy and use it for this object
1495 SwTableLineFormat *pNewFormat = pRet->GetDoc()->MakeTableLineFormat();
1496 *pNewFormat = *pRet;
1498 // register SwRowFrames that know me as clients at the new Format
1499 SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet );
1500 for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() )
1501 if( pFrame->GetTabLine() == this )
1502 pFrame->RegisterToFormat( *pNewFormat );
1504 // register myself
1505 pNewFormat->Add(*this);
1506 pRet = pNewFormat;
1507 break;
1511 return pRet;
1514 void SwTableLine::ChgFrameFormat(SwTableLineFormat* pNewFormat)
1516 auto pOld = GetFrameFormat();
1517 pOld->CallSwClientNotify(sw::TableLineFormatChanged(*pNewFormat, *this));
1518 // Now, re-register self.
1519 pNewFormat->Add(*this);
1520 if(!pOld->HasWriterListeners())
1521 delete pOld;
1524 SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const
1526 SwTwips nRet = 0;
1527 bLayoutAvailable = false;
1528 SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
1529 // A row could appear several times in headers/footers so only one chain of master/follow tables
1530 // will be accepted...
1531 const SwTabFrame* pChain = nullptr; // My chain
1532 for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
1534 if (pLast->GetTabLine() != this)
1535 continue;
1537 const SwTabFrame* pTab = pLast->FindTabFrame();
1538 if (!pTab)
1539 continue;
1541 bLayoutAvailable = ( pTab->IsVertical() ) ?
1542 ( 0 < pTab->getFrameArea().Height() ) :
1543 ( 0 < pTab->getFrameArea().Width() );
1545 // The first one defines the chain, if a chain is defined, only members of the chain
1546 // will be added.
1547 if (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain))
1549 pChain = pTab; // defines my chain (even it is already)
1550 if( pTab->IsVertical() )
1551 nRet += pLast->getFrameArea().Width();
1552 else
1553 nRet += pLast->getFrameArea().Height();
1554 // Optimization, if there are no master/follows in my chain, nothing more to add
1555 if( !pTab->HasFollow() && !pTab->IsFollow() )
1556 break;
1557 // This is not an optimization, this is necessary to avoid double additions of
1558 // repeating rows
1559 if( pTab->IsInHeadline(*pLast) )
1560 break;
1563 return nRet;
1566 bool SwTableLine::IsEmpty() const
1568 for (size_t i = 0; i < m_aBoxes.size(); ++i)
1570 if ( !m_aBoxes[i]->IsEmpty() )
1571 return false;
1573 return true;
1576 bool SwTable::IsEmpty() const
1578 for (size_t i = 0; i < m_aLines.size(); ++i)
1580 if ( !m_aLines[i]->IsEmpty() )
1581 return false;
1583 return true;
1586 bool SwTable::HasDeletedRowOrCell() const
1588 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1589 if ( aRedlineTable.empty() )
1590 return false;
1592 SwRedlineTable::size_type nRedlinePos = 0;
1593 for (size_t i = 0; i < m_aLines.size(); ++i)
1595 // has a deleted row
1596 if ( m_aLines[i]->IsDeleted(nRedlinePos) )
1597 return true;
1599 // has a deleted cell in the not deleted row
1600 SwTableBoxes& rBoxes = m_aLines[i]->GetTabBoxes();
1601 for( size_t j = 0; j < rBoxes.size(); ++j )
1603 if ( RedlineType::Delete == rBoxes[j]->GetRedlineType() )
1604 return true;
1607 return false;
1610 bool SwTable::IsDeleted() const
1612 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1613 if ( aRedlineTable.empty() )
1614 return false;
1616 SwRedlineTable::size_type nRedlinePos = 0;
1617 for (size_t i = 0; i < m_aLines.size(); ++i)
1619 if ( !m_aLines[i]->IsDeleted(nRedlinePos) )
1620 return false;
1622 return true;
1625 void SwTable::GatherFormulas(std::vector<SwTableBoxFormula*>& rvFormulas)
1627 GatherFormulas(*GetFrameFormat()->GetDoc(), rvFormulas);
1630 void SwTable::GatherFormulas(SwDoc& rDoc, std::vector<SwTableBoxFormula*>& rvFormulas)
1632 rvFormulas.clear();
1633 sw::TableFrameFormats* pTableFrameFormats = rDoc.GetTableFrameFormats();
1634 for(SwTableFormat* pFormat : *pTableFrameFormats)
1636 SwTable* pTable = FindTable(pFormat);
1637 if (!pTable)
1638 continue;
1639 SwTableLines& rTableLines = pTable->GetTabLines();
1640 for (SwTableLine* pTableLine : rTableLines)
1642 SwTableBoxes& rTableBoxes = pTableLine->GetTabBoxes();
1643 for (SwTableBox* pTableBox : rTableBoxes)
1645 SwTableBoxFormat* pTableBoxFormat = pTableBox->GetFrameFormat();
1646 if (const SwTableBoxFormula* pBoxFormula = pTableBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false ))
1648 const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
1649 if(!pNd || &pNd->GetNodes() != &pNd->GetDoc().GetNodes()) // is this ever valid or should we assert here?
1650 continue;
1651 rvFormulas.push_back(const_cast<SwTableBoxFormula*>(pBoxFormula));
1658 void SwTable::Split(const OUString& sNewTableName, sal_uInt16 nSplitLine, SwHistory* pHistory)
1660 SwTableFormulaUpdate aHint(this);
1661 aHint.m_eFlags = TBL_SPLITTBL;
1662 aHint.m_aData.pNewTableNm = &sNewTableName;
1663 aHint.m_nSplitLine = nSplitLine;
1665 std::vector<SwTableBoxFormula*> vFormulas;
1666 GatherFormulas(vFormulas);
1667 for(auto pBoxFormula: vFormulas)
1669 const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
1670 const SwTableNode* pTableNd = pNd->FindTableNode();
1671 if(pTableNd == nullptr)
1672 continue;
1673 if(&pTableNd->GetTable() == this)
1675 sal_uInt16 nLnPos = SwTableFormula::GetLnPosInTable(*this, pBoxFormula->GetTableBox());
1676 aHint.m_bBehindSplitLine = USHRT_MAX != nLnPos && aHint.m_nSplitLine <= nLnPos;
1678 else
1679 aHint.m_bBehindSplitLine = false;
1680 pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
1684 void SwTable::Merge(SwTable& rTable, SwHistory* pHistory)
1686 SwTableFormulaUpdate aHint(this);
1687 aHint.m_eFlags = TBL_MERGETBL;
1688 aHint.m_aData.pDelTable = &rTable;
1689 std::vector<SwTableBoxFormula*> vFormulas;
1690 GatherFormulas(vFormulas);
1691 for(auto pBoxFormula: vFormulas)
1692 pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
1695 void SwTable::UpdateFields(TableFormulaUpdateFlags eFlags)
1697 auto pDoc = GetFrameFormat()->GetDoc();
1698 auto pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Table, OUString(), false);
1699 if(!pFieldType)
1700 return;
1701 std::vector<SwFormatField*> vFields;
1702 pFieldType->GatherFields(vFields);
1703 for(auto pFormatField : vFields)
1705 SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField());
1706 // table where this field is located
1707 const SwTableNode* pTableNd;
1708 const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
1709 pTableNd = rTextNd.FindTableNode();
1710 if(pTableNd == nullptr || &pTableNd->GetTable() != this)
1711 continue;
1713 switch(eFlags)
1715 case TBL_BOXNAME:
1716 // to the external representation
1717 pField->PtrToBoxNm(this);
1718 break;
1719 case TBL_RELBOXNAME:
1720 // to the relative representation
1721 pField->ToRelBoxNm(this);
1722 break;
1723 case TBL_BOXPTR:
1724 // to the internal representation
1725 // JP 17.06.96: internal representation on all formulas
1726 // (reference to other table!!!)
1727 pField->BoxNmToPtr( &pTableNd->GetTable() );
1728 break;
1729 default:
1730 assert(false); // Only TBL_BOXNAME, TBL_RELBOXNAME and TBL_BOXPTR are supported
1731 break;
1735 // process all table box formulas
1736 SwTableLines& rTableLines = GetTabLines();
1737 for (SwTableLine* pTableLine : rTableLines)
1739 SwTableBoxes& rTableBoxes = pTableLine->GetTabBoxes();
1740 for (SwTableBox* pTableBox : rTableBoxes)
1742 SwTableBoxFormat* pTableBoxFormat = pTableBox->GetFrameFormat();
1743 if (const SwTableBoxFormula* pItem = pTableBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false ))
1745 // SwTableBoxFormula is non-shareable, so const_cast is somewhat OK
1746 auto & rBoxFormula = const_cast<SwTableBoxFormula&>(*pItem);
1747 if(eFlags == TBL_BOXPTR)
1748 rBoxFormula.TryBoxNmToPtr();
1749 else if(eFlags == TBL_RELBOXNAME)
1750 rBoxFormula.TryRelBoxNm();
1751 else
1752 rBoxFormula.ChangeState();
1758 void SwTable::dumpAsXml(xmlTextWriterPtr pWriter) const
1760 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
1761 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1762 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("table-format"), "%p", GetFrameFormat());
1763 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("lines"));
1764 for (const auto& pLine : m_aLines)
1766 pLine->dumpAsXml(pWriter);
1768 (void)xmlTextWriterEndElement(pWriter);
1769 (void)xmlTextWriterEndElement(pWriter);
1772 // TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells.
1773 // At tracked row deletion, return with the newest deletion of the row or
1774 // at tracked row insertion, return with the oldest insertion in the row, which
1775 // contain the change data of the row change.
1776 // If the return value is SwRedlineTable::npos, there is no tracked row change.
1777 SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(
1778 SwRedlineTable::size_type& rRedlinePos, bool bUpdateProperty ) const
1780 SwRedlineTable::size_type nRet = SwRedlineTable::npos;
1781 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1783 // check table row property "HasTextChangesOnly", if it's defined and its
1784 // value is false, and all text content is in delete redlines, the row is deleted
1785 const SvxPrintItem *pHasTextChangesOnlyProp =
1786 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
1787 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
1789 const SwTableBoxes & rBoxes = GetTabBoxes();
1790 size_t nBoxes = rBoxes.size();
1791 bool bInsertion = false;
1792 bool bPlainTextInLine = false;
1793 SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
1794 SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos;
1795 for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex)
1797 auto pBox = rBoxes[nBoxIndex];
1798 if ( pBox->IsEmpty( /*bWithRemainingNestedTable =*/ false ) )
1800 // no text content, check the next cells
1801 continue;
1804 bool bHasRedlineInBox = false;
1805 SwPosition aCellStart( *pBox->GetSttNd(), SwNodeOffset(0) );
1806 SwPosition aCellEnd( *pBox->GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
1807 SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
1808 SwRangeRedline* pPreviousDeleteRedline = nullptr;
1809 for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos )
1811 const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ];
1813 if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
1815 // no more redlines in the actual cell,
1816 // check the next ones
1817 break;
1820 // redline in the cell
1821 if ( aCellStart <= *pRedline->Start() )
1823 if ( !bHasRedlineInBox )
1825 bHasRedlineInBox = true;
1826 // plain text before the first redline in the text
1827 if ( pRedline->Start()->GetContentIndex() > 0 )
1828 bPlainTextInLine = true;
1831 RedlineType nType = pRedline->GetType();
1833 // first insert redline
1834 if ( !bInsertion )
1836 if ( RedlineType::Insert == nType )
1838 bInsertion = true;
1840 else
1842 // plain text between the delete redlines
1843 if ( pPreviousDeleteRedline &&
1844 *pPreviousDeleteRedline->End() < *pRedline->Start() &&
1845 // in the same section, i.e. not in a nested table
1846 pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
1847 pRedline->Start()->nNode.GetNode().StartOfSectionNode() )
1849 bPlainTextInLine = true;
1851 pPreviousDeleteRedline = const_cast<SwRangeRedline*>(pRedline);
1855 // search newest and oldest redlines
1856 if ( nNewestRedline == SwRedlineTable::npos ||
1857 aRedlineTable[nNewestRedline]->GetRedlineData().GetTimeStamp() <
1858 pRedline->GetRedlineData().GetTimeStamp() )
1860 nNewestRedline = rRedlinePos;
1862 if ( nOldestRedline == SwRedlineTable::npos ||
1863 aRedlineTable[nOldestRedline]->GetRedlineData().GetTimeStamp() >
1864 pRedline->GetRedlineData().GetTimeStamp() )
1866 nOldestRedline = rRedlinePos;
1871 // there is text content outside of redlines: not a deletion
1872 if ( !bInsertion && ( !bHasRedlineInBox || ( pPreviousDeleteRedline &&
1873 // in the same cell, i.e. not in a nested table
1874 pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
1875 aCellEnd.GetNode().StartOfSectionNode() &&
1876 ( pPreviousDeleteRedline->End()->GetNode() < aCellEnd.GetNode() ||
1877 pPreviousDeleteRedline->End()->GetContentIndex() <
1878 aCellEnd.GetNode().GetContentNode()->Len() ) ) ) )
1880 bPlainTextInLine = true;
1881 // not deleted cell content: the row is not empty
1882 // maybe insertion of a row, try to search it
1883 bInsertion = true;
1887 // choose return redline, if it exists or remove changed row attribute
1888 if ( bInsertion && SwRedlineTable::npos != nOldestRedline &&
1889 RedlineType::Insert == aRedlineTable[ nOldestRedline ]->GetType() )
1891 // there is an insert redline, which is the oldest redline in the row
1892 nRet = nOldestRedline;
1894 else if ( !bInsertion && !bPlainTextInLine && SwRedlineTable::npos != nNewestRedline &&
1895 RedlineType::Delete == aRedlineTable[ nNewestRedline ]->GetType() )
1897 // there is a delete redline, which is the newest redline in the row,
1898 // and no text outside of redlines, and no insert redline in the row,
1899 // i.e. whole text content is deleted
1900 nRet = nNewestRedline;
1902 else
1904 // no longer tracked row insertion or deletion
1905 nRet = SwRedlineTable::npos;
1906 // set TextChangesOnly = true to remove the tracked deletion
1907 // FIXME Undo is not supported here (this is only a fallback,
1908 // because using SetRowNotTracked() is not recommended here)
1909 if ( bUpdateProperty )
1911 SvxPrintItem aUnsetTracking(RES_PRINT, true);
1912 SwFrameFormat *pFormat = const_cast<SwTableLine*>(this)->ClaimFrameFormat();
1913 pFormat->LockModify();
1914 pFormat->SetFormatAttr( aUnsetTracking );
1915 pFormat->UnlockModify();
1920 // cache the result
1921 const_cast<SwTableLine*>(this)->SetRedlineType( SwRedlineTable::npos == nRet
1922 ? RedlineType::None
1923 : aRedlineTable[ nRet ]->GetType());
1925 return nRet;
1928 SwRedlineTable::size_type SwTableLine::GetTableRedline() const
1930 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1931 const SwStartNode* pFirstBox = GetTabBoxes().front()->GetSttNd();
1932 const SwStartNode* pLastBox = GetTabBoxes().back()->GetSttNd();
1934 // Box with no start node
1935 if ( !pFirstBox || !pLastBox )
1936 return SwRedlineTable::npos;
1938 const SwPosition aLineStart(*pFirstBox);
1939 const SwPosition aLineEnd(*pLastBox);
1940 SwRedlineTable::size_type n = 0;
1942 const SwRangeRedline* pFnd = aRedlineTable.FindAtPosition( aLineStart, n, /*next=*/false );
1943 if( pFnd && *pFnd->Start() < aLineStart && *pFnd->End() > aLineEnd )
1944 return n;
1946 return SwRedlineTable::npos;
1949 bool SwTableLine::IsTracked(SwRedlineTable::size_type& rRedlinePos, bool bOnlyDeleted) const
1951 SwRedlineTable::size_type nPos = UpdateTextChangesOnly(rRedlinePos);
1952 if ( nPos != SwRedlineTable::npos )
1954 const SwRedlineTable& aRedlineTable =
1955 GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1956 if ( RedlineType::Delete == aRedlineTable[nPos]->GetType() ||
1957 ( !bOnlyDeleted && RedlineType::Insert == aRedlineTable[nPos]->GetType() ) )
1958 return true;
1960 return false;
1963 bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const
1965 // if not a deleted row, check the deleted columns
1966 if ( !IsTracked(rRedlinePos, /*bOnlyDeleted=*/true) )
1968 const SwTableBoxes& rBoxes = GetTabBoxes();
1969 for( size_t i = 0; i < rBoxes.size(); ++i )
1971 // there is a not deleted column
1972 if ( rBoxes[i]->GetRedlineType() != RedlineType::Delete )
1973 return false;
1977 return true;
1980 RedlineType SwTableLine::GetRedlineType() const
1982 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1983 if ( aRedlineTable.empty() )
1984 return RedlineType::None;
1986 // check table row property "HasTextChangesOnly", if it's defined and its value is
1987 // false, return with the cached redline type, if it exists, otherwise calculate it
1988 const SvxPrintItem *pHasTextChangesOnlyProp =
1989 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
1990 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
1992 if ( RedlineType::None != m_eRedlineType )
1993 return m_eRedlineType;
1995 SwRedlineTable::size_type nPos = 0;
1996 nPos = UpdateTextChangesOnly(nPos);
1997 if ( nPos != SwRedlineTable::npos )
1998 return aRedlineTable[nPos]->GetType();
2000 else if ( RedlineType::None != m_eRedlineType )
2001 // empty the cache
2002 const_cast<SwTableLine*>(this)->SetRedlineType( RedlineType::None );
2004 // is the whole table part of a changed text
2005 SwRedlineTable::size_type nTableRedline = GetTableRedline();
2006 if ( nTableRedline != SwRedlineTable::npos )
2007 return aRedlineTable[nTableRedline]->GetType();
2009 return RedlineType::None;
2012 void SwTableLine::dumpAsXml(xmlTextWriterPtr pWriter) const
2014 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableLine"));
2015 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
2017 GetFrameFormat()->dumpAsXml(pWriter);
2019 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("boxes"));
2020 for (const auto& pBox : m_aBoxes)
2022 pBox->dumpAsXml(pWriter);
2024 (void)xmlTextWriterEndElement(pWriter);
2026 (void)xmlTextWriterEndElement(pWriter);
2029 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp )
2030 : SwClient(nullptr)
2031 , m_aLines()
2032 , m_pStartNode(nullptr)
2033 , m_pUpper(pUp)
2034 , mnRowSpan(1)
2035 , mbDummyFlag(false)
2036 , mbDirectFormatting(false)
2038 m_aLines.reserve( nLines );
2039 CheckBoxFormat( pFormat )->Add(*this);
2042 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwNodeIndex &rIdx,
2043 SwTableLine *pUp )
2044 : SwClient(nullptr)
2045 , m_aLines()
2046 , m_pUpper(pUp)
2047 , mnRowSpan(1)
2048 , mbDummyFlag(false)
2049 , mbDirectFormatting(false)
2051 CheckBoxFormat( pFormat )->Add(*this);
2053 m_pStartNode = rIdx.GetNode().GetStartNode();
2055 // insert into the table
2056 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2057 assert(pTableNd && "In which table is that box?");
2058 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2059 GetTabSortBoxes());
2060 SwTableBox* p = this; // error: &this
2061 rSrtArr.insert( p ); // insert
2064 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwStartNode& rSttNd, SwTableLine *pUp )
2065 : SwClient(nullptr)
2066 , m_aLines()
2067 , m_pStartNode(&rSttNd)
2068 , m_pUpper(pUp)
2069 , mnRowSpan(1)
2070 , mbDummyFlag(false)
2071 , mbDirectFormatting(false)
2073 CheckBoxFormat( pFormat )->Add(*this);
2075 // insert into the table
2076 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2077 assert(pTableNd && "In which table is the box?");
2078 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2079 GetTabSortBoxes());
2080 SwTableBox* p = this; // error: &this
2081 rSrtArr.insert( p ); // insert
2084 void SwTableBox::RemoveFromTable()
2086 if (m_pStartNode) // box containing contents?
2088 // remove from table
2089 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2090 assert(pTableNd && "In which table is that box?");
2091 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2092 GetTabSortBoxes());
2093 SwTableBox *p = this; // error: &this
2094 rSrtArr.erase( p ); // remove
2095 m_pStartNode = nullptr; // clear it so this is only run once
2099 SwTableBox::~SwTableBox()
2101 if (!GetFrameFormat()->GetDoc()->IsInDtor())
2103 RemoveFromTable();
2106 // the TabelleBox can be deleted if it's the last client of the FrameFormat
2107 sw::BroadcastingModify* pMod = GetFrameFormat();
2108 pMod->Remove(*this); // remove,
2109 if( !pMod->HasWriterListeners() )
2110 delete pMod; // and delete
2113 SwTableBoxFormat* SwTableBox::CheckBoxFormat( SwTableBoxFormat* pFormat )
2115 // We might need to create a new format here, because the box must be
2116 // added to the format solely if pFormat has a value or form.
2117 if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) ||
2118 SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ) )
2120 SwTableBox* pOther = SwIterator<SwTableBox,SwFormat>( *pFormat ).First();
2121 if( pOther )
2123 SwTableBoxFormat* pNewFormat = pFormat->GetDoc()->MakeTableBoxFormat();
2124 pNewFormat->LockModify();
2125 *pNewFormat = *pFormat;
2127 // Remove values and formulas
2128 pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
2129 pNewFormat->UnlockModify();
2131 pFormat = pNewFormat;
2134 return pFormat;
2137 SwTableBoxFormat* SwTableBox::ClaimFrameFormat()
2139 // This method makes sure that this object is an exclusive SwTableBox client
2140 // of an SwTableBoxFormat object
2141 // If other SwTableBox objects currently listen to the same SwTableBoxFormat as
2142 // this one, something needs to be done
2143 SwTableBoxFormat *pRet = GetFrameFormat();
2144 SwIterator<SwTableBox,SwFormat> aIter( *pRet );
2145 for( SwTableBox* pLast = aIter.First(); pLast; pLast = aIter.Next() )
2147 if ( pLast != this )
2149 // Found another SwTableBox object
2150 // create a new Format as a copy and assign me to it
2151 // don't copy values and formulas
2152 SwTableBoxFormat* pNewFormat = pRet->GetDoc()->MakeTableBoxFormat();
2153 pNewFormat->LockModify();
2154 *pNewFormat = *pRet;
2155 pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
2156 pNewFormat->UnlockModify();
2158 // re-register SwCellFrame objects that know me
2159 SwIterator<SwCellFrame,SwFormat> aFrameIter( *pRet );
2160 for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() )
2161 if( pCell->GetTabBox() == this )
2162 pCell->RegisterToFormat( *pNewFormat );
2164 // re-register myself
2165 pNewFormat->Add(*this);
2166 pRet = pNewFormat;
2167 break;
2170 return pRet;
2173 void SwTableBox::ChgFrameFormat(SwTableBoxFormat* pNewFormat, bool bNeedToReregister)
2175 SwFrameFormat* pOld = GetFrameFormat();
2176 // tdf#84635 We set bNeedToReregister=false to avoid a quadratic slowdown on loading large tables,
2177 // and since we are creating the table for the first time, no re-registration is necessary.
2178 // First, re-register the Frames.
2179 if(bNeedToReregister)
2180 pOld->CallSwClientNotify(sw::TableBoxFormatChanged(*pNewFormat, *this));
2181 // Now, re-register self.
2182 pNewFormat->Add(*this);
2183 if(!pOld->HasWriterListeners())
2184 delete pOld;
2187 // Return the name of this box. This is determined dynamically
2188 // resulting from the position in the lines/boxes/tables.
2189 void sw_GetTableBoxColStr( sal_uInt16 nCol, OUString& rNm )
2191 const sal_uInt16 coDiff = 52; // 'A'-'Z' 'a' - 'z'
2193 do {
2194 const sal_uInt16 nCalc = nCol % coDiff;
2195 if( nCalc >= 26 )
2196 rNm = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + rNm;
2197 else
2198 rNm = OUStringChar( sal_Unicode('A' + nCalc) ) + rNm;
2200 nCol = nCol - nCalc;
2201 if( 0 == nCol )
2202 break;
2203 nCol /= coDiff;
2204 --nCol;
2205 } while( true );
2208 Point SwTableBox::GetCoordinates() const
2210 if( !m_pStartNode ) // box without content?
2212 // search for the next first box?
2213 return Point( 0, 0 );
2216 const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
2217 sal_uInt16 nX, nY;
2218 const SwTableBox* pBox = this;
2219 do {
2220 const SwTableLine* pLine = pBox->GetUpper();
2221 // at the first level?
2222 const SwTableLines* pLines = pLine->GetUpper()
2223 ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
2225 nY = pLines->GetPos( pLine ) + 1 ;
2226 nX = pBox->GetUpper()->GetBoxPos( pBox ) + 1;
2227 pBox = pLine->GetUpper();
2228 } while( pBox );
2229 return Point( nX, nY );
2232 OUString SwTableBox::GetName() const
2234 if( !m_pStartNode ) // box without content?
2236 // search for the next first box?
2237 return OUString();
2240 const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
2241 sal_uInt16 nPos;
2242 OUString sNm, sTmp;
2243 const SwTableBox* pBox = this;
2244 do {
2245 const SwTableLine* pLine = pBox->GetUpper();
2246 // at the first level?
2247 const SwTableLines* pLines = pLine->GetUpper()
2248 ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
2250 nPos = pLines->GetPos( pLine ) + 1;
2251 sTmp = OUString::number( nPos );
2252 if( !sNm.isEmpty() )
2253 sNm = sTmp + "." + sNm;
2254 else
2255 sNm = sTmp;
2257 nPos = pBox->GetUpper()->GetBoxPos( pBox );
2258 sTmp = OUString::number(nPos + 1);
2259 pBox = pLine->GetUpper();
2260 if( nullptr != pBox )
2261 sNm = sTmp + "." + sNm;
2262 else
2263 sw_GetTableBoxColStr( nPos, sNm );
2265 } while( pBox );
2266 return sNm;
2269 bool SwTableBox::IsInHeadline( const SwTable* pTable ) const
2271 if( !GetUpper() ) // should only happen upon merge.
2272 return false;
2274 if( !pTable )
2275 pTable = &m_pStartNode->FindTableNode()->GetTable();
2277 const SwTableLine* pLine = GetUpper();
2278 while( pLine->GetUpper() )
2279 pLine = pLine->GetUpper()->GetUpper();
2281 // Headerline?
2282 return pTable->GetTabLines()[ 0 ] == pLine;
2285 SwNodeOffset SwTableBox::GetSttIdx() const
2287 return m_pStartNode ? m_pStartNode->GetIndex() : SwNodeOffset(0);
2290 bool SwTableBox::IsEmpty( bool bWithRemainingNestedTable ) const
2292 const SwStartNode *pSttNd = GetSttNd();
2294 if ( !pSttNd )
2295 return false;
2297 const SwNode * pFirstNode = pSttNd->GetNodes()[pSttNd->GetIndex() + 1];
2299 if ( pSttNd->GetIndex() + 2 == pSttNd->EndOfSectionIndex() )
2301 // single empty node in the box
2302 const SwContentNode *pCNd = pFirstNode->GetContentNode();
2303 if ( pCNd && !pCNd->Len() )
2304 return true;
2306 // tdf#157011 OOXML w:std cell content is imported with terminating 0x01 characters,
2307 // i.e. an empty box can contain double 0x01: handle it to avoid losing change tracking
2308 // FIXME regression since commit b5c616d10bff3213840d4893d13b4493de71fa56
2309 if ( pCNd && pCNd->Len() == 2 && pCNd->GetTextNode() )
2311 const OUString &rText = pCNd->GetTextNode()->GetText();
2312 if ( rText[0] == 0x01 && rText[1] == 0x01 )
2313 return true;
2316 else if ( bWithRemainingNestedTable )
2318 if ( const SwTableNode * pTableNode = pFirstNode->GetTableNode() )
2320 // empty nested table in the box and
2321 // no text content after it
2322 if ( pTableNode->EndOfSectionIndex() + 2 == pSttNd->EndOfSectionIndex() )
2323 return pTableNode->GetTable().IsEmpty();
2327 return false;
2330 // retrieve information from the client
2331 bool SwTable::GetInfo( SwFindNearestNode& rInfo ) const
2333 if( GetFrameFormat() &&
2334 GetFrameFormat()->GetFormatAttr( RES_PAGEDESC ).GetPageDesc() &&
2335 !m_TabSortContentBoxes.empty() &&
2336 m_TabSortContentBoxes[0]->GetSttNd()->GetNodes().IsDocNodes() )
2337 rInfo.CheckNode( *m_TabSortContentBoxes[0]->GetSttNd()->FindTableNode() );
2338 return true;
2341 SwTable * SwTable::FindTable( SwFrameFormat const*const pFormat )
2343 return pFormat
2344 ? SwIterator<SwTable,SwFormat>(*pFormat).First()
2345 : nullptr;
2348 SwTableNode* SwTable::GetTableNode() const
2350 return !GetTabSortBoxes().empty() ?
2351 const_cast<SwTableNode*>(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) :
2352 m_pTableNode;
2355 void SwTable::SetRefObject( SwServerObject* pObj )
2357 if( m_xRefObj.is() )
2358 m_xRefObj->Closed();
2360 m_xRefObj = pObj;
2363 void SwTable::SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout> const& r)
2365 m_xHTMLLayout = r;
2368 static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
2369 bool bChgAlign )
2371 SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
2372 ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos);
2374 void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
2375 bool bChgAlign, SwNodeOffset nNdPos )
2378 if( NODE_OFFSET_MAX == nNdPos )
2379 return;
2381 SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
2382 SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
2384 // assign adjustment
2385 if( bChgAlign )
2387 const SfxPoolItem* pItem;
2388 pItem = &pTNd->SwContentNode::GetAttr( RES_PARATR_ADJUST );
2389 SvxAdjust eAdjust = static_cast<const SvxAdjustItem*>(pItem)->GetAdjust();
2390 if( SvxAdjust::Left == eAdjust || SvxAdjust::Block == eAdjust )
2392 SvxAdjustItem aAdjust( *static_cast<const SvxAdjustItem*>(pItem) );
2393 aAdjust.SetAdjust( SvxAdjust::Right );
2394 pTNd->SetAttr( aAdjust );
2398 // assign color or save "user color"
2399 const SvxColorItem* pColorItem = nullptr;
2400 if( pTNd->GetpSwAttrSet() )
2401 pColorItem = pTNd->GetpSwAttrSet()->GetItemIfSet( RES_CHRATR_COLOR, false );
2403 const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
2404 std::optional<Color> pNewUserColor;
2405 if (pColorItem)
2406 pNewUserColor = pColorItem->GetValue();
2408 if( ( pNewUserColor && pOldNumFormatColor &&
2409 *pNewUserColor == *pOldNumFormatColor ) ||
2410 ( !pNewUserColor && !pOldNumFormatColor ))
2412 // Keep the user color, set updated values, delete old NumFormatColor if needed
2413 if( pCol )
2414 // if needed, set the color
2415 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2416 else if( pColorItem )
2418 pNewUserColor = rBox.GetSaveUserColor();
2419 if( pNewUserColor )
2420 pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
2421 else
2422 pTNd->ResetAttr( RES_CHRATR_COLOR );
2425 else
2427 // Save user color, set NumFormat color if needed, but never reset the color
2428 rBox.SetSaveUserColor( pNewUserColor ? *pNewUserColor : std::optional<Color>() );
2430 if( pCol )
2431 // if needed, set the color
2432 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2435 rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
2437 if( pTNd->GetText() != rText )
2439 // Exchange text. Bugfix to keep Tabs (front and back!) and annotations (inword comment anchors)
2440 const OUString& rOrig = pTNd->GetText();
2441 sal_Int32 n;
2443 for( n = 0; n < rOrig.getLength() && ('\x9' == rOrig[n] || CH_TXTATR_INWORD == rOrig[n]); ++n )
2445 for( ; n < rOrig.getLength() && '\x01' == rOrig[n]; ++n )
2447 SwContentIndex aIdx( pTNd, n );
2448 for( n = rOrig.getLength(); n && ('\x9' == rOrig[--n] || CH_TXTATR_INWORD == rOrig[n]); )
2450 sal_Int32 nEndPos = n;
2451 n -= aIdx.GetIndex() - 1;
2453 // Reset DontExpand-Flags before exchange, to retrigger expansion
2455 SwContentIndex aResetIdx( aIdx, n );
2456 pTNd->DontExpandFormat( aResetIdx.GetIndex(), false, false );
2459 if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
2461 SwPaM aTemp(*pTNd, 0, *pTNd, rOrig.getLength());
2462 pDoc->getIDocumentRedlineAccess().DeleteRedline(aTemp, true, RedlineType::Any);
2465 // preserve comments inside of the number by deleting number portions starting from the back
2466 sal_Int32 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
2467 while( nCommentPos > aIdx.GetIndex() )
2469 pTNd->EraseText( SwContentIndex(pTNd, nCommentPos+1), nEndPos - nCommentPos, SwInsertFlags::EMPTYEXPAND );
2470 // find the next non-sequential comment anchor
2473 nEndPos = nCommentPos;
2474 n = nEndPos - aIdx.GetIndex();
2475 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
2476 --nEndPos;
2478 while( nCommentPos > aIdx.GetIndex() && nCommentPos == nEndPos );
2481 pTNd->EraseText( aIdx, n, SwInsertFlags::EMPTYEXPAND );
2482 pTNd->InsertText( rText, aIdx, SwInsertFlags::EMPTYEXPAND );
2484 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
2486 SwPaM aTemp(*pTNd, 0, *pTNd, rText.getLength());
2487 pDoc->getIDocumentRedlineAccess().AppendRedline(new SwRangeRedline(RedlineType::Insert, aTemp), true);
2491 // assign vertical orientation
2492 const SwFormatVertOrient* pVertOrientItem;
2493 if( bChgAlign &&
2494 ( !(pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT )) ||
2495 text::VertOrientation::TOP == pVertOrientItem->GetVertOrient() ))
2497 rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM ));
2502 static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat )
2504 SwNodeOffset nNdPos = rBox.IsValidNumTextNd( false );
2505 if( NODE_OFFSET_MAX == nNdPos )
2506 return;
2508 SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
2509 SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
2510 bool bChgAlign = pDoc->IsInsTableAlignNum();
2512 const Color * pCol = nullptr;
2513 if( getSwDefaultTextFormat() != nFormat )
2515 // special text format:
2516 OUString sTmp;
2517 const OUString sText( pTNd->GetText() );
2518 pDoc->GetNumberFormatter()->GetOutputString( sText, nFormat, sTmp, &pCol );
2519 if( sText != sTmp )
2521 // exchange text
2522 // Reset DontExpand-Flags before exchange, to retrigger expansion
2523 pTNd->DontExpandFormat( sText.getLength(), false, false );
2524 SwContentIndex aIdx( pTNd, 0 );
2525 pTNd->EraseText( aIdx, SAL_MAX_INT32, SwInsertFlags::EMPTYEXPAND );
2526 pTNd->InsertText( sTmp, aIdx, SwInsertFlags::EMPTYEXPAND );
2530 const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
2532 // assign adjustment
2533 const SvxAdjustItem* pAdjustItem;
2534 if( bChgAlign && pAttrSet &&
2535 (pAdjustItem = pAttrSet->GetItemIfSet( RES_PARATR_ADJUST, false )) &&
2536 SvxAdjust::Right == pAdjustItem->GetAdjust() )
2538 pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
2541 // assign color or save "user color"
2542 const SvxColorItem* pColorItem = nullptr;
2543 if( pAttrSet )
2544 pColorItem = pAttrSet->GetItemIfSet( RES_CHRATR_COLOR, false );
2546 const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
2547 std::optional<Color> pNewUserColor;
2548 if (pColorItem)
2549 pNewUserColor = pColorItem->GetValue();
2551 if( ( pNewUserColor && pOldNumFormatColor &&
2552 *pNewUserColor == *pOldNumFormatColor ) ||
2553 ( !pNewUserColor && !pOldNumFormatColor ))
2555 // Keep the user color, set updated values, delete old NumFormatColor if needed
2556 if( pCol )
2557 // if needed, set the color
2558 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2559 else if( pColorItem )
2561 pNewUserColor = rBox.GetSaveUserColor();
2562 if( pNewUserColor )
2563 pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
2564 else
2565 pTNd->ResetAttr( RES_CHRATR_COLOR );
2568 else
2570 // Save user color, set NumFormat color if needed, but never reset the color
2571 rBox.SetSaveUserColor( pNewUserColor );
2573 if( pCol )
2574 // if needed, set the color
2575 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2578 rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
2580 // assign vertical orientation
2581 const SwFormatVertOrient* pVertOrientItem;
2582 if( bChgAlign &&
2583 (pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT, false )) &&
2584 text::VertOrientation::BOTTOM == pVertOrientItem->GetVertOrient() )
2586 rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP ));
2590 void SwTableBoxFormat::BoxAttributeChanged(SwTableBox& rBox, const SwTableBoxNumFormat* pNewFormat, const SwTableBoxFormula* pNewFormula, const SwTableBoxValue* pNewValue, sal_uLong nOldFormat)
2592 sal_uLong nNewFormat;
2593 if(pNewFormat)
2595 nNewFormat = pNewFormat->GetValue();
2596 // new formatting
2597 // is it newer or has the current been removed?
2598 if( SfxItemState::SET != GetItemState(RES_BOXATR_VALUE, false))
2599 pNewFormat = nullptr;
2601 else
2603 // fetch the current Item
2604 pNewFormat = GetItemIfSet(RES_BOXATR_FORMAT, false);
2605 nOldFormat = GetTableBoxNumFormat().GetValue();
2606 nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat;
2609 // is it newer or has the current been removed?
2610 if(pNewValue)
2612 if(GetDoc()->GetNumberFormatter()->IsTextFormat(nNewFormat))
2613 nOldFormat = 0;
2614 else
2616 if(SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false))
2617 nOldFormat = getSwDefaultTextFormat();
2618 else
2619 nNewFormat = getSwDefaultTextFormat();
2623 // Logic:
2624 // Value change: -> "simulate" a format change!
2625 // Format change:
2626 // Text -> !Text or format change:
2627 // - align right for horizontal alignment, if LEFT or JUSTIFIED
2628 // - align bottom for vertical alignment, if TOP is set, or default
2629 // - replace text (color? negative numbers RED?)
2630 // !Text -> Text:
2631 // - align left for horizontal alignment, if RIGHT
2632 // - align top for vertical alignment, if BOTTOM is set
2633 SvNumberFormatter* pNumFormatr = GetDoc()->GetNumberFormatter();
2634 bool bNewIsTextFormat = pNumFormatr->IsTextFormat(nNewFormat);
2636 if((!bNewIsTextFormat && nOldFormat != nNewFormat) || pNewFormula)
2638 bool bIsNumFormat = false;
2639 OUString aOrigText;
2640 bool bChgText = true;
2641 double fVal = 0;
2642 if(!pNewValue)
2643 pNewValue = GetItemIfSet(RES_BOXATR_VALUE, false);
2644 if(!pNewValue)
2646 // so far, no value has been set, so try to evaluate the content
2647 SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
2648 if(NODE_OFFSET_MAX != nNdPos)
2650 sal_uInt32 nTmpFormatIdx = nNewFormat;
2651 OUString aText(GetDoc()->GetNodes()[nNdPos] ->GetTextNode()->GetRedlineText());
2652 aOrigText = aText;
2653 if(aText.isEmpty())
2654 bChgText = false;
2655 else
2657 // Keep Tabs
2658 lcl_TabToBlankAtSttEnd(aText);
2660 // JP 22.04.98: Bug 49659 -
2661 // Special casing for percent
2662 if(SvNumFormatType::PERCENT == pNumFormatr->GetType(nNewFormat))
2664 sal_uInt32 nTmpFormat = 0;
2665 if(GetDoc()->IsNumberFormat(aText, nTmpFormat, fVal))
2667 if(SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat))
2668 aText += "%";
2670 bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
2673 else
2674 bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
2676 if(bIsNumFormat)
2678 // directly assign value - without Modify
2679 bool bIsLockMod = IsModifyLocked();
2680 LockModify();
2681 SetFormatAttr(SwTableBoxValue(fVal));
2682 if(!bIsLockMod)
2683 UnlockModify();
2688 else
2690 fVal = pNewValue->GetValue();
2691 bIsNumFormat = true;
2694 // format contents with the new value assigned and write to paragraph
2695 const Color* pCol = nullptr;
2696 OUString sNewText;
2697 bool bChangeFormat = true;
2698 if(DBL_MAX == fVal)
2700 sNewText = SwViewShell::GetShellRes()->aCalc_Error;
2702 else
2704 if(bIsNumFormat)
2705 pNumFormatr->GetOutputString(fVal, nNewFormat, sNewText, &pCol);
2706 else
2708 // Original text could not be parsed as
2709 // number/date/time/..., so keep the text.
2710 #if 0
2711 // Actually the text should be formatted
2712 // according to the format, which may include
2713 // additional text from the format, for example
2714 // in {0;-0;"BAD: "@}. But other places when
2715 // entering a new value or changing text or
2716 // changing to a different format of type Text
2717 // don't do this (yet?).
2718 pNumFormatr->GetOutputString(aOrigText, nNewFormat, sNewText, &pCol);
2719 #else
2720 sNewText = aOrigText;
2721 #endif
2722 // Remove the newly assigned numbering format as well if text actually exists.
2723 // Exception: assume user-defined formats are always intentional.
2724 if (bChgText && pNumFormatr->IsTextFormat(nOldFormat)
2725 && !pNumFormatr->IsUserDefined(nNewFormat))
2727 rBox.GetFrameFormat()->ResetFormatAttr(RES_BOXATR_FORMAT);
2728 bChangeFormat = false;
2732 if(!bChgText)
2733 sNewText.clear();
2736 // across all boxes
2737 if (bChangeFormat)
2738 ChgTextToNum(rBox, sNewText, pCol, GetDoc()->IsInsTableAlignNum());
2741 else if(bNewIsTextFormat && nOldFormat != nNewFormat)
2742 ChgNumToText(rBox, nNewFormat);
2745 SwTableBox* SwTableBoxFormat::SwTableBoxFormat::GetTableBox()
2747 SwIterator<SwTableBox,SwFormat> aIter(*this);
2748 auto pBox = aIter.First();
2749 SAL_INFO_IF(!pBox, "sw.core", "no box found at format");
2750 SAL_WARN_IF(pBox && aIter.Next(), "sw.core", "more than one box found at format");
2751 return pBox;
2754 // for detection of modifications (mainly TableBoxAttribute)
2755 void SwTableBoxFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
2757 if(rHint.GetId() == SfxHintId::SwFormatChange
2758 || rHint.GetId() == SfxHintId::SwObjectDying
2759 || rHint.GetId() == SfxHintId::SwUpdateAttr)
2761 SwFrameFormat::SwClientNotify(rMod, rHint);
2762 return;
2764 if(rHint.GetId() != SfxHintId::SwLegacyModify && rHint.GetId() != SfxHintId::SwAttrSetChange)
2765 return;
2766 if(IsModifyLocked() || !GetDoc() || GetDoc()->IsInDtor())
2768 SwFrameFormat::SwClientNotify(rMod, rHint);
2769 return;
2771 const SwTableBoxNumFormat* pNewFormat = nullptr;
2772 const SwTableBoxFormula* pNewFormula = nullptr;
2773 const SwTableBoxValue* pNewVal = nullptr;
2774 sal_uLong nOldFormat = getSwDefaultTextFormat();
2776 if(rHint.GetId() == SfxHintId::SwLegacyModify)
2778 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
2779 switch(pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0)
2781 case RES_BOXATR_FORMAT:
2782 pNewFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pNew);
2783 nOldFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pOld)->GetValue();
2784 break;
2785 case RES_BOXATR_FORMULA:
2786 pNewFormula = static_cast<const SwTableBoxFormula*>(pLegacy->m_pNew);
2787 break;
2788 case RES_BOXATR_VALUE:
2789 pNewVal = static_cast<const SwTableBoxValue*>(pLegacy->m_pNew);
2790 break;
2793 else // rHint.GetId() == SfxHintId::SwAttrSetChange
2795 auto pChangeHint = static_cast<const sw::AttrSetChangeHint*>(&rHint);
2796 const SfxItemSet& rSet = *pChangeHint->m_pNew->GetChgSet();
2797 pNewFormat = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false);
2798 if(pNewFormat)
2799 nOldFormat = pChangeHint->m_pOld->GetChgSet()->Get(RES_BOXATR_FORMAT).GetValue();
2800 pNewFormula = rSet.GetItemIfSet(RES_BOXATR_FORMULA, false);
2801 pNewVal = rSet.GetItemIfSet(RES_BOXATR_VALUE, false);
2804 // something changed and some BoxAttribute remained in the set!
2805 if( pNewFormat || pNewFormula || pNewVal )
2807 GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
2809 if(SfxItemState::SET == GetItemState(RES_BOXATR_FORMAT, false) ||
2810 SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false) ||
2811 SfxItemState::SET == GetItemState(RES_BOXATR_FORMULA, false) )
2813 if(auto pBox = GetTableBox())
2814 BoxAttributeChanged(*pBox, pNewFormat, pNewFormula, pNewVal, nOldFormat);
2817 // call base class
2818 SwFrameFormat::SwClientNotify(rMod, rHint);
2821 bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const
2823 return false;
2826 bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const
2828 return false;
2831 bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const
2833 return false;
2836 bool SwTableBox::HasNumContent( double& rNum, sal_uInt32& rFormatIndex,
2837 bool& rIsEmptyTextNd ) const
2839 bool bRet = false;
2840 SwNodeOffset nNdPos = IsValidNumTextNd();
2841 if( NODE_OFFSET_MAX != nNdPos )
2843 OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() );
2844 // Keep Tabs
2845 lcl_TabToBlankAtSttEnd( aText );
2846 rIsEmptyTextNd = aText.isEmpty();
2847 SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter();
2849 if( const SwTableBoxNumFormat* pItem = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false) )
2851 rFormatIndex = pItem->GetValue();
2852 // Special casing for percent
2853 if( !rIsEmptyTextNd && SvNumFormatType::PERCENT == pNumFormatr->GetType( rFormatIndex ))
2855 sal_uInt32 nTmpFormat = 0;
2856 if( GetFrameFormat()->GetDoc()->IsNumberFormat( aText, nTmpFormat, rNum ) &&
2857 SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat ))
2858 aText += "%";
2861 else
2862 rFormatIndex = 0;
2864 bRet = GetFrameFormat()->GetDoc()->IsNumberFormat( aText, rFormatIndex, rNum );
2866 else
2867 rIsEmptyTextNd = false;
2868 return bRet;
2871 bool SwTableBox::IsNumberChanged() const
2873 bool bRet = true;
2875 if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA, false ))
2877 const SwTableBoxNumFormat *pNumFormat = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false );
2878 const SwTableBoxValue *pValue = GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false );
2880 SwNodeOffset nNdPos;
2881 if( pNumFormat && pValue && NODE_OFFSET_MAX != ( nNdPos = IsValidNumTextNd() ) )
2883 OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]->
2884 GetTextNode()->GetRedlineText() );
2885 lcl_DelTabsAtSttEnd( sOldText );
2887 const Color* pCol = nullptr;
2888 GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString(
2889 pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol );
2891 bRet = sNewText != sOldText ||
2892 !( ( !pCol && !GetSaveNumFormatColor() ) ||
2893 ( pCol && GetSaveNumFormatColor() &&
2894 *pCol == *GetSaveNumFormatColor() ));
2897 return bRet;
2900 SwNodeOffset SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const
2902 SwNodeOffset nPos = NODE_OFFSET_MAX;
2903 if( m_pStartNode )
2905 SwNodeIndex aIdx( *m_pStartNode );
2906 SwNodeOffset nIndex = aIdx.GetIndex();
2907 const SwNodeOffset nIndexEnd = m_pStartNode->GetNodes()[ nIndex ]->EndOfSectionIndex();
2908 const SwTextNode *pTextNode = nullptr;
2909 while( ++nIndex < nIndexEnd )
2911 const SwNode* pNode = m_pStartNode->GetNodes()[nIndex];
2912 if( pNode->IsTableNode() )
2914 pTextNode = nullptr;
2915 break;
2917 if( pNode->IsTextNode() )
2919 if( pTextNode )
2921 pTextNode = nullptr;
2922 break;
2924 else
2926 pTextNode = pNode->GetTextNode();
2927 nPos = nIndex;
2931 if( pTextNode )
2933 if( bCheckAttr )
2935 const SwpHints* pHts = pTextNode->GetpSwpHints();
2936 // do some tests if there's only text in the node!
2937 // Flys/fields/...
2938 if( pHts )
2940 sal_Int32 nNextSetField = 0;
2941 for( size_t n = 0; n < pHts->Count(); ++n )
2943 const SwTextAttr* pAttr = pHts->Get(n);
2944 if( RES_TXTATR_NOEND_BEGIN <= pAttr->Which() )
2946 if ( (pAttr->GetStart() == nNextSetField)
2947 && (pAttr->Which() == RES_TXTATR_FIELD))
2949 // #i104949# hideous hack for report builder:
2950 // it inserts hidden variable-set fields at
2951 // the beginning of para in cell, but they
2952 // should not turn cell into text cell
2953 const SwField* pField = pAttr->GetFormatField().GetField();
2954 if (pField &&
2955 (pField->GetTypeId() == SwFieldTypesEnum::Set) &&
2956 (0 != (static_cast<SwSetExpField const*>
2957 (pField)->GetSubType() &
2958 nsSwExtendedSubType::SUB_INVISIBLE)))
2960 nNextSetField = pAttr->GetStart() + 1;
2961 continue;
2964 else if( RES_TXTATR_ANNOTATION == pAttr->Which() ||
2965 RES_TXTATR_FTN == pAttr->Which() )
2967 continue;
2969 nPos = NODE_OFFSET_MAX;
2970 break;
2976 else
2977 nPos = NODE_OFFSET_MAX;
2979 return nPos;
2982 // is this a Formula box or one with numeric content (AutoSum)
2983 sal_uInt16 SwTableBox::IsFormulaOrValueBox() const
2985 sal_uInt16 nWhich = 0;
2986 const SwTextNode* pTNd;
2987 SwFrameFormat* pFormat = GetFrameFormat();
2988 if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ))
2989 nWhich = RES_BOXATR_FORMULA;
2990 else if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) &&
2991 !pFormat->GetDoc()->GetNumberFormatter()->IsTextFormat(
2992 pFormat->GetTableBoxNumFormat().GetValue() ))
2993 nWhich = RES_BOXATR_VALUE;
2994 else if( m_pStartNode && m_pStartNode->GetIndex() + 2 == m_pStartNode->EndOfSectionIndex()
2995 && nullptr != ( pTNd = m_pStartNode->GetNodes()[ m_pStartNode->GetIndex() + 1 ]
2996 ->GetTextNode() ) && pTNd->GetText().isEmpty())
2997 nWhich = USHRT_MAX;
2999 return nWhich;
3002 void SwTableBox::ActualiseValueBox()
3004 SwFrameFormat* pFormat = GetFrameFormat();
3005 const SwTableBoxNumFormat *pFormatItem = pFormat->GetItemIfSet( RES_BOXATR_FORMAT, true );
3006 if (!pFormatItem)
3007 return;
3008 const SwTableBoxValue *pValItem = pFormat->GetItemIfSet( RES_BOXATR_VALUE );
3009 if (!pValItem)
3010 return;
3012 const sal_uLong nFormatId = pFormatItem->GetValue();
3013 SwNodeOffset nNdPos = NODE_OFFSET_MAX;
3014 SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter();
3016 if( !pNumFormatr->IsTextFormat( nFormatId ) &&
3017 NODE_OFFSET_MAX != (nNdPos = IsValidNumTextNd()) )
3019 double fVal = pValItem->GetValue();
3020 const Color* pCol = nullptr;
3021 OUString sNewText;
3022 pNumFormatr->GetOutputString( fVal, nFormatId, sNewText, &pCol );
3024 const OUString& rText = m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetText();
3025 if( rText != sNewText )
3026 ChgTextToNum( *this, sNewText, pCol, false ,nNdPos);
3030 SwRedlineTable::size_type SwTableBox::GetRedline() const
3032 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
3033 const SwStartNode *pSttNd = GetSttNd();
3035 if ( aRedlineTable.empty() || !pSttNd )
3036 return SwRedlineTable::npos;
3038 // check table row property "HasTextChangesOnly", if it's defined and its value is
3039 // false, return with the first redline of the cell
3040 const SvxPrintItem *pHasTextChangesOnlyProp =
3041 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
3042 if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
3043 return SwRedlineTable::npos;
3045 SwPosition aCellStart( *GetSttNd(), SwNodeOffset(0) );
3046 SwPosition aCellEnd( *GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
3047 SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
3048 SwRedlineTable::size_type nRedlinePos = 0;
3049 for( ; nRedlinePos < aRedlineTable.size(); ++nRedlinePos )
3051 const SwRangeRedline* pRedline = aRedlineTable[ nRedlinePos ];
3053 if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
3055 // no more redlines in the actual cell,
3056 // check the next ones
3057 break;
3060 // redline in the cell
3061 if ( aCellStart <= *pRedline->Start() )
3062 return nRedlinePos;
3065 return SwRedlineTable::npos;
3068 RedlineType SwTableBox::GetRedlineType() const
3070 SwRedlineTable::size_type nPos = GetRedline();
3071 if ( nPos != SwRedlineTable::npos )
3073 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
3074 const SwRangeRedline* pRedline = aRedlineTable[ nPos ];
3075 if ( RedlineType::Delete == pRedline->GetType() ||
3076 RedlineType::Insert == pRedline->GetType() )
3078 return pRedline->GetType();
3081 return RedlineType::None;
3084 void SwTableBox::dumpAsXml(xmlTextWriterPtr pWriter) const
3086 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableBox"));
3087 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
3088 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("start-node"), BAD_CAST(OString::number(static_cast<sal_Int32>(m_pStartNode->GetIndex())).getStr()));
3089 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("rowspan"), BAD_CAST(OString::number(mnRowSpan).getStr()));
3090 GetFrameFormat()->dumpAsXml(pWriter);
3091 (void)xmlTextWriterEndElement(pWriter);
3094 struct SwTableCellInfo::Impl
3096 const SwTable * m_pTable;
3097 const SwCellFrame * m_pCellFrame;
3098 const SwTabFrame * m_pTabFrame;
3099 typedef o3tl::sorted_vector<const SwTableBox *> TableBoxes_t;
3100 TableBoxes_t m_HandledTableBoxes;
3102 public:
3103 Impl()
3104 : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr)
3108 void setTable(const SwTable * pTable)
3110 m_pTable = pTable;
3111 SwFrameFormat * pFrameFormat = m_pTable->GetFrameFormat();
3112 m_pTabFrame = SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
3113 if (m_pTabFrame && m_pTabFrame->IsFollow())
3114 m_pTabFrame = m_pTabFrame->FindMaster(true);
3117 const SwCellFrame * getCellFrame() const { return m_pCellFrame; }
3119 const SwFrame * getNextFrameInTable(const SwFrame * pFrame);
3120 const SwCellFrame * getNextCellFrame(const SwFrame * pFrame);
3121 const SwCellFrame * getNextTableBoxsCellFrame(const SwFrame * pFrame);
3122 bool getNext();
3125 const SwFrame * SwTableCellInfo::Impl::getNextFrameInTable(const SwFrame * pFrame)
3127 const SwFrame * pResult = nullptr;
3129 if (((! pFrame->IsTabFrame()) || pFrame == m_pTabFrame) && pFrame->GetLower())
3130 pResult = pFrame->GetLower();
3131 else if (pFrame->GetNext())
3132 pResult = pFrame->GetNext();
3133 else
3135 while (pFrame->GetUpper() != nullptr)
3137 pFrame = pFrame->GetUpper();
3139 if (pFrame->IsTabFrame())
3141 m_pTabFrame = static_cast<const SwTabFrame *>(pFrame)->GetFollow();
3142 pResult = m_pTabFrame;
3143 break;
3145 else if (pFrame->GetNext())
3147 pResult = pFrame->GetNext();
3148 break;
3153 return pResult;
3156 const SwCellFrame * SwTableCellInfo::Impl::getNextCellFrame(const SwFrame * pFrame)
3158 const SwCellFrame * pResult = nullptr;
3160 while ((pFrame = getNextFrameInTable(pFrame)) != nullptr)
3162 if (pFrame->IsCellFrame())
3164 pResult = static_cast<const SwCellFrame *>(pFrame);
3165 break;
3169 return pResult;
3172 const SwCellFrame * SwTableCellInfo::Impl::getNextTableBoxsCellFrame(const SwFrame * pFrame)
3174 const SwCellFrame * pResult = nullptr;
3176 while ((pFrame = getNextCellFrame(pFrame)) != nullptr)
3178 const SwCellFrame * pCellFrame = static_cast<const SwCellFrame *>(pFrame);
3179 const SwTableBox * pTabBox = pCellFrame->GetTabBox();
3180 auto aIt = m_HandledTableBoxes.insert(pTabBox);
3181 if (aIt.second)
3183 pResult = pCellFrame;
3184 break;
3188 return pResult;
3191 const SwCellFrame * SwTableCellInfo::getCellFrame() const
3193 return m_pImpl->getCellFrame();
3196 bool SwTableCellInfo::Impl::getNext()
3198 if (m_pCellFrame == nullptr)
3200 if (m_pTabFrame != nullptr)
3201 m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pTabFrame);
3203 else
3204 m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pCellFrame);
3206 return m_pCellFrame != nullptr;
3209 SwTableCellInfo::SwTableCellInfo(const SwTable * pTable)
3210 : m_pImpl(std::make_unique<Impl>())
3212 m_pImpl->setTable(pTable);
3215 SwTableCellInfo::~SwTableCellInfo()
3219 bool SwTableCellInfo::getNext()
3221 return m_pImpl->getNext();
3224 SwRect SwTableCellInfo::getRect() const
3226 SwRect aRet;
3228 if (getCellFrame() != nullptr)
3229 aRet = getCellFrame()->getFrameArea();
3231 return aRet;
3234 const SwTableBox * SwTableCellInfo::getTableBox() const
3236 const SwTableBox * pRet = nullptr;
3238 if (getCellFrame() != nullptr)
3239 pRet = getCellFrame()->GetTabBox();
3241 return pRet;
3244 void SwTable::RegisterToFormat( SwFormat& rFormat )
3246 rFormat.Add(*this);
3249 bool SwTable::HasLayout() const
3251 const SwFrameFormat* pFrameFormat = GetFrameFormat();
3252 //a table in a clipboard document doesn't have any layout information
3253 return pFrameFormat && SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
3256 void SwTableBox::RegisterToFormat( SwFormat& rFormat )
3258 rFormat.Add(*this);
3261 // free's any remaining child objects
3262 SwTableLines::~SwTableLines()
3264 for ( const_iterator it = begin(); it != end(); ++it )
3265 delete *it;
3268 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */