Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / table / swtable.cxx
blobff06bf510195646f8536296cc60f668f142a18fc
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 OUString& 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" " );
108 return rText;
111 static OUString& lcl_DelTabsAtSttEnd( OUString& rText )
113 sal_Unicode c;
114 sal_Int32 n;
115 OUStringBuffer sBuff(rText);
117 for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n )
119 if( '\x9' == c )
120 sBuff.remove( n--, 1 );
122 for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); )
124 if( '\x9' == c )
125 sBuff.remove( n, 1 );
127 rText = sBuff.makeStringAndClear();
128 return rText;
131 void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd,
132 SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat,
133 SwTableBox* pBox,
134 sal_uInt16 nInsPos, sal_uInt16 nCnt )
136 OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" );
137 SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
138 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
139 if( !pCNd )
140 pCNd = rDoc.GetNodes().GoNext( &aIdx );
141 OSL_ENSURE( pCNd, "Box with no content node" );
143 if( pCNd->IsTextNode() )
145 if( pCNd->GetpSwAttrSet() )
147 SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() );
148 if(pCNd->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT))
150 SwFormatAutoFormat format = aAttrSet.Get(RES_PARATR_LIST_AUTOFMT);
151 const std::shared_ptr<SfxItemSet>& handle = format.GetStyleHandle();
152 aAttrSet.Put(*handle);
154 if( pBox->GetSaveNumFormatColor() )
156 if( pBox->GetSaveUserColor() )
157 aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR ));
158 else
159 aAttrSet.ClearItem( RES_CHRATR_COLOR );
161 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
162 static_cast<SwTextNode*>(pCNd)->GetTextColl(),
163 &aAttrSet, nInsPos, nCnt );
165 else
166 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
167 static_cast<SwTextNode*>(pCNd)->GetTextColl(),
168 pCNd->GetpSwAttrSet(), nInsPos, nCnt );
170 else
171 rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
172 rDoc.GetDfltTextFormatColl(), nullptr,
173 nInsPos, nCnt );
175 sal_Int32 nRowSpan = pBox->getRowSpan();
176 if( nRowSpan != 1 )
178 SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
179 for( sal_uInt16 i = 0; i < nCnt; ++i )
181 pBox = rTableBoxes[ i + nInsPos ];
182 pBox->setRowSpan( nRowSpan );
187 SwTable::SwTable()
188 : SwClient( nullptr ),
189 m_pTableNode( nullptr ),
190 m_nGraphicsThatResize( 0 ),
191 m_nRowsToRepeat( 1 ),
192 m_bModifyLocked( false ),
193 m_bNewModel( true )
195 // default value set in the options
196 m_eTableChgMode = GetTableChgDefaultMode();
199 SwTable::SwTable( const SwTable& rTable )
200 : SwClient( rTable.GetFrameFormat() ),
201 m_pTableNode( nullptr ),
202 m_eTableChgMode( rTable.m_eTableChgMode ),
203 m_nGraphicsThatResize( 0 ),
204 m_nRowsToRepeat( rTable.GetRowsToRepeat() ),
205 maTableStyleName(rTable.maTableStyleName),
206 m_bModifyLocked( false ),
207 m_bNewModel( rTable.m_bNewModel )
211 void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes )
213 for (size_t n = 0; n < rSortCntBoxes.size(); ++n)
215 rSortCntBoxes[ n ]->m_pStartNode = nullptr;
219 SwTable::~SwTable()
221 if( m_xRefObj.is() )
223 SwDoc* pDoc = GetFrameFormat()->GetDoc();
224 if( !pDoc->IsInDtor() ) // then remove from the list
225 pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() );
227 m_xRefObj->Closed();
230 // the table can be deleted if it's the last client of the FrameFormat
231 SwTableFormat* pFormat = GetFrameFormat();
232 pFormat->Remove( this ); // remove
234 if( !pFormat->HasWriterListeners() )
235 pFormat->GetDoc()->DelTableFrameFormat( pFormat ); // and delete
237 // Delete the pointers from the SortArray of the boxes. The objects
238 // are preserved and are deleted by the lines/boxes arrays dtor.
239 // Note: unfortunately not enough, pointers to the StartNode of the
240 // section need deletion.
241 DelBoxNode(m_TabSortContentBoxes);
242 m_TabSortContentBoxes.clear();
245 namespace
248 template<class T>
249 T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD)
251 assert(nD != 0);
252 return nD == 0 ? static_cast<T>(nA*nM) : static_cast<T>((nA*nM)/nD);
257 static void FormatInArr( std::vector<SwFormat*>& rFormatArr, SwFormat* pBoxFormat )
259 std::vector<SwFormat*>::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat );
260 if ( it == rFormatArr.end() )
261 rFormatArr.push_back( pBoxFormat );
264 static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
265 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr );
267 static void lcl_ModifyLines( SwTableLines &rLines, const tools::Long nOld,
268 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr, const bool bCheckSum )
270 for ( auto &rLine : rLines)
271 ::lcl_ModifyBoxes( rLine->GetTabBoxes(), nOld, nNew, rFormatArr );
272 if( bCheckSum )
274 for(SwFormat* pFormat : rFormatArr)
276 const SwTwips nBox = lcl_MulDiv64<SwTwips>(pFormat->GetFrameSize().GetWidth(), nNew, nOld);
277 SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 );
278 pFormat->LockModify();
279 pFormat->SetFormatAttr( aNewBox );
280 pFormat->UnlockModify();
285 static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
286 const tools::Long nNew, std::vector<SwFormat*>& rFormatArr )
288 sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths
289 sal_uInt64 nOriginalSum = 0; // Sum of original widths
290 for ( size_t i = 0; i < rBoxes.size(); ++i )
292 SwTableBox &rBox = *rBoxes[i];
293 if ( !rBox.GetTabLines().empty() )
295 // For SubTables the rounding problem will not be solved :-(
296 ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false );
298 // Adjust the box
299 SwFrameFormat *pFormat = rBox.GetFrameFormat();
300 sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth();
301 nOriginalSum += nBox;
302 nBox = lcl_MulDiv64<sal_uInt64>(nBox, nNew, nOld);
303 const sal_uInt64 nWishedSum = lcl_MulDiv64<sal_uInt64>(nOriginalSum, nNew, nOld) - nSum;
304 if( nWishedSum > 0 )
306 if( nBox == nWishedSum )
307 FormatInArr( rFormatArr, pFormat );
308 else
310 nBox = nWishedSum;
311 pFormat = rBox.ClaimFrameFormat();
312 SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 );
313 pFormat->LockModify();
314 pFormat->SetFormatAttr( aNewBox );
315 pFormat->UnlockModify();
318 else {
319 OSL_FAIL( "Rounding error" );
321 nSum += nBox;
325 void SwTable::SwClientNotify(const SwModify&, const SfxHint& rHint)
327 if (rHint.GetId() != SfxHintId::SwLegacyModify)
328 return;
329 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
330 // catch SSize changes, to adjust the lines/boxes
331 const sal_uInt16 nWhich = pLegacy->GetWhich();
332 const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
333 switch(nWhich)
335 case RES_ATTRSET_CHG:
337 if (pLegacy->m_pOld && pLegacy->m_pNew
338 && (pNewSize = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet()->GetItemIfSet(
339 RES_FRM_SIZE,
340 false)))
342 pOldSize = &static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->GetFrameSize();
345 break;
346 case RES_FRM_SIZE:
348 pOldSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pOld);
349 pNewSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew);
351 break;
352 default:
353 CheckRegistration(pLegacy->m_pOld);
355 if (pOldSize && pNewSize && !m_bModifyLocked)
356 AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
359 void SwTable::AdjustWidths( const tools::Long nOld, const tools::Long nNew )
361 std::vector<SwFormat*> aFormatArr;
362 aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() );
363 ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true );
366 static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos )
368 for ( size_t i = 0; i < rToFill.Count(); ++i )
370 if ( std::abs(static_cast<tools::Long>(nPos) - rToFill[i]) <= COLFUZZY )
372 rToFill.SetHidden( i, false );
373 break;
378 static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox,
379 const SwFrameFormat *pTabFormat, const bool bHidden,
380 const bool bRefreshHidden )
382 const tools::Long nWish = pTabFormat->GetFrameSize().GetWidth();
383 OSL_ENSURE(nWish, "weird <= 0 width frmfrm");
385 // The value for the left edge of the box is calculated from the
386 // widths of the previous boxes.
387 tools::Long nPos = 0;
388 tools::Long nLeftMin = 0;
389 tools::Long nRightMax = 0;
390 if (nWish != 0) //fdo#33012 0 width frmfmt
392 SwTwips nSum = 0;
393 const SwTableBox *pCur = pBox;
394 const SwTableLine *pLine = pBox->GetUpper();
395 const tools::Long nAct = rToFill.GetRight() - rToFill.GetLeft(); // +1 why?
397 while ( pLine )
399 const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
400 for ( size_t i = 0; i < rBoxes.size(); ++i )
402 const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
403 nSum += nWidth;
404 const tools::Long nTmp = lcl_MulDiv64<tools::Long>(nSum, nAct, nWish);
406 if (rBoxes[i] != pCur)
408 if ( pLine == pBox->GetUpper() || 0 == nLeftMin )
409 nLeftMin = nTmp - nPos;
410 nPos = nTmp;
412 else
414 nSum -= nWidth;
415 if ( 0 == nRightMax )
416 nRightMax = nTmp - nPos;
417 break;
420 pCur = pLine->GetUpper();
421 pLine = pCur ? pCur->GetUpper() : nullptr;
425 bool bInsert = !bRefreshHidden;
426 for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j )
428 tools::Long nCmp = rToFill[j];
429 if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
430 (nPos <= (nCmp + COLFUZZY)) )
432 bInsert = false; // Already has it.
434 else if ( nPos < nCmp )
436 bInsert = false;
437 rToFill.Insert( nPos, bHidden, j );
440 if ( bInsert )
441 rToFill.Insert( nPos, bHidden, rToFill.Count() );
442 else if ( bRefreshHidden )
443 ::lcl_RefreshHidden( rToFill, nPos );
445 if ( !bHidden || bRefreshHidden )
446 return;
448 // calculate minimum/maximum values for the existing entries:
449 nLeftMin = nPos - nLeftMin;
450 nRightMax = nPos + nRightMax;
452 // check if nPos is entry:
453 bool bFoundPos = false;
454 bool bFoundMax = false;
455 for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j )
457 SwTabColsEntry& rEntry = rToFill.GetEntry( j );
458 tools::Long nCmp = rToFill[j];
460 if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
461 (nPos <= (nCmp + COLFUZZY)) )
463 // check if nLeftMin is > old minimum for entry nPos:
464 const tools::Long nOldMin = rEntry.nMin;
465 if ( nLeftMin > nOldMin )
466 rEntry.nMin = nLeftMin;
467 // check if nRightMin is < old maximum for entry nPos:
468 const tools::Long nOldMax = rEntry.nMax;
469 if ( nRightMax < nOldMax )
470 rEntry.nMax = nRightMax;
472 bFoundPos = true;
474 else if ( (nRightMax >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
475 (nRightMax <= (nCmp + COLFUZZY)) )
477 // check if nPos is > old minimum for entry nRightMax:
478 const tools::Long nOldMin = rEntry.nMin;
479 if ( nPos > nOldMin )
480 rEntry.nMin = nPos;
482 bFoundMax = true;
487 static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill,
488 const SwFrameFormat *pTabFormat, bool bRefreshHidden )
490 if ( !pBox->GetTabLines().empty() )
492 const SwTableLines &rLines = pBox->GetTabLines();
493 for ( size_t i = 0; i < rLines.size(); ++i )
495 const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
496 for ( size_t j = 0; j < rBoxes.size(); ++j )
497 ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden);
500 else
501 ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden );
504 static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill,
505 const SwFrameFormat *pTabFormat )
507 for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i )
509 const SwTableBox *pBox = pLine->GetTabBoxes()[i];
510 if ( pBox->GetSttNd() )
511 ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, true, false );
512 else
513 for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j )
514 ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat );
518 void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart,
519 bool bRefreshHidden, bool bCurRowOnly ) const
521 // Optimization: if bHidden is set, we only update the Hidden Array.
522 if ( bRefreshHidden )
524 // remove corrections
525 for ( size_t i = 0; i < rToFill.Count(); ++i )
527 SwTabColsEntry& rEntry = rToFill.GetEntry( i );
528 rEntry.nPos -= rToFill.GetLeft();
529 rEntry.nMin -= rToFill.GetLeft();
530 rEntry.nMax -= rToFill.GetLeft();
533 // All are hidden, so add the visible ones.
534 for ( size_t i = 0; i < rToFill.Count(); ++i )
535 rToFill.SetHidden( i, true );
537 else
539 rToFill.Remove( 0, rToFill.Count() );
542 // Insertion cases:
543 // 1. All boxes which are inferior to Line which is superior to the Start,
544 // as well as their inferior boxes if present.
545 // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors.
546 // 3. Apply 2. to the Line superior to the chain of boxes,
547 // until the Line's superior is not a box but the table.
548 // Only those boxes are inserted that don't contain further rows. The insertion
549 // function takes care to avoid duplicates. In order to achieve this, we work
550 // with some degree of fuzzyness (to avoid rounding errors).
551 // Only the left edge of the boxes are inserted.
552 // Finally, the first entry is removed again, because it's already
553 // covered by the border.
554 // 4. Scan the table again and insert _all_ boxes, this time as hidden.
556 const SwFrameFormat *pTabFormat = GetFrameFormat();
558 // 1.
559 const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
561 for ( size_t i = 0; i < rBoxes.size(); ++i )
562 ::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden );
564 // 2. and 3.
565 const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
566 pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
567 while ( pLine )
569 const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
570 for ( size_t k = 0; k < rBoxes2.size(); ++k )
571 ::lcl_SortedTabColInsert( rToFill, rBoxes2[k],
572 pTabFormat, false, bRefreshHidden );
573 pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
576 if ( !bRefreshHidden )
578 // 4.
579 if ( !bCurRowOnly )
581 for ( size_t i = 0; i < m_aLines.size(); ++i )
582 ::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat );
585 rToFill.Remove( 0 );
588 // Now the coordinates are relative to the left table border - i.e.
589 // relative to SwTabCols.nLeft. However, they are expected
590 // relative to the left document border, i.e. SwTabCols.nLeftMin.
591 // So all values need to be extended by nLeft.
592 for ( size_t i = 0; i < rToFill.Count(); ++i )
594 SwTabColsEntry& rEntry = rToFill.GetEntry( i );
595 rEntry.nPos += rToFill.GetLeft();
596 rEntry.nMin += rToFill.GetLeft();
597 rEntry.nMax += rToFill.GetLeft();
601 // Structure for parameter passing
602 struct Parm
604 const SwTabCols &rNew;
605 const SwTabCols &rOld;
606 tools::Long nNewWish,
607 nOldWish;
608 std::deque<SwTableBox*> aBoxArr;
609 SwShareBoxFormats aShareFormats;
611 Parm( const SwTabCols &rN, const SwTabCols &rO )
612 : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0)
616 static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm );
618 static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm )
620 SwTableBoxes &rBoxes = pLine->GetTabBoxes();
621 for ( size_t i = rBoxes.size(); i > 0; )
623 --i;
624 ::lcl_ProcessBoxSet( rBoxes[i], rParm );
628 static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm )
630 if ( !pBox->GetTabLines().empty() )
632 SwTableLines &rLines = pBox->GetTabLines();
633 for ( size_t i = rLines.size(); i > 0; )
635 --i;
636 lcl_ProcessLine( rLines[i], rParm );
639 else
641 // Search the old TabCols for the current position (calculate from
642 // left and right edge). Adjust the box if the values differ from
643 // the new TabCols. If the adjusted edge has no neighbour we also
644 // adjust all superior boxes.
646 const tools::Long nOldAct = rParm.rOld.GetRight() -
647 rParm.rOld.GetLeft(); // +1 why?
649 // The value for the left edge of the box is calculated from the
650 // widths of the previous boxes plus the left edge.
651 tools::Long nLeft = rParm.rOld.GetLeft();
652 const SwTableBox *pCur = pBox;
653 const SwTableLine *pLine = pBox->GetUpper();
655 while ( pLine )
657 const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
658 for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i)
660 nLeft += lcl_MulDiv64<tools::Long>(
661 rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(),
662 nOldAct, rParm.nOldWish);
664 pCur = pLine->GetUpper();
665 pLine = pCur ? pCur->GetUpper() : nullptr;
667 tools::Long nLeftDiff = 0;
668 tools::Long nRightDiff = 0;
669 if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this.
671 // Right edge is left edge plus width.
672 const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
673 pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
674 nOldAct, rParm.nOldWish);
675 const tools::Long nRight = nLeft + nWidth;
676 size_t nLeftPos = 0;
677 size_t nRightPos = 0;
678 bool bFoundLeftPos = false;
679 bool bFoundRightPos = false;
680 for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
682 if ( nLeft >= (rParm.rOld[i] - COLFUZZY) &&
683 nLeft <= (rParm.rOld[i] + COLFUZZY) )
685 nLeftPos = i;
686 bFoundLeftPos = true;
688 else if ( nRight >= (rParm.rOld[i] - COLFUZZY) &&
689 nRight <= (rParm.rOld[i] + COLFUZZY) )
691 nRightPos = i;
692 bFoundRightPos = true;
695 nLeftDiff = bFoundLeftPos ?
696 rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0;
697 nRightDiff= bFoundRightPos ?
698 rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0;
700 else // The first box.
702 nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft();
703 if ( rParm.rOld.Count() )
705 // Calculate the difference to the edge touching the first box.
706 const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
707 pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
708 nOldAct, rParm.nOldWish);
709 const tools::Long nTmp = nWidth + rParm.rOld.GetLeft();
710 for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
712 if ( nTmp >= (rParm.rOld[i] - COLFUZZY) &&
713 nTmp <= (rParm.rOld[i] + COLFUZZY) )
715 nRightDiff = rParm.rNew[i] - rParm.rOld[i];
716 break;
722 if( pBox->getRowSpan() == 1 )
724 const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox );
725 SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes();
726 if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 )
727 nLeftDiff = 0;
728 if( nPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) &&
729 rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 )
730 nRightDiff = 0;
732 else
733 nLeftDiff = nRightDiff = 0;
735 if ( nLeftDiff || nRightDiff )
737 // The difference is the actual difference amount. For stretched
738 // tables, it does not make sense to adjust the attributes of the
739 // boxes by this amount. The difference amount needs to be converted
740 // accordingly.
741 tools::Long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why?
742 nLeftDiff *= rParm.nNewWish;
743 nLeftDiff /= nTmp;
744 nRightDiff *= rParm.nNewWish;
745 nRightDiff /= nTmp;
746 tools::Long nDiff = nLeftDiff + nRightDiff;
748 // Adjust the box and all superiors by the difference amount.
749 while ( pBox )
751 SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
752 aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
753 if ( aFormatFrameSize.GetWidth() < 0 )
754 aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() );
755 rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
757 // The outer cells of the last row are responsible to adjust a surrounding cell.
758 // Last line check:
759 if ( pBox->GetUpper()->GetUpper() &&
760 pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back())
762 pBox = nullptr;
764 else
766 // Middle cell check:
767 if ( pBox != pBox->GetUpper()->GetTabBoxes().front() )
768 nDiff = nRightDiff;
770 if ( pBox != pBox->GetUpper()->GetTabBoxes().back() )
771 nDiff -= nRightDiff;
773 pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr;
780 static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque<SwTableBox*> &rBoxArr,
781 bool bBefore )
783 if ( !pBox->GetTabLines().empty() )
785 const SwTableLines &rLines = pBox->GetTabLines();
786 for ( size_t i = 0; i < rLines.size(); ++i )
788 const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
789 for ( size_t j = 0; j < rBoxes.size(); ++j )
790 ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore );
793 else if ( bBefore )
794 rBoxArr.push_front( pBox );
795 else
796 rBoxArr.push_back( pBox );
799 static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm );
801 static void lcl_AdjustLines( SwTableLines &rLines, const tools::Long nDiff, Parm &rParm )
803 for ( size_t i = 0; i < rLines.size(); ++i )
805 SwTableBox *pBox = rLines[i]->GetTabBoxes()
806 [rLines[i]->GetTabBoxes().size()-1];
807 lcl_AdjustBox( pBox, nDiff, rParm );
811 static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm )
813 if ( !pBox->GetTabLines().empty() )
814 ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm );
816 // Adjust the size of the box.
817 SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
818 aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
820 rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
823 void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld,
824 const SwTableBox *pStart, bool bCurRowOnly )
826 CHECK_TABLE( *this )
828 SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // delete HTML-Layout
830 // FME: Made rOld const. The caller is responsible for passing correct
831 // values of rOld. Therefore we do not have to call GetTabCols anymore:
832 //GetTabCols( rOld, pStart );
834 Parm aParm( rNew, rOld );
836 OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.");
838 // Convert the edges. We need to adjust the size of the table and some boxes.
839 // For the size adjustment, we must not make use of the Modify, since that'd
840 // adjust all boxes, which we really don't want.
841 SwFrameFormat *pFormat = GetFrameFormat();
842 aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth();
843 if ( (rOld.GetLeft() != rNew.GetLeft()) ||
844 (rOld.GetRight()!= rNew.GetRight()) )
846 LockModify();
848 SvxLRSpaceItem aLR( pFormat->GetLRSpace() );
849 SvxShadowItem aSh( pFormat->GetShadow() );
851 SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT );
852 SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT );
854 aLR.SetLeft ( rNew.GetLeft() - nShLeft );
855 aLR.SetRight( rNew.GetRightMax() - rNew.GetRight() - nShRight );
856 pFormat->SetFormatAttr( aLR );
858 // The alignment of the table needs to be adjusted accordingly.
859 // This is done by preserving the exact positions that have been
860 // set by the user.
861 SwFormatHoriOrient aOri( pFormat->GetHoriOrient() );
862 if( text::HoriOrientation::NONE != aOri.GetHoriOrient() &&
863 text::HoriOrientation::CENTER != aOri.GetHoriOrient() )
865 const bool bLeftDist = rNew.GetLeft() != nShLeft;
866 const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax();
867 if(!bLeftDist && !bRightDist)
868 aOri.SetHoriOrient( text::HoriOrientation::FULL );
869 else if(!bRightDist && rNew.GetLeft() > nShLeft )
870 aOri.SetHoriOrient( text::HoriOrientation::RIGHT );
871 else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax())
872 aOri.SetHoriOrient( text::HoriOrientation::LEFT );
873 else
875 // if an automatic table hasn't (really) changed size, then leave it as auto.
876 const tools::Long nOldWidth = rOld.GetRight() - rOld.GetLeft();
877 const tools::Long nNewWidth = rNew.GetRight() - rNew.GetLeft();
878 if (aOri.GetHoriOrient() != text::HoriOrientation::FULL
879 || std::abs(nOldWidth - nNewWidth) > COLFUZZY)
881 aOri.SetHoriOrient(text::HoriOrientation::LEFT_AND_WIDTH);
885 pFormat->SetFormatAttr( aOri );
887 const tools::Long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why?
888 tools::Long nTabDiff = 0;
890 if ( rOld.GetLeft() != rNew.GetLeft() )
892 nTabDiff = rOld.GetLeft() - rNew.GetLeft();
893 nTabDiff *= aParm.nOldWish;
894 nTabDiff /= nAct;
896 if ( rOld.GetRight() != rNew.GetRight() )
898 tools::Long nDiff = rNew.GetRight() - rOld.GetRight();
899 nDiff *= aParm.nOldWish;
900 nDiff /= nAct;
901 nTabDiff += nDiff;
902 if( !IsNewModel() )
903 ::lcl_AdjustLines( GetTabLines(), nDiff, aParm );
906 // Adjust the size of the table, watch out for stretched tables.
907 if ( nTabDiff )
909 aParm.nNewWish += nTabDiff;
910 if ( aParm.nNewWish < 0 )
911 aParm.nNewWish = USHRT_MAX; // Oops! Have to roll back.
912 SwFormatFrameSize aSz( pFormat->GetFrameSize() );
913 if ( aSz.GetWidth() != aParm.nNewWish )
915 aSz.SetWidth( aParm.nNewWish );
916 aSz.SetWidthPercent( 0 );
917 pFormat->SetFormatAttr( aSz );
920 UnlockModify();
923 if( IsNewModel() )
924 NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly );
925 else
927 if ( bCurRowOnly )
929 // To adjust the current row, we need to process all its boxes,
930 // similar to the filling of the TabCols (see GetTabCols()).
931 // Unfortunately we again have to take care to adjust the boxes
932 // from back to front, respectively from outer to inner.
933 // The best way to achieve this is probably to track the boxes
934 // in a PtrArray.
935 const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
936 for ( size_t i = 0; i < rBoxes.size(); ++i )
937 ::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false );
939 const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
940 pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
941 const SwTableBox *pExcl = pStart->GetUpper()->GetUpper();
942 while ( pLine )
944 const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
945 bool bBefore = true;
946 for ( size_t i = 0; i < rBoxes2.size(); ++i )
948 if ( rBoxes2[i] != pExcl )
949 ::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore );
950 else
951 bBefore = false;
953 pExcl = pLine->GetUpper();
954 pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
956 // After we've inserted a bunch of boxes (hopefully all and in
957 // correct order), we just need to process them in reverse order.
958 for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j )
960 SwTableBox *pBox = aParm.aBoxArr[j];
961 ::lcl_ProcessBoxSet( pBox, aParm );
964 else
966 // Adjusting the entire table is 'easy'. All boxes without lines are
967 // adjusted, as are their superiors. Of course we need to process
968 // in reverse order to prevent fooling ourselves!
969 SwTableLines &rLines = GetTabLines();
970 for ( size_t i = rLines.size(); i > 0; )
972 --i;
973 ::lcl_ProcessLine( rLines[i], aParm );
978 #ifdef DBG_UTIL
980 // do some checking for correct table widths
981 SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth();
982 for (size_t n = 0; n < m_aLines.size(); ++n)
984 CheckBoxWidth( *m_aLines[ n ], nSize );
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 )
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 bool bGoOn = !aRowSpanPos.empty();
1227 sal_uInt16 j = nCurr;
1228 while( bGoOn )
1230 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[--j],
1231 rParm.nOldWish, nOldWidth, true );
1232 lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
1233 bGoOn = !aRowSpanPos.empty() && j > 0;
1235 aRowSpanPos.clear();
1237 if( nCurr+1 < o3tl::narrowing<sal_uInt16>(rLines.size()) )
1239 ChangeList aCopy;
1240 sal_uInt16 nPos = 0;
1241 for( const auto& rCop : aOldNew )
1243 aCopy.push_back( rCop );
1244 aRowSpanPos.push_back( nPos++ );
1246 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
1247 rParm.nOldWish, nOldWidth, false );
1248 bool bGoOn = !aRowSpanPos.empty();
1249 sal_uInt16 j = nCurr;
1250 while( bGoOn )
1252 lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j],
1253 rParm.nOldWish, nOldWidth, false );
1254 lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
1255 bGoOn = !aRowSpanPos.empty() && j+1 < o3tl::narrowing<sal_uInt16>(rLines.size());
1258 ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY );
1260 else
1262 for( size_t i = 0; i < rLines.size(); ++i )
1263 ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY );
1265 CHECK_TABLE( *this )
1268 // return the pointer of the box specified.
1269 static bool lcl_IsValidRowName( std::u16string_view rStr )
1271 bool bIsValid = true;
1272 size_t nLen = rStr.size();
1273 for( size_t i = 0; i < nLen && bIsValid; ++i )
1275 const sal_Unicode cChar = rStr[i];
1276 if (cChar < '0' || cChar > '9')
1277 bIsValid = false;
1279 return bIsValid;
1282 // #i80314#
1283 // add 3rd parameter and its handling
1284 sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart,
1285 const bool bPerformValidCheck )
1287 sal_uInt16 nRet = 0;
1288 if( bFirstPart ) // true == column; false == row
1290 sal_Int32 nPos = 0;
1291 // the first one uses letters for addressing!
1292 bool bFirst = true;
1293 sal_uInt32 num = 0;
1294 bool overflow = false;
1295 while (nPos<rStr.getLength())
1297 sal_Unicode cChar = rStr[nPos];
1298 if ((cChar<'A' || cChar>'Z') && (cChar<'a' || cChar>'z'))
1299 break;
1300 cChar -= 'A';
1301 if( cChar >= 26 )
1302 cChar -= 'a' - '[';
1303 if( bFirst )
1304 bFirst = false;
1305 else
1306 ++num;
1307 num = num * 52 + cChar;
1308 if (num > SAL_MAX_UINT16) {
1309 overflow = true;
1311 ++nPos;
1313 nRet = overflow ? SAL_MAX_UINT16 : num;
1314 rStr = rStr.copy( nPos ); // Remove char from String
1316 else
1318 const sal_Int32 nPos = rStr.indexOf( "." );
1319 if ( nPos<0 )
1321 nRet = 0;
1322 if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) )
1324 nRet = o3tl::narrowing<sal_uInt16>(rStr.toInt32());
1326 rStr.clear();
1328 else
1330 nRet = 0;
1331 const std::u16string_view aText( rStr.subView( 0, nPos ) );
1332 if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) )
1334 nRet = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(aText));
1336 rStr = rStr.copy( nPos+1 );
1339 return nRet;
1342 // #i80314#
1343 // add 2nd parameter and its handling
1344 const SwTableBox* SwTable::GetTableBox( const OUString& rName,
1345 const bool bPerformValidCheck ) const
1347 const SwTableBox* pBox = nullptr;
1348 const SwTableLine* pLine;
1349 const SwTableLines* pLines;
1351 sal_uInt16 nLine, nBox;
1352 OUString aNm( rName );
1353 while( !aNm.isEmpty() )
1355 nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck );
1356 // first box ?
1357 if( !pBox )
1358 pLines = &GetTabLines();
1359 else
1361 pLines = &pBox->GetTabLines();
1362 if( nBox )
1363 --nBox;
1366 nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck );
1368 // determine line
1369 if( !nLine || nLine > pLines->size() )
1370 return nullptr;
1371 pLine = (*pLines)[ nLine-1 ];
1373 // determine box
1374 const SwTableBoxes* pBoxes = &pLine->GetTabBoxes();
1375 if( nBox >= pBoxes->size() )
1376 return nullptr;
1377 pBox = (*pBoxes)[ nBox ];
1380 // check if the box found has any contents
1381 if( pBox && !pBox->GetSttNd() )
1383 OSL_FAIL( "Box without content, looking for the next one!" );
1384 // "drop this" until the first box
1385 while( !pBox->GetTabLines().empty() )
1386 pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
1388 return pBox;
1391 SwTableBox* SwTable::GetTableBox( SwNodeOffset nSttIdx )
1393 // For optimizations, don't always process the entire SortArray.
1394 // Converting text to table, tries certain conditions
1395 // to ask for a table box of a table that is not yet having a format
1396 if(!GetFrameFormat())
1397 return nullptr;
1398 SwTableBox* pRet = nullptr;
1399 SwNodes& rNds = GetFrameFormat()->GetDoc()->GetNodes();
1400 SwNodeOffset nIndex = nSttIdx + 1;
1401 SwContentNode* pCNd = nullptr;
1402 SwTableNode* pTableNd = nullptr;
1404 while ( nIndex < rNds.Count() )
1406 pTableNd = rNds[ nIndex ]->GetTableNode();
1407 if ( pTableNd )
1408 break;
1410 pCNd = rNds[ nIndex ]->GetContentNode();
1411 if ( pCNd )
1412 break;
1414 ++nIndex;
1417 if ( pCNd || pTableNd )
1419 sw::BroadcastingModify* pModify = pCNd;
1420 // #144862# Better handling of table in table
1421 if ( pTableNd && pTableNd->GetTable().GetFrameFormat() )
1422 pModify = pTableNd->GetTable().GetFrameFormat();
1424 SwFrame* pFrame = pModify ? SwIterator<SwFrame,sw::BroadcastingModify>(*pModify).First() : nullptr;
1425 while ( pFrame && !pFrame->IsCellFrame() )
1426 pFrame = pFrame->GetUpper();
1427 if ( pFrame )
1428 pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1431 // In case the layout doesn't exist yet or anything else goes wrong.
1432 if ( !pRet )
1434 for (size_t n = m_TabSortContentBoxes.size(); n; )
1436 if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx)
1438 return m_TabSortContentBoxes[ n ];
1442 return pRet;
1445 bool SwTable::IsTableComplex() const
1447 // Returns true for complex tables, i.e. tables that contain nestings,
1448 // like containing boxes not part of the first line, e.g. results of
1449 // splits/merges which lead to more complex structures.
1450 for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n)
1452 if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper())
1454 return true;
1457 return false;
1460 SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes,
1461 SwTableBox *pUp )
1462 : SwClient( pFormat )
1463 , m_pUpper( pUp )
1464 , m_eRedlineType( RedlineType::None )
1466 m_aBoxes.reserve( nBoxes );
1469 SwTableLine::~SwTableLine()
1471 for (size_t i = 0; i < m_aBoxes.size(); ++i)
1473 delete m_aBoxes[i];
1475 // the TabelleLine can be deleted if it's the last client of the FrameFormat
1476 sw::BroadcastingModify* pMod = GetFrameFormat();
1477 pMod->Remove( this ); // remove,
1478 if( !pMod->HasWriterListeners() )
1479 delete pMod; // and delete
1482 SwFrameFormat* SwTableLine::ClaimFrameFormat()
1484 // This method makes sure that this object is an exclusive SwTableLine client
1485 // of an SwTableLineFormat object
1486 // If other SwTableLine objects currently listen to the same SwTableLineFormat as
1487 // this one, something needs to be done
1488 SwTableLineFormat *pRet = static_cast<SwTableLineFormat*>(GetFrameFormat());
1489 SwIterator<SwTableLine,SwFormat> aIter( *pRet );
1490 for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() )
1492 if ( pLast != this )
1494 // found another SwTableLine that is a client of the current Format
1495 // create a new Format as a copy and use it for this object
1496 SwTableLineFormat *pNewFormat = pRet->GetDoc()->MakeTableLineFormat();
1497 *pNewFormat = *pRet;
1499 // register SwRowFrames that know me as clients at the new Format
1500 SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet );
1501 for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() )
1502 if( pFrame->GetTabLine() == this )
1503 pFrame->RegisterToFormat( *pNewFormat );
1505 // register myself
1506 pNewFormat->Add( this );
1507 pRet = pNewFormat;
1508 break;
1512 return pRet;
1515 void SwTableLine::ChgFrameFormat(SwTableLineFormat* pNewFormat)
1517 auto pOld = GetFrameFormat();
1518 pOld->CallSwClientNotify(sw::TableLineFormatChanged(*pNewFormat, *this));
1519 // Now, re-register self.
1520 pNewFormat->Add(this);
1521 if(!pOld->HasWriterListeners())
1522 delete pOld;
1525 SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const
1527 SwTwips nRet = 0;
1528 bLayoutAvailable = false;
1529 SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
1530 // A row could appear several times in headers/footers so only one chain of master/follow tables
1531 // will be accepted...
1532 const SwTabFrame* pChain = nullptr; // My chain
1533 for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
1535 if (pLast->GetTabLine() != this)
1536 continue;
1538 const SwTabFrame* pTab = pLast->FindTabFrame();
1539 if (!pTab)
1540 continue;
1542 bLayoutAvailable = ( pTab->IsVertical() ) ?
1543 ( 0 < pTab->getFrameArea().Height() ) :
1544 ( 0 < pTab->getFrameArea().Width() );
1546 // The first one defines the chain, if a chain is defined, only members of the chain
1547 // will be added.
1548 if (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain))
1550 pChain = pTab; // defines my chain (even it is already)
1551 if( pTab->IsVertical() )
1552 nRet += pLast->getFrameArea().Width();
1553 else
1554 nRet += pLast->getFrameArea().Height();
1555 // Optimization, if there are no master/follows in my chain, nothing more to add
1556 if( !pTab->HasFollow() && !pTab->IsFollow() )
1557 break;
1558 // This is not an optimization, this is necessary to avoid double additions of
1559 // repeating rows
1560 if( pTab->IsInHeadline(*pLast) )
1561 break;
1564 return nRet;
1567 bool SwTableLine::IsEmpty() const
1569 for (size_t i = 0; i < m_aBoxes.size(); ++i)
1571 if ( !m_aBoxes[i]->IsEmpty() )
1572 return false;
1574 return true;
1577 bool SwTable::IsEmpty() const
1579 for (size_t i = 0; i < m_aLines.size(); ++i)
1581 if ( !m_aLines[i]->IsEmpty() )
1582 return false;
1584 return true;
1587 bool SwTable::HasDeletedRowOrCell() const
1589 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1590 if ( aRedlineTable.empty() )
1591 return false;
1593 SwRedlineTable::size_type nRedlinePos = 0;
1594 for (size_t i = 0; i < m_aLines.size(); ++i)
1596 // has a deleted row
1597 if ( m_aLines[i]->IsDeleted(nRedlinePos) )
1598 return true;
1600 // has a deleted cell in the not deleted row
1601 SwTableBoxes& rBoxes = m_aLines[i]->GetTabBoxes();
1602 for( size_t j = 0; j < rBoxes.size(); ++j )
1604 if ( RedlineType::Delete == rBoxes[j]->GetRedlineType() )
1605 return true;
1608 return false;
1611 bool SwTable::IsDeleted() const
1613 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1614 if ( aRedlineTable.empty() )
1615 return false;
1617 SwRedlineTable::size_type nRedlinePos = 0;
1618 for (size_t i = 0; i < m_aLines.size(); ++i)
1620 if ( !m_aLines[i]->IsDeleted(nRedlinePos) )
1621 return false;
1623 return true;
1626 void SwTable::GatherFormulas(std::vector<SwTableBoxFormula*>& rvFormulas)
1628 for(SfxPoolItem* pItem: GetFrameFormat()->GetDoc()->GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
1630 auto pBoxFormula = dynamic_cast<SwTableBoxFormula*>(pItem);
1631 assert(pBoxFormula); // use StaticWhichCast instead?
1632 if(!pBoxFormula->GetDefinedIn())
1633 continue;
1634 const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
1635 if(!pNd || &pNd->GetNodes() != &pNd->GetDoc().GetNodes()) // is this ever valid or should we assert here?
1636 continue;
1637 rvFormulas.push_back(pBoxFormula);
1641 void SwTable::Split(OUString sNewTableName, sal_uInt16 nSplitLine, SwHistory* pHistory)
1643 SwTableFormulaUpdate aHint(this);
1644 aHint.m_eFlags = TBL_SPLITTBL;
1645 aHint.m_aData.pNewTableNm = &sNewTableName;
1646 aHint.m_nSplitLine = nSplitLine;
1648 std::vector<SwTableBoxFormula*> vFormulas;
1649 GatherFormulas(vFormulas);
1650 for(auto pBoxFormula: vFormulas)
1652 const SwNode* pNd = pBoxFormula->GetNodeOfFormula();
1653 const SwTableNode* pTableNd = pNd->FindTableNode();
1654 if(pTableNd == nullptr)
1655 continue;
1656 if(&pTableNd->GetTable() == this)
1658 sal_uInt16 nLnPos = SwTableFormula::GetLnPosInTable(*this, pBoxFormula->GetTableBox());
1659 aHint.m_bBehindSplitLine = USHRT_MAX != nLnPos && aHint.m_nSplitLine <= nLnPos;
1661 else
1662 aHint.m_bBehindSplitLine = false;
1663 pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
1667 void SwTable::Merge(SwTable& rTable, SwHistory* pHistory)
1669 SwTableFormulaUpdate aHint(this);
1670 aHint.m_eFlags = TBL_MERGETBL;
1671 aHint.m_aData.pDelTable = &rTable;
1672 std::vector<SwTableBoxFormula*> vFormulas;
1673 GatherFormulas(vFormulas);
1674 for(auto pBoxFormula: vFormulas)
1675 pBoxFormula->ToSplitMergeBoxNmWithHistory(aHint, pHistory);
1678 void SwTable::UpdateFields(TableFormulaUpdateFlags eFlags)
1680 auto pDoc = GetFrameFormat()->GetDoc();
1681 auto pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Table, OUString(), false);
1682 if(!pFieldType)
1683 return;
1684 std::vector<SwFormatField*> vFields;
1685 pFieldType->GatherFields(vFields);
1686 for(auto pFormatField : vFields)
1688 SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField());
1689 // table where this field is located
1690 const SwTableNode* pTableNd;
1691 const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
1692 pTableNd = rTextNd.FindTableNode();
1693 if(pTableNd == nullptr || &pTableNd->GetTable() != this)
1694 continue;
1696 switch(eFlags)
1698 case TBL_BOXNAME:
1699 // to the external representation
1700 pField->PtrToBoxNm(this);
1701 break;
1702 case TBL_RELBOXNAME:
1703 // to the relative representation
1704 pField->ToRelBoxNm(this);
1705 break;
1706 case TBL_BOXPTR:
1707 // to the internal representation
1708 // JP 17.06.96: internal representation on all formulas
1709 // (reference to other table!!!)
1710 pField->BoxNmToPtr( &pTableNd->GetTable() );
1711 break;
1712 default:
1713 assert(false); // Only TBL_BOXNAME, TBL_RELBOXNAME and TBL_BOXPTR are supported
1714 break;
1717 // process all table box formulas
1718 for(const SfxPoolItem* pItem : pDoc->GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
1720 auto pBoxFormula = const_cast<SwTableBoxFormula*>(pItem->DynamicWhichCast(RES_BOXATR_FORMULA));
1721 if(pBoxFormula && pBoxFormula->GetDefinedIn())
1723 if(eFlags == TBL_BOXPTR)
1724 pBoxFormula->TryBoxNmToPtr();
1725 else if(eFlags == TBL_RELBOXNAME)
1726 pBoxFormula->ToRelBoxNm(this);
1727 else
1728 pBoxFormula->ChangeState();
1733 void SwTable::dumpAsXml(xmlTextWriterPtr pWriter) const
1735 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
1736 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1737 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("table-format"), "%p", GetFrameFormat());
1738 for (const auto& pLine : GetTabLines())
1740 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableLine"));
1741 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pLine);
1742 pLine->GetFrameFormat()->dumpAsXml(pWriter);
1743 (void)xmlTextWriterEndElement(pWriter);
1745 (void)xmlTextWriterEndElement(pWriter);
1748 // TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells.
1749 // At tracked row deletion, return with the newest deletion of the row or
1750 // at tracked row insertion, return with the oldest insertion in the row, which
1751 // contain the change data of the row change.
1752 // If the return value is SwRedlineTable::npos, there is no tracked row change.
1753 SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(
1754 SwRedlineTable::size_type& rRedlinePos, bool bUpdateProperty ) const
1756 SwRedlineTable::size_type nRet = SwRedlineTable::npos;
1757 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1759 // check table row property "HasTextChangesOnly", if it's defined and its
1760 // value is false, and all text content is in delete redlines, the row is deleted
1761 const SvxPrintItem *pHasTextChangesOnlyProp =
1762 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
1763 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
1765 const SwTableBoxes & rBoxes = GetTabBoxes();
1766 size_t nBoxes = rBoxes.size();
1767 bool bInsertion = false;
1768 bool bPlainTextInLine = false;
1769 SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
1770 SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos;
1771 for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex)
1773 auto pBox = rBoxes[nBoxIndex];
1774 if ( pBox->IsEmpty( /*bWithRemainingNestedTable =*/ false ) )
1776 // no text content, check the next cells
1777 continue;
1780 bool bHasRedlineInBox = false;
1781 SwPosition aCellStart( *pBox->GetSttNd(), SwNodeOffset(0) );
1782 SwPosition aCellEnd( *pBox->GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
1783 SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
1784 SwRangeRedline* pPreviousDeleteRedline = nullptr;
1785 for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos )
1787 const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ];
1789 if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
1791 // no more redlines in the actual cell,
1792 // check the next ones
1793 break;
1796 // redline in the cell
1797 if ( aCellStart <= *pRedline->Start() )
1799 if ( !bHasRedlineInBox )
1801 bHasRedlineInBox = true;
1802 // plain text before the first redline in the text
1803 if ( pRedline->Start()->GetContentIndex() > 0 )
1804 bPlainTextInLine = true;
1807 RedlineType nType = pRedline->GetType();
1809 // first insert redline
1810 if ( !bInsertion )
1812 if ( RedlineType::Insert == nType )
1814 bInsertion = true;
1816 else
1818 // plain text between the delete redlines
1819 if ( pPreviousDeleteRedline &&
1820 *pPreviousDeleteRedline->End() < *pRedline->Start() &&
1821 // in the same section, i.e. not in a nested table
1822 pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
1823 pRedline->Start()->nNode.GetNode().StartOfSectionNode() )
1825 bPlainTextInLine = true;
1827 pPreviousDeleteRedline = const_cast<SwRangeRedline*>(pRedline);
1831 // search newest and oldest redlines
1832 if ( nNewestRedline == SwRedlineTable::npos ||
1833 aRedlineTable[nNewestRedline]->GetRedlineData().GetTimeStamp() <
1834 pRedline->GetRedlineData().GetTimeStamp() )
1836 nNewestRedline = rRedlinePos;
1838 if ( nOldestRedline == SwRedlineTable::npos ||
1839 aRedlineTable[nOldestRedline]->GetRedlineData().GetTimeStamp() >
1840 pRedline->GetRedlineData().GetTimeStamp() )
1842 nOldestRedline = rRedlinePos;
1847 // there is text content outside of redlines: not a deletion
1848 if ( !bInsertion && ( !bHasRedlineInBox || ( pPreviousDeleteRedline &&
1849 // in the same cell, i.e. not in a nested table
1850 pPreviousDeleteRedline->End()->nNode.GetNode().StartOfSectionNode() ==
1851 aCellEnd.GetNode().StartOfSectionNode() &&
1852 ( pPreviousDeleteRedline->End()->GetNode() < aCellEnd.GetNode() ||
1853 pPreviousDeleteRedline->End()->GetContentIndex() <
1854 aCellEnd.GetNode().GetContentNode()->Len() ) ) ) )
1856 bPlainTextInLine = true;
1857 // not deleted cell content: the row is not empty
1858 // maybe insertion of a row, try to search it
1859 bInsertion = true;
1863 // choose return redline, if it exists or remove changed row attribute
1864 if ( bInsertion && SwRedlineTable::npos != nOldestRedline &&
1865 RedlineType::Insert == aRedlineTable[ nOldestRedline ]->GetType() )
1867 // there is an insert redline, which is the oldest redline in the row
1868 nRet = nOldestRedline;
1870 else if ( !bInsertion && !bPlainTextInLine && SwRedlineTable::npos != nNewestRedline &&
1871 RedlineType::Delete == aRedlineTable[ nNewestRedline ]->GetType() )
1873 // there is a delete redline, which is the newest redline in the row,
1874 // and no text outside of redlines, and no insert redline in the row,
1875 // i.e. whole text content is deleted
1876 nRet = nNewestRedline;
1878 else
1880 // no longer tracked row insertion or deletion
1881 nRet = SwRedlineTable::npos;
1882 // set TextChangesOnly = true to remove the tracked deletion
1883 // FIXME Undo is not supported here (this is only a fallback,
1884 // because using SetRowNotTracked() is not recommended here)
1885 if ( bUpdateProperty )
1887 SvxPrintItem aUnsetTracking(RES_PRINT, true);
1888 SwFrameFormat *pFormat = const_cast<SwTableLine*>(this)->ClaimFrameFormat();
1889 pFormat->LockModify();
1890 pFormat->SetFormatAttr( aUnsetTracking );
1891 pFormat->UnlockModify();
1896 // cache the result
1897 const_cast<SwTableLine*>(this)->SetRedlineType( SwRedlineTable::npos == nRet
1898 ? RedlineType::None
1899 : aRedlineTable[ nRet ]->GetType());
1901 return nRet;
1904 SwRedlineTable::size_type SwTableLine::GetTableRedline() const
1906 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1907 const SwStartNode* pFirstBox = GetTabBoxes().front()->GetSttNd();
1908 const SwStartNode* pLastBox = GetTabBoxes().back()->GetSttNd();
1910 // Box with no start node
1911 if ( !pFirstBox || !pLastBox )
1912 return SwRedlineTable::npos;
1914 const SwPosition aLineStart(*pFirstBox);
1915 const SwPosition aLineEnd(*pLastBox);
1916 SwRedlineTable::size_type n = 0;
1918 const SwRangeRedline* pFnd = aRedlineTable.FindAtPosition( aLineStart, n, /*next=*/false );
1919 if( pFnd && *pFnd->Start() < aLineStart && *pFnd->End() > aLineEnd )
1920 return n;
1922 return SwRedlineTable::npos;
1925 bool SwTableLine::IsTracked(SwRedlineTable::size_type& rRedlinePos, bool bOnlyDeleted) const
1927 SwRedlineTable::size_type nPos = UpdateTextChangesOnly(rRedlinePos);
1928 if ( nPos != SwRedlineTable::npos )
1930 const SwRedlineTable& aRedlineTable =
1931 GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1932 if ( RedlineType::Delete == aRedlineTable[nPos]->GetType() ||
1933 ( !bOnlyDeleted && RedlineType::Insert == aRedlineTable[nPos]->GetType() ) )
1934 return true;
1936 return false;
1939 bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const
1941 // if not a deleted row, check the deleted columns
1942 if ( !IsTracked(rRedlinePos, /*bOnlyDeleted=*/true) )
1944 const SwTableBoxes& rBoxes = GetTabBoxes();
1945 for( size_t i = 0; i < rBoxes.size(); ++i )
1947 // there is a not deleted column
1948 if ( rBoxes[i]->GetRedlineType() != RedlineType::Delete )
1949 return false;
1953 return true;
1956 RedlineType SwTableLine::GetRedlineType() const
1958 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1959 if ( aRedlineTable.empty() )
1960 return RedlineType::None;
1962 // check table row property "HasTextChangesOnly", if it's defined and its value is
1963 // false, return with the cached redline type, if it exists, otherwise calculate it
1964 const SvxPrintItem *pHasTextChangesOnlyProp =
1965 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
1966 if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
1968 if ( RedlineType::None != m_eRedlineType )
1969 return m_eRedlineType;
1971 SwRedlineTable::size_type nPos = 0;
1972 nPos = UpdateTextChangesOnly(nPos);
1973 if ( nPos != SwRedlineTable::npos )
1974 return aRedlineTable[nPos]->GetType();
1976 else if ( RedlineType::None != m_eRedlineType )
1977 // empty the cache
1978 const_cast<SwTableLine*>(this)->SetRedlineType( RedlineType::None );
1980 // is the whole table part of a changed text
1981 SwRedlineTable::size_type nTableRedline = GetTableRedline();
1982 if ( nTableRedline != SwRedlineTable::npos )
1983 return aRedlineTable[nTableRedline]->GetType();
1985 return RedlineType::None;
1988 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp )
1989 : SwClient(nullptr)
1990 , m_aLines()
1991 , m_pStartNode(nullptr)
1992 , m_pUpper(pUp)
1993 , mnRowSpan(1)
1994 , mbDummyFlag(false)
1995 , mbDirectFormatting(false)
1997 m_aLines.reserve( nLines );
1998 CheckBoxFormat( pFormat )->Add( this );
2001 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwNodeIndex &rIdx,
2002 SwTableLine *pUp )
2003 : SwClient(nullptr)
2004 , m_aLines()
2005 , m_pUpper(pUp)
2006 , mnRowSpan(1)
2007 , mbDummyFlag(false)
2008 , mbDirectFormatting(false)
2010 CheckBoxFormat( pFormat )->Add( this );
2012 m_pStartNode = rIdx.GetNode().GetStartNode();
2014 // insert into the table
2015 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2016 assert(pTableNd && "In which table is that box?");
2017 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2018 GetTabSortBoxes());
2019 SwTableBox* p = this; // error: &this
2020 rSrtArr.insert( p ); // insert
2023 SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwStartNode& rSttNd, SwTableLine *pUp )
2024 : SwClient(nullptr)
2025 , m_aLines()
2026 , m_pStartNode(&rSttNd)
2027 , m_pUpper(pUp)
2028 , mnRowSpan(1)
2029 , mbDummyFlag(false)
2030 , mbDirectFormatting(false)
2032 CheckBoxFormat( pFormat )->Add( this );
2034 // insert into the table
2035 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2036 OSL_ENSURE( pTableNd, "In which table is the box?" );
2037 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2038 GetTabSortBoxes());
2039 SwTableBox* p = this; // error: &this
2040 rSrtArr.insert( p ); // insert
2043 void SwTableBox::RemoveFromTable()
2045 if (m_pStartNode) // box containing contents?
2047 // remove from table
2048 const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
2049 assert(pTableNd && "In which table is that box?");
2050 SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
2051 GetTabSortBoxes());
2052 SwTableBox *p = this; // error: &this
2053 rSrtArr.erase( p ); // remove
2054 m_pStartNode = nullptr; // clear it so this is only run once
2058 SwTableBox::~SwTableBox()
2060 if (!GetFrameFormat()->GetDoc()->IsInDtor())
2062 RemoveFromTable();
2065 // the TabelleBox can be deleted if it's the last client of the FrameFormat
2066 sw::BroadcastingModify* pMod = GetFrameFormat();
2067 pMod->Remove( this ); // remove,
2068 if( !pMod->HasWriterListeners() )
2069 delete pMod; // and delete
2072 SwTableBoxFormat* SwTableBox::CheckBoxFormat( SwTableBoxFormat* pFormat )
2074 // We might need to create a new format here, because the box must be
2075 // added to the format solely if pFormat has a value or form.
2076 if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) ||
2077 SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ) )
2079 SwTableBox* pOther = SwIterator<SwTableBox,SwFormat>( *pFormat ).First();
2080 if( pOther )
2082 SwTableBoxFormat* pNewFormat = pFormat->GetDoc()->MakeTableBoxFormat();
2083 pNewFormat->LockModify();
2084 *pNewFormat = *pFormat;
2086 // Remove values and formulas
2087 pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
2088 pNewFormat->UnlockModify();
2090 pFormat = pNewFormat;
2093 return pFormat;
2096 SwFrameFormat* SwTableBox::ClaimFrameFormat()
2098 // This method makes sure that this object is an exclusive SwTableBox client
2099 // of an SwTableBoxFormat object
2100 // If other SwTableBox objects currently listen to the same SwTableBoxFormat as
2101 // this one, something needs to be done
2102 SwTableBoxFormat *pRet = static_cast<SwTableBoxFormat*>(GetFrameFormat());
2103 SwIterator<SwTableBox,SwFormat> aIter( *pRet );
2104 for( SwTableBox* pLast = aIter.First(); pLast; pLast = aIter.Next() )
2106 if ( pLast != this )
2108 // Found another SwTableBox object
2109 // create a new Format as a copy and assign me to it
2110 // don't copy values and formulas
2111 SwTableBoxFormat* pNewFormat = pRet->GetDoc()->MakeTableBoxFormat();
2112 pNewFormat->LockModify();
2113 *pNewFormat = *pRet;
2114 pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
2115 pNewFormat->UnlockModify();
2117 // re-register SwCellFrame objects that know me
2118 SwIterator<SwCellFrame,SwFormat> aFrameIter( *pRet );
2119 for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() )
2120 if( pCell->GetTabBox() == this )
2121 pCell->RegisterToFormat( *pNewFormat );
2123 // re-register myself
2124 pNewFormat->Add( this );
2125 pRet = pNewFormat;
2126 break;
2129 return pRet;
2132 void SwTableBox::ChgFrameFormat(SwTableBoxFormat* pNewFormat, bool bNeedToReregister)
2134 SwFrameFormat* pOld = GetFrameFormat();
2135 // tdf#84635 We set bNeedToReregister=false to avoid a quadratic slowdown on loading large tables,
2136 // and since we are creating the table for the first time, no re-registration is necessary.
2137 // First, re-register the Frames.
2138 if(bNeedToReregister)
2139 pOld->CallSwClientNotify(sw::TableBoxFormatChanged(*pNewFormat, *this));
2140 // Now, re-register self.
2141 pNewFormat->Add(this);
2142 if(!pOld->HasWriterListeners())
2143 delete pOld;
2146 // Return the name of this box. This is determined dynamically
2147 // resulting from the position in the lines/boxes/tables.
2148 void sw_GetTableBoxColStr( sal_uInt16 nCol, OUString& rNm )
2150 const sal_uInt16 coDiff = 52; // 'A'-'Z' 'a' - 'z'
2152 do {
2153 const sal_uInt16 nCalc = nCol % coDiff;
2154 if( nCalc >= 26 )
2155 rNm = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + rNm;
2156 else
2157 rNm = OUStringChar( sal_Unicode('A' + nCalc) ) + rNm;
2159 nCol = nCol - nCalc;
2160 if( 0 == nCol )
2161 break;
2162 nCol /= coDiff;
2163 --nCol;
2164 } while( true );
2167 Point SwTableBox::GetCoordinates() const
2169 if( !m_pStartNode ) // box without content?
2171 // search for the next first box?
2172 return Point( 0, 0 );
2175 const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
2176 sal_uInt16 nX, nY;
2177 const SwTableBox* pBox = this;
2178 do {
2179 const SwTableLine* pLine = pBox->GetUpper();
2180 // at the first level?
2181 const SwTableLines* pLines = pLine->GetUpper()
2182 ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
2184 nY = pLines->GetPos( pLine ) + 1 ;
2185 nX = pBox->GetUpper()->GetBoxPos( pBox ) + 1;
2186 pBox = pLine->GetUpper();
2187 } while( pBox );
2188 return Point( nX, nY );
2191 OUString SwTableBox::GetName() const
2193 if( !m_pStartNode ) // box without content?
2195 // search for the next first box?
2196 return OUString();
2199 const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
2200 sal_uInt16 nPos;
2201 OUString sNm, sTmp;
2202 const SwTableBox* pBox = this;
2203 do {
2204 const SwTableLine* pLine = pBox->GetUpper();
2205 // at the first level?
2206 const SwTableLines* pLines = pLine->GetUpper()
2207 ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
2209 nPos = pLines->GetPos( pLine ) + 1;
2210 sTmp = OUString::number( nPos );
2211 if( !sNm.isEmpty() )
2212 sNm = sTmp + "." + sNm;
2213 else
2214 sNm = sTmp;
2216 nPos = pBox->GetUpper()->GetBoxPos( pBox );
2217 sTmp = OUString::number(nPos + 1);
2218 pBox = pLine->GetUpper();
2219 if( nullptr != pBox )
2220 sNm = sTmp + "." + sNm;
2221 else
2222 sw_GetTableBoxColStr( nPos, sNm );
2224 } while( pBox );
2225 return sNm;
2228 bool SwTableBox::IsInHeadline( const SwTable* pTable ) const
2230 if( !GetUpper() ) // should only happen upon merge.
2231 return false;
2233 if( !pTable )
2234 pTable = &m_pStartNode->FindTableNode()->GetTable();
2236 const SwTableLine* pLine = GetUpper();
2237 while( pLine->GetUpper() )
2238 pLine = pLine->GetUpper()->GetUpper();
2240 // Headerline?
2241 return pTable->GetTabLines()[ 0 ] == pLine;
2244 SwNodeOffset SwTableBox::GetSttIdx() const
2246 return m_pStartNode ? m_pStartNode->GetIndex() : SwNodeOffset(0);
2249 bool SwTableBox::IsEmpty( bool bWithRemainingNestedTable ) const
2251 const SwStartNode *pSttNd = GetSttNd();
2253 if ( !pSttNd )
2254 return false;
2256 const SwNode * pFirstNode = pSttNd->GetNodes()[pSttNd->GetIndex() + 1];
2258 if ( pSttNd->GetIndex() + 2 == pSttNd->EndOfSectionIndex() )
2260 // single empty node in the box
2261 const SwContentNode *pCNd = pFirstNode->GetContentNode();
2262 if ( pCNd && !pCNd->Len() )
2263 return true;
2265 // tdf#157011 OOXML w:std cell content is imported with terminating 0x01 characters,
2266 // i.e. an empty box can contain double 0x01: handle it to avoid losing change tracking
2267 // FIXME regression since commit b5c616d10bff3213840d4893d13b4493de71fa56
2268 if ( pCNd && pCNd->Len() == 2 && pCNd->GetTextNode() )
2270 const OUString &rText = pCNd->GetTextNode()->GetText();
2271 if ( rText[0] == 0x01 && rText[1] == 0x01 )
2272 return true;
2275 else if ( bWithRemainingNestedTable )
2277 if ( const SwTableNode * pTableNode = pFirstNode->GetTableNode() )
2279 // empty nested table in the box and
2280 // no text content after it
2281 if ( pTableNode->EndOfSectionIndex() + 2 == pSttNd->EndOfSectionIndex() )
2282 return pTableNode->GetTable().IsEmpty();
2286 return false;
2289 // retrieve information from the client
2290 bool SwTable::GetInfo( SfxPoolItem& rInfo ) const
2292 switch( rInfo.Which() )
2294 case RES_AUTOFMT_DOCNODE:
2296 const SwTableNode* pNode = GetTableNode();
2297 if (pNode && &pNode->GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes)
2299 if (!m_TabSortContentBoxes.empty())
2301 SwNodeIndex aIdx( *m_TabSortContentBoxes[0]->GetSttNd() );
2302 GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx );
2304 return false;
2306 break;
2308 case RES_FINDNEARESTNODE:
2309 if( GetFrameFormat() &&
2310 GetFrameFormat()->GetFormatAttr( RES_PAGEDESC ).GetPageDesc() &&
2311 !m_TabSortContentBoxes.empty() &&
2312 m_TabSortContentBoxes[0]->GetSttNd()->GetNodes().IsDocNodes() )
2313 static_cast<SwFindNearestNode&>(rInfo).CheckNode( *
2314 m_TabSortContentBoxes[0]->GetSttNd()->FindTableNode() );
2315 break;
2317 return true;
2320 SwTable * SwTable::FindTable( SwFrameFormat const*const pFormat )
2322 return pFormat
2323 ? SwIterator<SwTable,SwFormat>(*pFormat).First()
2324 : nullptr;
2327 SwTableNode* SwTable::GetTableNode() const
2329 return !GetTabSortBoxes().empty() ?
2330 const_cast<SwTableNode*>(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) :
2331 m_pTableNode;
2334 void SwTable::SetRefObject( SwServerObject* pObj )
2336 if( m_xRefObj.is() )
2337 m_xRefObj->Closed();
2339 m_xRefObj = pObj;
2342 void SwTable::SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout> const& r)
2344 m_xHTMLLayout = r;
2347 static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
2348 bool bChgAlign )
2350 SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
2351 ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos);
2353 void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
2354 bool bChgAlign, SwNodeOffset nNdPos )
2357 if( NODE_OFFSET_MAX == nNdPos )
2358 return;
2360 SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
2361 SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
2363 // assign adjustment
2364 if( bChgAlign )
2366 const SfxPoolItem* pItem;
2367 pItem = &pTNd->SwContentNode::GetAttr( RES_PARATR_ADJUST );
2368 SvxAdjust eAdjust = static_cast<const SvxAdjustItem*>(pItem)->GetAdjust();
2369 if( SvxAdjust::Left == eAdjust || SvxAdjust::Block == eAdjust )
2371 SvxAdjustItem aAdjust( *static_cast<const SvxAdjustItem*>(pItem) );
2372 aAdjust.SetAdjust( SvxAdjust::Right );
2373 pTNd->SetAttr( aAdjust );
2377 // assign color or save "user color"
2378 const SvxColorItem* pColorItem = nullptr;
2379 if( pTNd->GetpSwAttrSet() )
2380 pColorItem = pTNd->GetpSwAttrSet()->GetItemIfSet( RES_CHRATR_COLOR, false );
2382 const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
2383 std::optional<Color> pNewUserColor;
2384 if (pColorItem)
2385 pNewUserColor = pColorItem->GetValue();
2387 if( ( pNewUserColor && pOldNumFormatColor &&
2388 *pNewUserColor == *pOldNumFormatColor ) ||
2389 ( !pNewUserColor && !pOldNumFormatColor ))
2391 // Keep the user color, set updated values, delete old NumFormatColor if needed
2392 if( pCol )
2393 // if needed, set the color
2394 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2395 else if( pColorItem )
2397 pNewUserColor = rBox.GetSaveUserColor();
2398 if( pNewUserColor )
2399 pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
2400 else
2401 pTNd->ResetAttr( RES_CHRATR_COLOR );
2404 else
2406 // Save user color, set NumFormat color if needed, but never reset the color
2407 rBox.SetSaveUserColor( pNewUserColor ? *pNewUserColor : std::optional<Color>() );
2409 if( pCol )
2410 // if needed, set the color
2411 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2414 rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
2416 if( pTNd->GetText() != rText )
2418 // Exchange text. Bugfix to keep Tabs (front and back!) and annotations (inword comment anchors)
2419 const OUString& rOrig = pTNd->GetText();
2420 sal_Int32 n;
2422 for( n = 0; n < rOrig.getLength() && ('\x9' == rOrig[n] || CH_TXTATR_INWORD == rOrig[n]); ++n )
2424 for( ; n < rOrig.getLength() && '\x01' == rOrig[n]; ++n )
2426 SwContentIndex aIdx( pTNd, n );
2427 for( n = rOrig.getLength(); n && ('\x9' == rOrig[--n] || CH_TXTATR_INWORD == rOrig[n]); )
2429 sal_Int32 nEndPos = n;
2430 n -= aIdx.GetIndex() - 1;
2432 // Reset DontExpand-Flags before exchange, to retrigger expansion
2434 SwContentIndex aResetIdx( aIdx, n );
2435 pTNd->DontExpandFormat( aResetIdx.GetIndex(), false, false );
2438 if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
2440 SwPaM aTemp(*pTNd, 0, *pTNd, rOrig.getLength());
2441 pDoc->getIDocumentRedlineAccess().DeleteRedline(aTemp, true, RedlineType::Any);
2444 // preserve comments inside of the number by deleting number portions starting from the back
2445 sal_Int32 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
2446 while( nCommentPos > aIdx.GetIndex() )
2448 pTNd->EraseText( SwContentIndex(pTNd, nCommentPos+1), nEndPos - nCommentPos, SwInsertFlags::EMPTYEXPAND );
2449 // find the next non-sequential comment anchor
2452 nEndPos = nCommentPos;
2453 n = nEndPos - aIdx.GetIndex();
2454 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
2455 --nEndPos;
2457 while( nCommentPos > aIdx.GetIndex() && nCommentPos == nEndPos );
2460 pTNd->EraseText( aIdx, n, SwInsertFlags::EMPTYEXPAND );
2461 pTNd->InsertText( rText, aIdx, SwInsertFlags::EMPTYEXPAND );
2463 if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
2465 SwPaM aTemp(*pTNd, 0, *pTNd, rText.getLength());
2466 pDoc->getIDocumentRedlineAccess().AppendRedline(new SwRangeRedline(RedlineType::Insert, aTemp), true);
2470 // assign vertical orientation
2471 const SwFormatVertOrient* pVertOrientItem;
2472 if( bChgAlign &&
2473 ( !(pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT )) ||
2474 text::VertOrientation::TOP == pVertOrientItem->GetVertOrient() ))
2476 rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM ));
2481 static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat )
2483 SwNodeOffset nNdPos = rBox.IsValidNumTextNd( false );
2484 if( NODE_OFFSET_MAX == nNdPos )
2485 return;
2487 SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
2488 SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
2489 bool bChgAlign = pDoc->IsInsTableAlignNum();
2491 const Color * pCol = nullptr;
2492 if( getSwDefaultTextFormat() != nFormat )
2494 // special text format:
2495 OUString sTmp;
2496 const OUString sText( pTNd->GetText() );
2497 pDoc->GetNumberFormatter()->GetOutputString( sText, nFormat, sTmp, &pCol );
2498 if( sText != sTmp )
2500 // exchange text
2501 // Reset DontExpand-Flags before exchange, to retrigger expansion
2502 pTNd->DontExpandFormat( sText.getLength(), false, false );
2503 SwContentIndex aIdx( pTNd, 0 );
2504 pTNd->EraseText( aIdx, SAL_MAX_INT32, SwInsertFlags::EMPTYEXPAND );
2505 pTNd->InsertText( sTmp, aIdx, SwInsertFlags::EMPTYEXPAND );
2509 const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
2511 // assign adjustment
2512 const SvxAdjustItem* pAdjustItem;
2513 if( bChgAlign && pAttrSet &&
2514 (pAdjustItem = pAttrSet->GetItemIfSet( RES_PARATR_ADJUST, false )) &&
2515 SvxAdjust::Right == pAdjustItem->GetAdjust() )
2517 pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
2520 // assign color or save "user color"
2521 const SvxColorItem* pColorItem = nullptr;
2522 if( pAttrSet )
2523 pColorItem = pAttrSet->GetItemIfSet( RES_CHRATR_COLOR, false );
2525 const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
2526 std::optional<Color> pNewUserColor;
2527 if (pColorItem)
2528 pNewUserColor = pColorItem->GetValue();
2530 if( ( pNewUserColor && pOldNumFormatColor &&
2531 *pNewUserColor == *pOldNumFormatColor ) ||
2532 ( !pNewUserColor && !pOldNumFormatColor ))
2534 // Keep the user color, set updated values, delete old NumFormatColor if needed
2535 if( pCol )
2536 // if needed, set the color
2537 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2538 else if( pColorItem )
2540 pNewUserColor = rBox.GetSaveUserColor();
2541 if( pNewUserColor )
2542 pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
2543 else
2544 pTNd->ResetAttr( RES_CHRATR_COLOR );
2547 else
2549 // Save user color, set NumFormat color if needed, but never reset the color
2550 rBox.SetSaveUserColor( pNewUserColor );
2552 if( pCol )
2553 // if needed, set the color
2554 pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
2557 rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
2559 // assign vertical orientation
2560 const SwFormatVertOrient* pVertOrientItem;
2561 if( bChgAlign &&
2562 (pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT, false )) &&
2563 text::VertOrientation::BOTTOM == pVertOrientItem->GetVertOrient() )
2565 rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP ));
2569 void SwTableBoxFormat::BoxAttributeChanged(SwTableBox& rBox, const SwTableBoxNumFormat* pNewFormat, const SwTableBoxFormula* pNewFormula, const SwTableBoxValue* pNewValue, sal_uLong nOldFormat)
2571 sal_uLong nNewFormat;
2572 if(pNewFormat)
2574 nNewFormat = pNewFormat->GetValue();
2575 // new formatting
2576 // is it newer or has the current been removed?
2577 if( SfxItemState::SET != GetItemState(RES_BOXATR_VALUE, false))
2578 pNewFormat = nullptr;
2580 else
2582 // fetch the current Item
2583 pNewFormat = GetItemIfSet(RES_BOXATR_FORMAT, false);
2584 nOldFormat = GetTableBoxNumFormat().GetValue();
2585 nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat;
2588 // is it newer or has the current been removed?
2589 if(pNewValue)
2591 if(GetDoc()->GetNumberFormatter()->IsTextFormat(nNewFormat))
2592 nOldFormat = 0;
2593 else
2595 if(SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false))
2596 nOldFormat = getSwDefaultTextFormat();
2597 else
2598 nNewFormat = getSwDefaultTextFormat();
2602 // Logic:
2603 // Value change: -> "simulate" a format change!
2604 // Format change:
2605 // Text -> !Text or format change:
2606 // - align right for horizontal alignment, if LEFT or JUSTIFIED
2607 // - align bottom for vertical alignment, if TOP is set, or default
2608 // - replace text (color? negative numbers RED?)
2609 // !Text -> Text:
2610 // - align left for horizontal alignment, if RIGHT
2611 // - align top for vertical alignment, if BOTTOM is set
2612 SvNumberFormatter* pNumFormatr = GetDoc()->GetNumberFormatter();
2613 bool bNewIsTextFormat = pNumFormatr->IsTextFormat(nNewFormat);
2615 if((!bNewIsTextFormat && nOldFormat != nNewFormat) || pNewFormula)
2617 bool bIsNumFormat = false;
2618 OUString aOrigText;
2619 bool bChgText = true;
2620 double fVal = 0;
2621 if(!pNewValue)
2622 pNewValue = GetItemIfSet(RES_BOXATR_VALUE, false);
2623 if(!pNewValue)
2625 // so far, no value has been set, so try to evaluate the content
2626 SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
2627 if(NODE_OFFSET_MAX != nNdPos)
2629 sal_uInt32 nTmpFormatIdx = nNewFormat;
2630 OUString aText(GetDoc()->GetNodes()[nNdPos] ->GetTextNode()->GetRedlineText());
2631 aOrigText = aText;
2632 if(aText.isEmpty())
2633 bChgText = false;
2634 else
2636 // Keep Tabs
2637 lcl_TabToBlankAtSttEnd(aText);
2639 // JP 22.04.98: Bug 49659 -
2640 // Special casing for percent
2641 if(SvNumFormatType::PERCENT == pNumFormatr->GetType(nNewFormat))
2643 sal_uInt32 nTmpFormat = 0;
2644 if(GetDoc()->IsNumberFormat(aText, nTmpFormat, fVal))
2646 if(SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat))
2647 aText += "%";
2649 bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
2652 else
2653 bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
2655 if(bIsNumFormat)
2657 // directly assign value - without Modify
2658 bool bIsLockMod = IsModifyLocked();
2659 LockModify();
2660 SetFormatAttr(SwTableBoxValue(fVal));
2661 if(!bIsLockMod)
2662 UnlockModify();
2667 else
2669 fVal = pNewValue->GetValue();
2670 bIsNumFormat = true;
2673 // format contents with the new value assigned and write to paragraph
2674 const Color* pCol = nullptr;
2675 OUString sNewText;
2676 bool bChangeFormat = true;
2677 if(DBL_MAX == fVal)
2679 sNewText = SwViewShell::GetShellRes()->aCalc_Error;
2681 else
2683 if(bIsNumFormat)
2684 pNumFormatr->GetOutputString(fVal, nNewFormat, sNewText, &pCol);
2685 else
2687 // Original text could not be parsed as
2688 // number/date/time/..., so keep the text.
2689 #if 0
2690 // Actually the text should be formatted
2691 // according to the format, which may include
2692 // additional text from the format, for example
2693 // in {0;-0;"BAD: "@}. But other places when
2694 // entering a new value or changing text or
2695 // changing to a different format of type Text
2696 // don't do this (yet?).
2697 pNumFormatr->GetOutputString(aOrigText, nNewFormat, sNewText, &pCol);
2698 #else
2699 sNewText = aOrigText;
2700 #endif
2701 // Remove the newly assigned numbering format as well if text actually exists.
2702 // Exception: assume user-defined formats are always intentional.
2703 if (bChgText && pNumFormatr->IsTextFormat(nOldFormat)
2704 && !pNumFormatr->IsUserDefined(nNewFormat))
2706 rBox.GetFrameFormat()->ResetFormatAttr(RES_BOXATR_FORMAT);
2707 bChangeFormat = false;
2711 if(!bChgText)
2712 sNewText.clear();
2715 // across all boxes
2716 if (bChangeFormat)
2717 ChgTextToNum(rBox, sNewText, pCol, GetDoc()->IsInsTableAlignNum());
2720 else if(bNewIsTextFormat && nOldFormat != nNewFormat)
2721 ChgNumToText(rBox, nNewFormat);
2724 SwTableBox* SwTableBoxFormat::SwTableBoxFormat::GetTableBox()
2726 SwIterator<SwTableBox,SwFormat> aIter(*this);
2727 auto pBox = aIter.First();
2728 SAL_INFO_IF(!pBox, "sw.core", "no box found at format");
2729 SAL_WARN_IF(pBox && aIter.Next(), "sw.core", "more than one box found at format");
2730 return pBox;
2733 // for detection of modifications (mainly TableBoxAttribute)
2734 void SwTableBoxFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
2736 if(rHint.GetId() != SfxHintId::SwLegacyModify)
2737 return;
2738 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
2739 if(IsModifyLocked() || !GetDoc() || GetDoc()->IsInDtor())
2741 SwFrameFormat::SwClientNotify(rMod, rHint);
2742 return;
2744 const SwTableBoxNumFormat* pNewFormat = nullptr;
2745 const SwTableBoxFormula* pNewFormula = nullptr;
2746 const SwTableBoxValue* pNewVal = nullptr;
2747 sal_uLong nOldFormat = getSwDefaultTextFormat();
2749 switch(pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0)
2751 case RES_ATTRSET_CHG:
2753 const SfxItemSet& rSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
2754 pNewFormat = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false);
2755 if(pNewFormat)
2756 nOldFormat = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->Get(RES_BOXATR_FORMAT).GetValue();
2757 pNewFormula = rSet.GetItemIfSet(RES_BOXATR_FORMULA, false);
2758 pNewVal = rSet.GetItemIfSet(RES_BOXATR_VALUE, false);
2759 break;
2761 case RES_BOXATR_FORMAT:
2762 pNewFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pNew);
2763 nOldFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pOld)->GetValue();
2764 break;
2765 case RES_BOXATR_FORMULA:
2766 pNewFormula = static_cast<const SwTableBoxFormula*>(pLegacy->m_pNew);
2767 break;
2768 case RES_BOXATR_VALUE:
2769 pNewVal = static_cast<const SwTableBoxValue*>(pLegacy->m_pNew);
2770 break;
2773 // something changed and some BoxAttribut remained in the set!
2774 if( pNewFormat || pNewFormula || pNewVal )
2776 GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
2778 if(SfxItemState::SET == GetItemState(RES_BOXATR_FORMAT, false) ||
2779 SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false) ||
2780 SfxItemState::SET == GetItemState(RES_BOXATR_FORMULA, false) )
2782 if(auto pBox = GetTableBox())
2783 BoxAttributeChanged(*pBox, pNewFormat, pNewFormula, pNewVal, nOldFormat);
2786 // call base class
2787 SwFrameFormat::SwClientNotify(rMod, rHint);
2790 bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const
2792 return false;
2795 bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const
2797 return false;
2800 bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const
2802 return false;
2805 bool SwTableBox::HasNumContent( double& rNum, sal_uInt32& rFormatIndex,
2806 bool& rIsEmptyTextNd ) const
2808 bool bRet = false;
2809 SwNodeOffset nNdPos = IsValidNumTextNd();
2810 if( NODE_OFFSET_MAX != nNdPos )
2812 OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() );
2813 // Keep Tabs
2814 lcl_TabToBlankAtSttEnd( aText );
2815 rIsEmptyTextNd = aText.isEmpty();
2816 SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter();
2818 if( const SwTableBoxNumFormat* pItem = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false) )
2820 rFormatIndex = pItem->GetValue();
2821 // Special casing for percent
2822 if( !rIsEmptyTextNd && SvNumFormatType::PERCENT == pNumFormatr->GetType( rFormatIndex ))
2824 sal_uInt32 nTmpFormat = 0;
2825 if( GetFrameFormat()->GetDoc()->IsNumberFormat( aText, nTmpFormat, rNum ) &&
2826 SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat ))
2827 aText += "%";
2830 else
2831 rFormatIndex = 0;
2833 bRet = GetFrameFormat()->GetDoc()->IsNumberFormat( aText, rFormatIndex, rNum );
2835 else
2836 rIsEmptyTextNd = false;
2837 return bRet;
2840 bool SwTableBox::IsNumberChanged() const
2842 bool bRet = true;
2844 if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA, false ))
2846 const SwTableBoxNumFormat *pNumFormat = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false );
2847 const SwTableBoxValue *pValue = GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false );
2849 SwNodeOffset nNdPos;
2850 if( pNumFormat && pValue && NODE_OFFSET_MAX != ( nNdPos = IsValidNumTextNd() ) )
2852 OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]->
2853 GetTextNode()->GetRedlineText() );
2854 lcl_DelTabsAtSttEnd( sOldText );
2856 const Color* pCol = nullptr;
2857 GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString(
2858 pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol );
2860 bRet = sNewText != sOldText ||
2861 !( ( !pCol && !GetSaveNumFormatColor() ) ||
2862 ( pCol && GetSaveNumFormatColor() &&
2863 *pCol == *GetSaveNumFormatColor() ));
2866 return bRet;
2869 SwNodeOffset SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const
2871 SwNodeOffset nPos = NODE_OFFSET_MAX;
2872 if( m_pStartNode )
2874 SwNodeIndex aIdx( *m_pStartNode );
2875 SwNodeOffset nIndex = aIdx.GetIndex();
2876 const SwNodeOffset nIndexEnd = m_pStartNode->GetNodes()[ nIndex ]->EndOfSectionIndex();
2877 const SwTextNode *pTextNode = nullptr;
2878 while( ++nIndex < nIndexEnd )
2880 const SwNode* pNode = m_pStartNode->GetNodes()[nIndex];
2881 if( pNode->IsTableNode() )
2883 pTextNode = nullptr;
2884 break;
2886 if( pNode->IsTextNode() )
2888 if( pTextNode )
2890 pTextNode = nullptr;
2891 break;
2893 else
2895 pTextNode = pNode->GetTextNode();
2896 nPos = nIndex;
2900 if( pTextNode )
2902 if( bCheckAttr )
2904 const SwpHints* pHts = pTextNode->GetpSwpHints();
2905 // do some tests if there's only text in the node!
2906 // Flys/fields/...
2907 if( pHts )
2909 sal_Int32 nNextSetField = 0;
2910 for( size_t n = 0; n < pHts->Count(); ++n )
2912 const SwTextAttr* pAttr = pHts->Get(n);
2913 if( RES_TXTATR_NOEND_BEGIN <= pAttr->Which() )
2915 if ( (pAttr->GetStart() == nNextSetField)
2916 && (pAttr->Which() == RES_TXTATR_FIELD))
2918 // #i104949# hideous hack for report builder:
2919 // it inserts hidden variable-set fields at
2920 // the beginning of para in cell, but they
2921 // should not turn cell into text cell
2922 const SwField* pField = pAttr->GetFormatField().GetField();
2923 if (pField &&
2924 (pField->GetTypeId() == SwFieldTypesEnum::Set) &&
2925 (0 != (static_cast<SwSetExpField const*>
2926 (pField)->GetSubType() &
2927 nsSwExtendedSubType::SUB_INVISIBLE)))
2929 nNextSetField = pAttr->GetStart() + 1;
2930 continue;
2933 else if( RES_TXTATR_ANNOTATION == pAttr->Which() ||
2934 RES_TXTATR_FTN == pAttr->Which() )
2936 continue;
2938 nPos = NODE_OFFSET_MAX;
2939 break;
2945 else
2946 nPos = NODE_OFFSET_MAX;
2948 return nPos;
2951 // is this a Formula box or one with numeric content (AutoSum)
2952 sal_uInt16 SwTableBox::IsFormulaOrValueBox() const
2954 sal_uInt16 nWhich = 0;
2955 const SwTextNode* pTNd;
2956 SwFrameFormat* pFormat = GetFrameFormat();
2957 if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ))
2958 nWhich = RES_BOXATR_FORMULA;
2959 else if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) &&
2960 !pFormat->GetDoc()->GetNumberFormatter()->IsTextFormat(
2961 pFormat->GetTableBoxNumFormat().GetValue() ))
2962 nWhich = RES_BOXATR_VALUE;
2963 else if( m_pStartNode && m_pStartNode->GetIndex() + 2 == m_pStartNode->EndOfSectionIndex()
2964 && nullptr != ( pTNd = m_pStartNode->GetNodes()[ m_pStartNode->GetIndex() + 1 ]
2965 ->GetTextNode() ) && pTNd->GetText().isEmpty())
2966 nWhich = USHRT_MAX;
2968 return nWhich;
2971 void SwTableBox::ActualiseValueBox()
2973 SwFrameFormat* pFormat = GetFrameFormat();
2974 const SwTableBoxNumFormat *pFormatItem = pFormat->GetItemIfSet( RES_BOXATR_FORMAT, true );
2975 if (!pFormatItem)
2976 return;
2977 const SwTableBoxValue *pValItem = pFormat->GetItemIfSet( RES_BOXATR_VALUE );
2978 if (!pValItem)
2979 return;
2981 const sal_uLong nFormatId = pFormatItem->GetValue();
2982 SwNodeOffset nNdPos = NODE_OFFSET_MAX;
2983 SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter();
2985 if( !pNumFormatr->IsTextFormat( nFormatId ) &&
2986 NODE_OFFSET_MAX != (nNdPos = IsValidNumTextNd()) )
2988 double fVal = pValItem->GetValue();
2989 const Color* pCol = nullptr;
2990 OUString sNewText;
2991 pNumFormatr->GetOutputString( fVal, nFormatId, sNewText, &pCol );
2993 const OUString& rText = m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetText();
2994 if( rText != sNewText )
2995 ChgTextToNum( *this, sNewText, pCol, false ,nNdPos);
2999 SwRedlineTable::size_type SwTableBox::GetRedline() const
3001 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
3002 const SwStartNode *pSttNd = GetSttNd();
3004 if ( aRedlineTable.empty() || !pSttNd )
3005 return SwRedlineTable::npos;
3007 // check table row property "HasTextChangesOnly", if it's defined and its value is
3008 // false, return with the first redline of the cell
3009 const SvxPrintItem *pHasTextChangesOnlyProp =
3010 GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
3011 if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
3012 return SwRedlineTable::npos;
3014 SwPosition aCellStart( *GetSttNd(), SwNodeOffset(0) );
3015 SwPosition aCellEnd( *GetSttNd()->EndOfSectionNode(), SwNodeOffset(-1) );
3016 SwNodeIndex pEndNodeIndex(aCellEnd.GetNode());
3017 SwRedlineTable::size_type nRedlinePos = 0;
3018 for( ; nRedlinePos < aRedlineTable.size(); ++nRedlinePos )
3020 const SwRangeRedline* pRedline = aRedlineTable[ nRedlinePos ];
3022 if ( pRedline->Start()->GetNodeIndex() > pEndNodeIndex.GetIndex() )
3024 // no more redlines in the actual cell,
3025 // check the next ones
3026 break;
3029 // redline in the cell
3030 if ( aCellStart <= *pRedline->Start() )
3031 return nRedlinePos;
3034 return SwRedlineTable::npos;
3037 RedlineType SwTableBox::GetRedlineType() const
3039 SwRedlineTable::size_type nPos = GetRedline();
3040 if ( nPos != SwRedlineTable::npos )
3042 const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
3043 const SwRangeRedline* pRedline = aRedlineTable[ nPos ];
3044 if ( RedlineType::Delete == pRedline->GetType() ||
3045 RedlineType::Insert == pRedline->GetType() )
3047 return pRedline->GetType();
3050 return RedlineType::None;
3053 struct SwTableCellInfo::Impl
3055 const SwTable * m_pTable;
3056 const SwCellFrame * m_pCellFrame;
3057 const SwTabFrame * m_pTabFrame;
3058 typedef o3tl::sorted_vector<const SwTableBox *> TableBoxes_t;
3059 TableBoxes_t m_HandledTableBoxes;
3061 public:
3062 Impl()
3063 : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr)
3067 void setTable(const SwTable * pTable)
3069 m_pTable = pTable;
3070 SwFrameFormat * pFrameFormat = m_pTable->GetFrameFormat();
3071 m_pTabFrame = SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
3072 if (m_pTabFrame && m_pTabFrame->IsFollow())
3073 m_pTabFrame = m_pTabFrame->FindMaster(true);
3076 const SwCellFrame * getCellFrame() const { return m_pCellFrame; }
3078 const SwFrame * getNextFrameInTable(const SwFrame * pFrame);
3079 const SwCellFrame * getNextCellFrame(const SwFrame * pFrame);
3080 const SwCellFrame * getNextTableBoxsCellFrame(const SwFrame * pFrame);
3081 bool getNext();
3084 const SwFrame * SwTableCellInfo::Impl::getNextFrameInTable(const SwFrame * pFrame)
3086 const SwFrame * pResult = nullptr;
3088 if (((! pFrame->IsTabFrame()) || pFrame == m_pTabFrame) && pFrame->GetLower())
3089 pResult = pFrame->GetLower();
3090 else if (pFrame->GetNext())
3091 pResult = pFrame->GetNext();
3092 else
3094 while (pFrame->GetUpper() != nullptr)
3096 pFrame = pFrame->GetUpper();
3098 if (pFrame->IsTabFrame())
3100 m_pTabFrame = static_cast<const SwTabFrame *>(pFrame)->GetFollow();
3101 pResult = m_pTabFrame;
3102 break;
3104 else if (pFrame->GetNext())
3106 pResult = pFrame->GetNext();
3107 break;
3112 return pResult;
3115 const SwCellFrame * SwTableCellInfo::Impl::getNextCellFrame(const SwFrame * pFrame)
3117 const SwCellFrame * pResult = nullptr;
3119 while ((pFrame = getNextFrameInTable(pFrame)) != nullptr)
3121 if (pFrame->IsCellFrame())
3123 pResult = static_cast<const SwCellFrame *>(pFrame);
3124 break;
3128 return pResult;
3131 const SwCellFrame * SwTableCellInfo::Impl::getNextTableBoxsCellFrame(const SwFrame * pFrame)
3133 const SwCellFrame * pResult = nullptr;
3135 while ((pFrame = getNextCellFrame(pFrame)) != nullptr)
3137 const SwCellFrame * pCellFrame = static_cast<const SwCellFrame *>(pFrame);
3138 const SwTableBox * pTabBox = pCellFrame->GetTabBox();
3139 auto aIt = m_HandledTableBoxes.insert(pTabBox);
3140 if (aIt.second)
3142 pResult = pCellFrame;
3143 break;
3147 return pResult;
3150 const SwCellFrame * SwTableCellInfo::getCellFrame() const
3152 return m_pImpl->getCellFrame();
3155 bool SwTableCellInfo::Impl::getNext()
3157 if (m_pCellFrame == nullptr)
3159 if (m_pTabFrame != nullptr)
3160 m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pTabFrame);
3162 else
3163 m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pCellFrame);
3165 return m_pCellFrame != nullptr;
3168 SwTableCellInfo::SwTableCellInfo(const SwTable * pTable)
3169 : m_pImpl(std::make_unique<Impl>())
3171 m_pImpl->setTable(pTable);
3174 SwTableCellInfo::~SwTableCellInfo()
3178 bool SwTableCellInfo::getNext()
3180 return m_pImpl->getNext();
3183 SwRect SwTableCellInfo::getRect() const
3185 SwRect aRet;
3187 if (getCellFrame() != nullptr)
3188 aRet = getCellFrame()->getFrameArea();
3190 return aRet;
3193 const SwTableBox * SwTableCellInfo::getTableBox() const
3195 const SwTableBox * pRet = nullptr;
3197 if (getCellFrame() != nullptr)
3198 pRet = getCellFrame()->GetTabBox();
3200 return pRet;
3203 void SwTable::RegisterToFormat( SwFormat& rFormat )
3205 rFormat.Add( this );
3208 bool SwTable::HasLayout() const
3210 const SwFrameFormat* pFrameFormat = GetFrameFormat();
3211 //a table in a clipboard document doesn't have any layout information
3212 return pFrameFormat && SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
3215 void SwTableBox::RegisterToFormat( SwFormat& rFormat )
3217 rFormat.Add( this );
3220 // free's any remaining child objects
3221 SwTableLines::~SwTableLines()
3223 for ( const_iterator it = begin(); it != end(); ++it )
3224 delete *it;
3227 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */