1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
37 #include <IDocumentLinksAdministration.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <IDocumentFieldsAccess.hxx>
42 #include <swtable.hxx>
46 #include <cellfrm.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>
59 #include <redline.hxx>
62 #include <o3tl/string_view.hxx>
63 #include <svl/numformat.hxx>
68 #define CHECK_TABLE(t) (t).CheckConsistency();
70 #define CHECK_TABLE(t)
73 using namespace com::sun::star
;
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
91 void SwTableBox::setDummyFlag( bool bDummy
)
96 //JP 15.09.98: Bug 55741 - Keep tabs (front and rear)
97 static OUString
& lcl_TabToBlankAtSttEnd( OUString
& rText
)
102 for( n
= 0; n
< rText
.getLength() && ' ' >= ( c
= rText
[n
] ); ++n
)
104 rText
= rText
.replaceAt( n
, 1, u
" " );
105 for( n
= rText
.getLength(); n
&& ' ' >= ( c
= rText
[--n
] ); )
107 rText
= rText
.replaceAt( n
, 1, u
" " );
111 static OUString
& lcl_DelTabsAtSttEnd( OUString
& rText
)
115 OUStringBuffer
sBuff(rText
);
117 for( n
= 0; n
< sBuff
.getLength() && ' ' >= ( c
= sBuff
[ n
]); ++n
)
120 sBuff
.remove( n
--, 1 );
122 for( n
= sBuff
.getLength(); n
&& ' ' >= ( c
= sBuff
[ --n
]); )
125 sBuff
.remove( n
, 1 );
127 rText
= sBuff
.makeStringAndClear();
131 void InsTableBox( SwDoc
& rDoc
, SwTableNode
* pTableNd
,
132 SwTableLine
* pLine
, SwTableBoxFormat
* pBoxFrameFormat
,
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();
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
));
159 aAttrSet
.ClearItem( RES_CHRATR_COLOR
);
161 rDoc
.GetNodes().InsBoxen( pTableNd
, pLine
, pBoxFrameFormat
,
162 static_cast<SwTextNode
*>(pCNd
)->GetTextColl(),
163 &aAttrSet
, nInsPos
, nCnt
);
166 rDoc
.GetNodes().InsBoxen( pTableNd
, pLine
, pBoxFrameFormat
,
167 static_cast<SwTextNode
*>(pCNd
)->GetTextColl(),
168 pCNd
->GetpSwAttrSet(), nInsPos
, nCnt
);
171 rDoc
.GetNodes().InsBoxen( pTableNd
, pLine
, pBoxFrameFormat
,
172 rDoc
.GetDfltTextFormatColl(), nullptr,
175 sal_Int32 nRowSpan
= pBox
->getRowSpan();
178 SwTableBoxes
& rTableBoxes
= pLine
->GetTabBoxes();
179 for( sal_uInt16 i
= 0; i
< nCnt
; ++i
)
181 pBox
= rTableBoxes
[ i
+ nInsPos
];
182 pBox
->setRowSpan( nRowSpan
);
188 : SwClient( nullptr ),
189 m_pTableNode( nullptr ),
190 m_nGraphicsThatResize( 0 ),
191 m_nRowsToRepeat( 1 ),
192 m_bModifyLocked( false ),
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;
223 SwDoc
* pDoc
= GetFrameFormat()->GetDoc();
224 if( !pDoc
->IsInDtor() ) // then remove from the list
225 pDoc
->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj
.get() );
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();
249 T
lcl_MulDiv64(sal_uInt64 nA
, sal_uInt64 nM
, sal_uInt64 nD
)
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
);
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 );
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
;
306 if( nBox
== nWishedSum
)
307 FormatInArr( rFormatArr
, pFormat
);
311 pFormat
= rBox
.ClaimFrameFormat();
312 SwFormatFrameSize
aNewBox( SwFrameSize::Variable
, static_cast< SwTwips
>(nBox
), 0 );
313 pFormat
->LockModify();
314 pFormat
->SetFormatAttr( aNewBox
);
315 pFormat
->UnlockModify();
319 OSL_FAIL( "Rounding error" );
325 void SwTable::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
327 if (rHint
.GetId() != SfxHintId::SwLegacyModify
)
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;
335 case RES_ATTRSET_CHG
:
337 if (pLegacy
->m_pOld
&& pLegacy
->m_pNew
338 && (pNewSize
= static_cast<const SwAttrSetChg
*>(pLegacy
->m_pNew
)->GetChgSet()->GetItemIfSet(
342 pOldSize
= &static_cast<const SwAttrSetChg
*>(pLegacy
->m_pOld
)->GetChgSet()->GetFrameSize();
348 pOldSize
= static_cast<const SwFormatFrameSize
*>(pLegacy
->m_pOld
);
349 pNewSize
= static_cast<const SwFormatFrameSize
*>(pLegacy
->m_pNew
);
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 );
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
393 const SwTableBox
*pCur
= pBox
;
394 const SwTableLine
*pLine
= pBox
->GetUpper();
395 const tools::Long nAct
= rToFill
.GetRight() - rToFill
.GetLeft(); // +1 why?
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();
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
;
415 if ( 0 == nRightMax
)
416 nRightMax
= nTmp
- nPos
;
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
)
437 rToFill
.Insert( nPos
, bHidden
, j
);
441 rToFill
.Insert( nPos
, bHidden
, rToFill
.Count() );
442 else if ( bRefreshHidden
)
443 ::lcl_RefreshHidden( rToFill
, nPos
);
445 if ( !bHidden
|| bRefreshHidden
)
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
;
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
)
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
);
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 );
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 );
539 rToFill
.Remove( 0, rToFill
.Count() );
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();
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
);
565 const SwTableLine
*pLine
= pStart
->GetUpper()->GetUpper() ?
566 pStart
->GetUpper()->GetUpper()->GetUpper() : nullptr;
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
)
581 for ( size_t i
= 0; i
< m_aLines
.size(); ++i
)
582 ::lcl_ProcessLineGet( m_aLines
[i
], rToFill
, pTabFormat
);
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
604 const SwTabCols
&rNew
;
605 const SwTabCols
&rOld
;
606 tools::Long nNewWish
,
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; )
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; )
636 lcl_ProcessLine( rLines
[i
], rParm
);
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();
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
;
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
) )
686 bFoundLeftPos
= true;
688 else if ( nRight
>= (rParm
.rOld
[i
] - COLFUZZY
) &&
689 nRight
<= (rParm
.rOld
[i
] + COLFUZZY
) )
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
];
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 )
728 if( nPos
+ 1 < o3tl::narrowing
<sal_uInt16
>(rTableBoxes
.size()) &&
729 rTableBoxes
[ nPos
+ 1 ]->getRowSpan() != 1 )
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
741 tools::Long nTmp
= rParm
.rNew
.GetRight() - rParm
.rNew
.GetLeft(); // +1 why?
742 nLeftDiff
*= rParm
.nNewWish
;
744 nRightDiff
*= rParm
.nNewWish
;
746 tools::Long nDiff
= nLeftDiff
+ nRightDiff
;
748 // Adjust the box and all superiors by the difference amount.
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.
759 if ( pBox
->GetUpper()->GetUpper() &&
760 pBox
->GetUpper() != pBox
->GetUpper()->GetUpper()->GetTabLines().back())
766 // Middle cell check:
767 if ( pBox
!= pBox
->GetUpper()->GetTabBoxes().front() )
770 if ( pBox
!= pBox
->GetUpper()->GetTabBoxes().back() )
773 pBox
= nDiff
? pBox
->GetUpper()->GetUpper() : nullptr;
780 static void lcl_ProcessBoxPtr( SwTableBox
*pBox
, std::deque
<SwTableBox
*> &rBoxArr
,
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
);
794 rBoxArr
.push_front( pBox
);
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
)
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()) )
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
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
);
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
;
896 if ( rOld
.GetRight() != rNew
.GetRight() )
898 tools::Long nDiff
= rNew
.GetRight() - rOld
.GetRight();
899 nDiff
*= aParm
.nOldWish
;
903 ::lcl_AdjustLines( GetTabLines(), nDiff
, aParm
);
906 // Adjust the size of the table, watch out for stretched tables.
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
);
924 NewSetTabCols( aParm
, rNew
, rOld
, pStart
, 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
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();
944 const SwTableBoxes
&rBoxes2
= pLine
->GetTabBoxes();
946 for ( size_t i
= 0; i
< rBoxes2
.size(); ++i
)
948 if ( rBoxes2
[i
] != pExcl
)
949 ::lcl_ProcessBoxPtr( rBoxes2
[i
], aParm
.aBoxArr
, bBefore
);
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
);
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; )
973 ::lcl_ProcessLine( rLines
[i
], aParm
);
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
);
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() )
999 const size_t nCount
= pLine
->GetTabBoxes().size();
1000 SwTwips nBorder
= 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
;
1009 if( pCurr
!= rOldNew
.end() && nBorder
+ nColFuzzy
>= pCurr
->first
)
1011 nBorder
-= nColFuzzy
;
1012 while( pCurr
!= rOldNew
.end() && nBorder
> pCurr
->first
)
1014 if( pCurr
!= rOldNew
.end() )
1016 nBorder
+= nColFuzzy
;
1017 if( nBorder
+ nColFuzzy
>= pCurr
->first
)
1019 if( pCurr
->second
== pCurr
->first
)
1022 nRest
= pCurr
->second
- nBorder
;
1028 if( nNewWidth
!= nWidth
)
1032 nRest
+= 1 - nNewWidth
;
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() )
1050 if( rSpanPos
.empty() )
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
),
1078 while( pCurr
!= rChanges
.end() && pCurr
->first
< nPos
)
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
);
1098 ColChange
aTmp( nPos
, nPos
);
1099 aNewChanges
.push_back( aTmp
);
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
;
1122 pCurr
->second
= lcl_MulDiv64
<sal_uInt16
>(
1123 pCurr
->first
- pLast
->first
,
1124 pLeftMove
->second
- pLast
->second
,
1125 pLeftMove
->first
- pLast
->first
) + pLast
->second
;
1131 else if( pCurr
->second
> pCurr
->first
)
1135 ChangeList::iterator pNext
= pCurr
;
1136 while( pNext
!= pLeftMove
&& pNext
->second
== pNext
->first
&&
1137 pNext
->second
< pLast
->second
)
1139 while( pCurr
!= pNext
)
1141 if( pNext
== aNewChanges
.end() || pNext
->first
== pLast
->first
)
1142 pCurr
->second
= pLast
->second
;
1145 pCurr
->second
= lcl_MulDiv64
<sal_uInt16
>(
1146 pCurr
->first
- pLast
->first
,
1147 pNext
->second
- pLast
->second
,
1148 pNext
->first
- pLast
->first
) + pLast
->second
;
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;
1172 // First step: evaluate which lines have been moved/which widths changed
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 )
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();
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();
1203 return; // no change, nothing to do
1204 SwTableLines
&rLines
= GetTabLines();
1207 const SwTableLine
* pCurrLine
= pStart
->GetUpper();
1208 sal_uInt16 nCurr
= rLines
.GetPos( pCurrLine
);
1209 if( nCurr
>= USHRT_MAX
)
1212 ColChange
aChg( 0, 0 );
1213 aOldNew
.push_front( aChg
);
1214 std::vector
<sal_uInt16
> aRowSpanPos
;
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
;
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()) )
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
;
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
);
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')
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
1291 // the first one uses letters for addressing!
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'))
1307 num
= num
* 52 + cChar
;
1308 if (num
> SAL_MAX_UINT16
) {
1313 nRet
= overflow
? SAL_MAX_UINT16
: num
;
1314 rStr
= rStr
.copy( nPos
); // Remove char from String
1318 const sal_Int32 nPos
= rStr
.indexOf( "." );
1322 if ( !bPerformValidCheck
|| lcl_IsValidRowName( rStr
) )
1324 nRet
= o3tl::narrowing
<sal_uInt16
>(rStr
.toInt32());
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 );
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
);
1358 pLines
= &GetTabLines();
1361 pLines
= &pBox
->GetTabLines();
1366 nLine
= SwTable::GetBoxNum( aNm
, false, bPerformValidCheck
);
1369 if( !nLine
|| nLine
> pLines
->size() )
1371 pLine
= (*pLines
)[ nLine
-1 ];
1374 const SwTableBoxes
* pBoxes
= &pLine
->GetTabBoxes();
1375 if( nBox
>= pBoxes
->size() )
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();
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())
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();
1410 pCNd
= rNds
[ nIndex
]->GetContentNode();
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();
1428 pRet
= const_cast<SwTableBox
*>(static_cast<SwCellFrame
*>(pFrame
)->GetTabBox());
1431 // In case the layout doesn't exist yet or anything else goes wrong.
1434 for (size_t n
= m_TabSortContentBoxes
.size(); n
; )
1436 if (m_TabSortContentBoxes
[ --n
]->GetSttIdx() == nSttIdx
)
1438 return m_TabSortContentBoxes
[ n
];
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())
1460 SwTableLine::SwTableLine( SwTableLineFormat
*pFormat
, sal_uInt16 nBoxes
,
1462 : SwClient( pFormat
)
1464 , m_eRedlineType( RedlineType::None
)
1466 m_aBoxes
.reserve( nBoxes
);
1469 SwTableLine::~SwTableLine()
1471 for (size_t i
= 0; i
< m_aBoxes
.size(); ++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
);
1506 pNewFormat
->Add( this );
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())
1525 SwTwips
SwTableLine::GetTableLineHeight( bool& bLayoutAvailable
) const
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)
1538 const SwTabFrame
* pTab
= pLast
->FindTabFrame();
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
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();
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() )
1558 // This is not an optimization, this is necessary to avoid double additions of
1560 if( pTab
->IsInHeadline(*pLast
) )
1567 bool SwTableLine::IsEmpty() const
1569 for (size_t i
= 0; i
< m_aBoxes
.size(); ++i
)
1571 if ( !m_aBoxes
[i
]->IsEmpty() )
1577 bool SwTable::IsEmpty() const
1579 for (size_t i
= 0; i
< m_aLines
.size(); ++i
)
1581 if ( !m_aLines
[i
]->IsEmpty() )
1587 bool SwTable::HasDeletedRowOrCell() const
1589 const SwRedlineTable
& aRedlineTable
= GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1590 if ( aRedlineTable
.empty() )
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
) )
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() )
1611 bool SwTable::IsDeleted() const
1613 const SwRedlineTable
& aRedlineTable
= GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1614 if ( aRedlineTable
.empty() )
1617 SwRedlineTable::size_type nRedlinePos
= 0;
1618 for (size_t i
= 0; i
< m_aLines
.size(); ++i
)
1620 if ( !m_aLines
[i
]->IsDeleted(nRedlinePos
) )
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())
1634 const SwNode
* pNd
= pBoxFormula
->GetNodeOfFormula();
1635 if(!pNd
|| &pNd
->GetNodes() != &pNd
->GetDoc().GetNodes()) // is this ever valid or should we assert here?
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)
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
;
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);
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)
1699 // to the external representation
1700 pField
->PtrToBoxNm(this);
1702 case TBL_RELBOXNAME
:
1703 // to the relative representation
1704 pField
->ToRelBoxNm(this);
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() );
1713 assert(false); // Only TBL_BOXNAME, TBL_RELBOXNAME and TBL_BOXPTR are supported
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);
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
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
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
1812 if ( RedlineType::Insert
== nType
)
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
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
;
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();
1897 const_cast<SwTableLine
*>(this)->SetRedlineType( SwRedlineTable::npos
== nRet
1899 : aRedlineTable
[ nRet
]->GetType());
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
)
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() ) )
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
)
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
)
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
)
1991 , m_pStartNode(nullptr)
1994 , mbDummyFlag(false)
1995 , mbDirectFormatting(false)
1997 m_aLines
.reserve( nLines
);
1998 CheckBoxFormat( pFormat
)->Add( this );
2001 SwTableBox::SwTableBox( SwTableBoxFormat
* pFormat
, const SwNodeIndex
&rIdx
,
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().
2019 SwTableBox
* p
= this; // error: &this
2020 rSrtArr
.insert( p
); // insert
2023 SwTableBox::SwTableBox( SwTableBoxFormat
* pFormat
, const SwStartNode
& rSttNd
, SwTableLine
*pUp
)
2026 , m_pStartNode(&rSttNd
)
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().
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().
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())
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();
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
;
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 );
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())
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'
2153 const sal_uInt16 nCalc
= nCol
% coDiff
;
2155 rNm
= OUStringChar( sal_Unicode('a' - 26 + nCalc
) ) + rNm
;
2157 rNm
= OUStringChar( sal_Unicode('A' + nCalc
) ) + rNm
;
2159 nCol
= nCol
- nCalc
;
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();
2177 const SwTableBox
* pBox
= this;
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();
2188 return Point( nX
, nY
);
2191 OUString
SwTableBox::GetName() const
2193 if( !m_pStartNode
) // box without content?
2195 // search for the next first box?
2199 const SwTable
& rTable
= m_pStartNode
->FindTableNode()->GetTable();
2202 const SwTableBox
* pBox
= this;
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
;
2216 nPos
= pBox
->GetUpper()->GetBoxPos( pBox
);
2217 sTmp
= OUString::number(nPos
+ 1);
2218 pBox
= pLine
->GetUpper();
2219 if( nullptr != pBox
)
2220 sNm
= sTmp
+ "." + sNm
;
2222 sw_GetTableBoxColStr( nPos
, sNm
);
2228 bool SwTableBox::IsInHeadline( const SwTable
* pTable
) const
2230 if( !GetUpper() ) // should only happen upon merge.
2234 pTable
= &m_pStartNode
->FindTableNode()->GetTable();
2236 const SwTableLine
* pLine
= GetUpper();
2237 while( pLine
->GetUpper() )
2238 pLine
= pLine
->GetUpper()->GetUpper();
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();
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() )
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 )
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();
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
);
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() );
2320 SwTable
* SwTable::FindTable( SwFrameFormat
const*const pFormat
)
2323 ? SwIterator
<SwTable
,SwFormat
>(*pFormat
).First()
2327 SwTableNode
* SwTable::GetTableNode() const
2329 return !GetTabSortBoxes().empty() ?
2330 const_cast<SwTableNode
*>(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) :
2334 void SwTable::SetRefObject( SwServerObject
* pObj
)
2336 if( m_xRefObj
.is() )
2337 m_xRefObj
->Closed();
2342 void SwTable::SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
> const& r
)
2347 static void ChgTextToNum( SwTableBox
& rBox
, const OUString
& rText
, const Color
* pCol
,
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
)
2360 SwDoc
* pDoc
= rBox
.GetFrameFormat()->GetDoc();
2361 SwTextNode
* pTNd
= pDoc
->GetNodes()[ nNdPos
]->GetTextNode();
2363 // assign adjustment
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
;
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
2393 // if needed, set the color
2394 pTNd
->SetAttr( SvxColorItem( *pCol
, RES_CHRATR_COLOR
));
2395 else if( pColorItem
)
2397 pNewUserColor
= rBox
.GetSaveUserColor();
2399 pTNd
->SetAttr( SvxColorItem( *pNewUserColor
, RES_CHRATR_COLOR
));
2401 pTNd
->ResetAttr( RES_CHRATR_COLOR
);
2406 // Save user color, set NumFormat color if needed, but never reset the color
2407 rBox
.SetSaveUserColor( pNewUserColor
? *pNewUserColor
: std::optional
<Color
>() );
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();
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
);
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
;
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
)
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:
2496 const OUString
sText( pTNd
->GetText() );
2497 pDoc
->GetNumberFormatter()->GetOutputString( sText
, nFormat
, sTmp
, &pCol
);
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;
2523 pColorItem
= pAttrSet
->GetItemIfSet( RES_CHRATR_COLOR
, false );
2525 const std::optional
<Color
>& pOldNumFormatColor
= rBox
.GetSaveNumFormatColor();
2526 std::optional
<Color
> pNewUserColor
;
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
2536 // if needed, set the color
2537 pTNd
->SetAttr( SvxColorItem( *pCol
, RES_CHRATR_COLOR
));
2538 else if( pColorItem
)
2540 pNewUserColor
= rBox
.GetSaveUserColor();
2542 pTNd
->SetAttr( SvxColorItem( *pNewUserColor
, RES_CHRATR_COLOR
));
2544 pTNd
->ResetAttr( RES_CHRATR_COLOR
);
2549 // Save user color, set NumFormat color if needed, but never reset the color
2550 rBox
.SetSaveUserColor( pNewUserColor
);
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
;
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
;
2574 nNewFormat
= pNewFormat
->GetValue();
2576 // is it newer or has the current been removed?
2577 if( SfxItemState::SET
!= GetItemState(RES_BOXATR_VALUE
, false))
2578 pNewFormat
= nullptr;
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?
2591 if(GetDoc()->GetNumberFormatter()->IsTextFormat(nNewFormat
))
2595 if(SfxItemState::SET
== GetItemState(RES_BOXATR_VALUE
, false))
2596 nOldFormat
= getSwDefaultTextFormat();
2598 nNewFormat
= getSwDefaultTextFormat();
2603 // Value change: -> "simulate" a 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?)
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;
2619 bool bChgText
= true;
2622 pNewValue
= GetItemIfSet(RES_BOXATR_VALUE
, false);
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());
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
))
2649 bIsNumFormat
= GetDoc()->IsNumberFormat(aText
, nTmpFormatIdx
, fVal
);
2653 bIsNumFormat
= GetDoc()->IsNumberFormat(aText
, nTmpFormatIdx
, fVal
);
2657 // directly assign value - without Modify
2658 bool bIsLockMod
= IsModifyLocked();
2660 SetFormatAttr(SwTableBoxValue(fVal
));
2669 fVal
= pNewValue
->GetValue();
2670 bIsNumFormat
= true;
2673 // format contents with the new value assigned and write to paragraph
2674 const Color
* pCol
= nullptr;
2676 bool bChangeFormat
= true;
2679 sNewText
= SwViewShell::GetShellRes()->aCalc_Error
;
2684 pNumFormatr
->GetOutputString(fVal
, nNewFormat
, sNewText
, &pCol
);
2687 // Original text could not be parsed as
2688 // number/date/time/..., so keep the text.
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
);
2699 sNewText
= aOrigText
;
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;
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");
2733 // for detection of modifications (mainly TableBoxAttribute)
2734 void SwTableBoxFormat::SwClientNotify(const SwModify
& rMod
, const SfxHint
& rHint
)
2736 if(rHint
.GetId() != SfxHintId::SwLegacyModify
)
2738 auto pLegacy
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
2739 if(IsModifyLocked() || !GetDoc() || GetDoc()->IsInDtor())
2741 SwFrameFormat::SwClientNotify(rMod
, rHint
);
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);
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);
2761 case RES_BOXATR_FORMAT
:
2762 pNewFormat
= static_cast<const SwTableBoxNumFormat
*>(pLegacy
->m_pNew
);
2763 nOldFormat
= static_cast<const SwTableBoxNumFormat
*>(pLegacy
->m_pOld
)->GetValue();
2765 case RES_BOXATR_FORMULA
:
2766 pNewFormula
= static_cast<const SwTableBoxFormula
*>(pLegacy
->m_pNew
);
2768 case RES_BOXATR_VALUE
:
2769 pNewVal
= static_cast<const SwTableBoxValue
*>(pLegacy
->m_pNew
);
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
);
2787 SwFrameFormat::SwClientNotify(rMod
, rHint
);
2790 bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const
2795 bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const
2800 bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const
2805 bool SwTableBox::HasNumContent( double& rNum
, sal_uInt32
& rFormatIndex
,
2806 bool& rIsEmptyTextNd
) const
2809 SwNodeOffset nNdPos
= IsValidNumTextNd();
2810 if( NODE_OFFSET_MAX
!= nNdPos
)
2812 OUString
aText( m_pStartNode
->GetNodes()[ nNdPos
]->GetTextNode()->GetRedlineText() );
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
))
2833 bRet
= GetFrameFormat()->GetDoc()->IsNumberFormat( aText
, rFormatIndex
, rNum
);
2836 rIsEmptyTextNd
= false;
2840 bool SwTableBox::IsNumberChanged() const
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() ));
2869 SwNodeOffset
SwTableBox::IsValidNumTextNd( bool bCheckAttr
) const
2871 SwNodeOffset nPos
= NODE_OFFSET_MAX
;
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;
2886 if( pNode
->IsTextNode() )
2890 pTextNode
= nullptr;
2895 pTextNode
= pNode
->GetTextNode();
2904 const SwpHints
* pHts
= pTextNode
->GetpSwpHints();
2905 // do some tests if there's only text in the node!
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();
2924 (pField
->GetTypeId() == SwFieldTypesEnum::Set
) &&
2925 (0 != (static_cast<SwSetExpField
const*>
2926 (pField
)->GetSubType() &
2927 nsSwExtendedSubType::SUB_INVISIBLE
)))
2929 nNextSetField
= pAttr
->GetStart() + 1;
2933 else if( RES_TXTATR_ANNOTATION
== pAttr
->Which() ||
2934 RES_TXTATR_FTN
== pAttr
->Which() )
2938 nPos
= NODE_OFFSET_MAX
;
2946 nPos
= NODE_OFFSET_MAX
;
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())
2971 void SwTableBox::ActualiseValueBox()
2973 SwFrameFormat
* pFormat
= GetFrameFormat();
2974 const SwTableBoxNumFormat
*pFormatItem
= pFormat
->GetItemIfSet( RES_BOXATR_FORMAT
, true );
2977 const SwTableBoxValue
*pValItem
= pFormat
->GetItemIfSet( RES_BOXATR_VALUE
);
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;
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
3029 // redline in the cell
3030 if ( aCellStart
<= *pRedline
->Start() )
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
;
3063 : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr)
3067 void setTable(const SwTable
* 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
);
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();
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
;
3104 else if (pFrame
->GetNext())
3106 pResult
= pFrame
->GetNext();
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
);
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
);
3142 pResult
= pCellFrame
;
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
);
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
3187 if (getCellFrame() != nullptr)
3188 aRet
= getCellFrame()->getFrameArea();
3193 const SwTableBox
* SwTableCellInfo::getTableBox() const
3195 const SwTableBox
* pRet
= nullptr;
3197 if (getCellFrame() != nullptr)
3198 pRet
= getCellFrame()->GetTabBox();
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
)
3227 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */