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>
21 #include <config_wasm_strip.h>
24 #include <hintids.hxx>
25 #include <editeng/lrspitem.hxx>
26 #include <editeng/protitem.hxx>
27 #include <editeng/boxitem.hxx>
28 #include <svl/stritem.hxx>
29 #include <editeng/shaditem.hxx>
30 #include <fmtfsize.hxx>
31 #include <fmtornt.hxx>
32 #include <fmtfordr.hxx>
33 #include <fmtpdsc.hxx>
34 #include <fmtanchr.hxx>
35 #include <fmtlsplt.hxx>
37 #include <cellfrm.hxx>
38 #include <pagefrm.hxx>
41 #include <IDocumentUndoRedo.hxx>
42 #include <UndoManager.hxx>
43 #include <DocumentSettingManager.hxx>
44 #include <IDocumentChartDataProviderAccess.hxx>
45 #include <IDocumentRedlineAccess.hxx>
46 #include <IDocumentStylePoolAccess.hxx>
47 #include <IDocumentFieldsAccess.hxx>
48 #include <IDocumentLayoutAccess.hxx>
49 #include <IDocumentState.hxx>
53 #include <swtable.hxx>
56 #include <poolfmt.hxx>
58 #include <UndoCore.hxx>
59 #include <UndoRedline.hxx>
60 #include <UndoDelete.hxx>
61 #include <UndoNumbering.hxx>
62 #include <UndoTable.hxx>
64 #include <tblafmt.hxx>
66 #include <cellatr.hxx>
67 #include <swtblfmt.hxx>
68 #include <swddetbl.hxx>
71 #include <redline.hxx>
73 #include <tblrwcl.hxx>
76 #include <section.hxx>
77 #include <frmtool.hxx>
78 #include <node2lay.hxx>
79 #include <strings.hrc>
81 #include <unochart.hxx>
87 #include <rootfrm.hxx>
88 #include <fldupde.hxx>
90 #include <fntcache.hxx>
91 #include <frameformats.hxx>
92 #include <o3tl/numeric.hxx>
93 #include <o3tl/string_view.hxx>
94 #include <svl/numformat.hxx>
95 #include <tools/datetimeutils.hxx>
96 #include <sal/log.hxx>
97 #include <osl/diagnose.h>
100 #define CHECK_TABLE(t) (t).CheckConsistency();
102 #define CHECK_TABLE(t)
105 using ::editeng::SvxBorderLine
;
106 using namespace ::com::sun::star
;
108 const sal_Unicode T2T_PARA
= 0x0a;
110 static void lcl_SetDfltBoxAttr( SwFrameFormat
& rFormat
, sal_uInt8 nId
)
112 bool bTop
= false, bBottom
= false, bLeft
= false, bRight
= false;
115 case 0: bTop
= bBottom
= bLeft
= true; break;
116 case 1: bTop
= bBottom
= bLeft
= bRight
= true; break;
117 case 2: bBottom
= bLeft
= true; break;
118 case 3: bBottom
= bLeft
= bRight
= true; break;
121 const bool bHTML
= rFormat
.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE
);
122 Color
aCol( bHTML
? COL_GRAY
: COL_BLACK
);
123 // Default border in Writer: 0.5pt (matching Word)
124 SvxBorderLine
aLine( &aCol
, SvxBorderLineWidth::VeryThin
);
127 aLine
.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE
);
129 SvxBoxItem
aBox(RES_BOX
);
130 aBox
.SetAllDistances(55);
132 aBox
.SetLine( &aLine
, SvxBoxItemLine::TOP
);
134 aBox
.SetLine( &aLine
, SvxBoxItemLine::BOTTOM
);
136 aBox
.SetLine( &aLine
, SvxBoxItemLine::LEFT
);
138 aBox
.SetLine( &aLine
, SvxBoxItemLine::RIGHT
);
139 rFormat
.SetFormatAttr( aBox
);
142 typedef std::map
<SwFrameFormat
*, SwTableBoxFormat
*> DfltBoxAttrMap_t
;
143 typedef std::vector
<DfltBoxAttrMap_t
*> DfltBoxAttrList_t
;
146 lcl_SetDfltBoxAttr(SwTableBox
& rBox
, DfltBoxAttrList_t
& rBoxFormatArr
,
147 sal_uInt8
const nId
, SwTableAutoFormat
const*const pAutoFormat
= nullptr)
149 DfltBoxAttrMap_t
* pMap
= rBoxFormatArr
[ nId
];
152 pMap
= new DfltBoxAttrMap_t
;
153 rBoxFormatArr
[ nId
] = pMap
;
156 SwTableBoxFormat
* pNewTableBoxFormat
= nullptr;
157 SwFrameFormat
* pBoxFrameFormat
= rBox
.GetFrameFormat();
158 DfltBoxAttrMap_t::iterator
const iter(pMap
->find(pBoxFrameFormat
));
159 if (pMap
->end() != iter
)
161 pNewTableBoxFormat
= iter
->second
;
165 SwDoc
* pDoc
= pBoxFrameFormat
->GetDoc();
166 // format does not exist, so create it
167 pNewTableBoxFormat
= pDoc
->MakeTableBoxFormat();
168 pNewTableBoxFormat
->SetFormatAttr( pBoxFrameFormat
->GetAttrSet().Get( RES_FRM_SIZE
) );
171 pAutoFormat
->UpdateToSet( nId
, false, false,
172 const_cast<SfxItemSet
&>(static_cast<SfxItemSet
const &>(pNewTableBoxFormat
->GetAttrSet())),
173 SwTableAutoFormatUpdateFlags::Box
,
174 pDoc
->GetNumberFormatter() );
176 ::lcl_SetDfltBoxAttr( *pNewTableBoxFormat
, nId
);
178 (*pMap
)[pBoxFrameFormat
] = pNewTableBoxFormat
;
180 rBox
.ChgFrameFormat( pNewTableBoxFormat
);
183 static SwTableBoxFormat
*lcl_CreateDfltBoxFormat( SwDoc
&rDoc
, std::vector
<SwTableBoxFormat
*> &rBoxFormatArr
,
184 sal_uInt16 nCols
, sal_uInt8 nId
)
186 if ( !rBoxFormatArr
[nId
] )
188 SwTableBoxFormat
* pBoxFormat
= rDoc
.MakeTableBoxFormat();
189 if( USHRT_MAX
!= nCols
)
190 pBoxFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
,
191 USHRT_MAX
/ nCols
, 0 ));
192 ::lcl_SetDfltBoxAttr( *pBoxFormat
, nId
);
193 rBoxFormatArr
[ nId
] = pBoxFormat
;
195 return rBoxFormatArr
[nId
];
198 static SwTableBoxFormat
*lcl_CreateAFormatBoxFormat( SwDoc
&rDoc
, std::vector
<SwTableBoxFormat
*> &rBoxFormatArr
,
199 const SwTableAutoFormat
& rAutoFormat
,
200 const sal_uInt16 nRows
, const sal_uInt16 nCols
, sal_uInt8 nId
)
202 if( !rBoxFormatArr
[nId
] )
204 SwTableBoxFormat
* pBoxFormat
= rDoc
.MakeTableBoxFormat();
205 rAutoFormat
.UpdateToSet( nId
, nRows
==1, nCols
==1,
206 const_cast<SfxItemSet
&>(static_cast<SfxItemSet
const &>(pBoxFormat
->GetAttrSet())),
207 SwTableAutoFormatUpdateFlags::Box
,
208 rDoc
.GetNumberFormatter( ) );
209 if( USHRT_MAX
!= nCols
)
210 pBoxFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
,
211 USHRT_MAX
/ nCols
, 0 ));
212 rBoxFormatArr
[ nId
] = pBoxFormat
;
214 return rBoxFormatArr
[nId
];
217 SwTableNode
* SwDoc::IsIdxInTable( const SwNodeIndex
& rIdx
) { return IsInTable(rIdx
.GetNode()); }
219 SwTableNode
* SwDoc::IsInTable(const SwNode
& rIdx
)
221 SwNode
* pNd
= const_cast<SwNode
*>(&rIdx
);
223 pNd
= pNd
->StartOfSectionNode();
224 SwTableNode
* pTableNd
= pNd
->GetTableNode();
227 } while ( pNd
->GetIndex() );
232 * Insert a new Box before the InsPos
234 bool SwNodes::InsBoxen( SwTableNode
* pTableNd
,
236 SwTableBoxFormat
* pBoxFormat
,
237 SwTextFormatColl
* pTextColl
,
238 const SfxItemSet
* pAutoAttr
,
244 OSL_ENSURE( pLine
, "No valid Line" );
246 // Move Index after the Line's last Box
247 SwNodeOffset
nIdxPos(0);
248 SwTableBox
*pPrvBox
= nullptr, *pNxtBox
= nullptr;
249 if( !pLine
->GetTabBoxes().empty() )
251 if( nInsPos
< pLine
->GetTabBoxes().size() )
253 pPrvBox
= pLine
->FindPreviousBox( pTableNd
->GetTable(),
254 pLine
->GetTabBoxes()[ nInsPos
] );
255 if( nullptr == pPrvBox
)
256 pPrvBox
= pLine
->FindPreviousBox( pTableNd
->GetTable() );
260 pNxtBox
= pLine
->FindNextBox( pTableNd
->GetTable(),
261 pLine
->GetTabBoxes().back() );
262 if( nullptr == pNxtBox
)
263 pNxtBox
= pLine
->FindNextBox( pTableNd
->GetTable() );
268 pNxtBox
= pLine
->FindNextBox( pTableNd
->GetTable() );
269 if( nullptr == pNxtBox
)
270 pPrvBox
= pLine
->FindPreviousBox( pTableNd
->GetTable() );
273 if( !pPrvBox
&& !pNxtBox
)
275 bool bSetIdxPos
= true;
276 if( !pTableNd
->GetTable().GetTabLines().empty() && !nInsPos
)
278 const SwTableLine
* pTableLn
= pLine
;
279 while( pTableLn
->GetUpper() )
280 pTableLn
= pTableLn
->GetUpper()->GetUpper();
282 if( pTableNd
->GetTable().GetTabLines()[ 0 ] == pTableLn
)
284 // Before the Table's first Box
285 while( !( pNxtBox
= pLine
->GetTabBoxes()[0])->GetTabLines().empty() )
286 pLine
= pNxtBox
->GetTabLines()[0];
287 nIdxPos
= pNxtBox
->GetSttIdx();
292 // Tables without content or at the end; move before the End
293 nIdxPos
= pTableNd
->EndOfSectionIndex();
295 else if( pNxtBox
) // There is a successor
296 nIdxPos
= pNxtBox
->GetSttIdx();
297 else // There is a predecessor
298 nIdxPos
= pPrvBox
->GetSttNd()->EndOfSectionIndex() + 1;
300 SwNodeIndex
aEndIdx( *this, nIdxPos
);
301 for( sal_uInt16 n
= 0; n
< nCnt
; ++n
)
303 SwStartNode
* pSttNd
= new SwStartNode( aEndIdx
.GetNode(), SwNodeType::Start
,
304 SwTableBoxStartNode
);
305 pSttNd
->m_pStartOfSection
= pTableNd
;
306 new SwEndNode( aEndIdx
.GetNode(), *pSttNd
);
308 pPrvBox
= new SwTableBox( pBoxFormat
, *pSttNd
, pLine
);
310 SwTableBoxes
& rTabBoxes
= pLine
->GetTabBoxes();
311 sal_uInt16 nRealInsPos
= nInsPos
+ n
;
312 if (nRealInsPos
> rTabBoxes
.size())
313 nRealInsPos
= rTabBoxes
.size();
315 rTabBoxes
.insert( rTabBoxes
.begin() + nRealInsPos
, pPrvBox
);
317 if( ! pTextColl
->IsAssignedToListLevelOfOutlineStyle()
318 && RES_CONDTXTFMTCOLL
!= pTextColl
->Which()
320 new SwTextNode( *pSttNd
->EndOfSectionNode(), pTextColl
, pAutoAttr
);
323 // Handle Outline numbering correctly!
324 SwTextNode
* pTNd
= new SwTextNode(
325 *pSttNd
->EndOfSectionNode(),
326 GetDoc().GetDfltTextFormatColl(),
328 pTNd
->ChgFormatColl( pTextColl
);
337 const SwTable
* SwDoc::InsertTable( const SwInsertTableOptions
& rInsTableOpts
,
338 const SwPosition
& rPos
, sal_uInt16 nRows
,
339 sal_uInt16 nCols
, sal_Int16 eAdjust
,
340 const SwTableAutoFormat
* pTAFormat
,
341 const std::vector
<sal_uInt16
> *pColArr
,
342 bool bCalledFromShell
,
344 const OUString
& rTableName
)
346 assert(nRows
&& "Table without line?");
347 assert(nCols
&& "Table without rows?");
350 // Do not copy into Footnotes!
351 if( rPos
.GetNode() < GetNodes().GetEndOfInserts() &&
352 rPos
.GetNode().GetIndex() >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
355 // If the ColumnArray has a wrong count, ignore it!
357 static_cast<size_t>(nCols
+ ( text::HoriOrientation::NONE
== eAdjust
? 2 : 1 )) != pColArr
->size() )
361 OUString aTableName
= rTableName
;
362 if (aTableName
.isEmpty() || FindTableFormatByName(aTableName
) != nullptr)
363 aTableName
= GetUniqueTableName();
365 if( GetIDocumentUndoRedo().DoesUndo() )
367 GetIDocumentUndoRedo().AppendUndo(
368 std::make_unique
<SwUndoInsTable
>( rPos
, nCols
, nRows
, o3tl::narrowing
<sal_uInt16
>(eAdjust
),
369 rInsTableOpts
, pTAFormat
, pColArr
,
373 // Start with inserting the Nodes and get the AutoFormat for the Table
374 SwTextFormatColl
*pBodyColl
= getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE
),
375 *pHeadColl
= pBodyColl
;
377 bool bDfltBorders( rInsTableOpts
.mnInsMode
& SwInsertTableFlags::DefaultBorder
);
379 if( (rInsTableOpts
.mnInsMode
& SwInsertTableFlags::Headline
) && (1 != nRows
|| !bDfltBorders
) )
380 pHeadColl
= getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN
);
382 const sal_uInt16 nRowsToRepeat
=
383 SwInsertTableFlags::Headline
== (rInsTableOpts
.mnInsMode
& SwInsertTableFlags::Headline
) ?
384 rInsTableOpts
.mnRowsToRepeat
:
387 /* Save content node to extract FRAMEDIR from. */
388 const SwContentNode
* pContentNd
= rPos
.GetNode().GetContentNode();
390 /* If we are called from a shell pass the attrset from
391 pContentNd (aka the node the table is inserted at) thus causing
392 SwNodes::InsertTable to propagate an adjust item if
394 SwTableNode
*pTableNd
= SwNodes::InsertTable(
401 bCalledFromShell
? &pContentNd
->GetSwAttrSet() : nullptr );
403 // Create the Box/Line/Table construct
404 SwTableLineFormat
* pLineFormat
= MakeTableLineFormat();
405 SwTableFormat
* pTableFormat
= MakeTableFrameFormat( aTableName
, GetDfltFrameFormat() );
407 /* If the node to insert the table at is a context node and has a
408 non-default FRAMEDIR propagate it to the table. */
411 const SwAttrSet
& aNdSet
= pContentNd
->GetSwAttrSet();
412 if (const SvxFrameDirectionItem
* pItem
= aNdSet
.GetItemIfSet( RES_FRAMEDIR
))
414 pTableFormat
->SetFormatAttr( *pItem
);
418 // Set Orientation at the Table's Format
419 pTableFormat
->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust
) );
420 // All lines use the left-to-right Fill-Order!
421 pLineFormat
->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT
));
423 // Set USHRT_MAX as the Table's default SSize
424 SwTwips nWidth
= USHRT_MAX
;
427 sal_uInt16 nSttPos
= pColArr
->front();
428 sal_uInt16 nLastPos
= pColArr
->back();
429 if( text::HoriOrientation::NONE
== eAdjust
)
431 sal_uInt16 nFrameWidth
= nLastPos
;
432 nLastPos
= (*pColArr
)[ pColArr
->size()-2 ];
433 pTableFormat
->SetFormatAttr( SvxLRSpaceItem( nSttPos
, nFrameWidth
- nLastPos
, 0, RES_LR_SPACE
) );
435 nWidth
= nLastPos
- nSttPos
;
440 nWidth
*= nCols
; // to avoid rounding problems
442 pTableFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nWidth
));
443 if( !(rInsTableOpts
.mnInsMode
& SwInsertTableFlags::SplitLayout
) )
444 pTableFormat
->SetFormatAttr( SwFormatLayoutSplit( false ));
446 // Move the hard PageDesc/PageBreak Attributes if needed
447 SwContentNode
* pNextNd
= GetNodes()[ pTableNd
->EndOfSectionIndex()+1 ]
449 if( pNextNd
&& pNextNd
->HasSwAttrSet() )
451 const SfxItemSet
* pNdSet
= pNextNd
->GetpSwAttrSet();
452 if( const SwFormatPageDesc
* pItem
= pNdSet
->GetItemIfSet( RES_PAGEDESC
, false ) )
454 pTableFormat
->SetFormatAttr( *pItem
);
455 pNextNd
->ResetAttr( RES_PAGEDESC
);
456 pNdSet
= pNextNd
->GetpSwAttrSet();
458 const SvxFormatBreakItem
* pItem
;
459 if( pNdSet
&& (pItem
= pNdSet
->GetItemIfSet( RES_BREAK
, false )) )
461 pTableFormat
->SetFormatAttr( *pItem
);
462 pNextNd
->ResetAttr( RES_BREAK
);
466 SwTable
& rNdTable
= pTableNd
->GetTable();
467 rNdTable
.RegisterToFormat( *pTableFormat
);
469 rNdTable
.SetRowsToRepeat( nRowsToRepeat
);
470 rNdTable
.SetTableModel( bNewModel
);
472 std::vector
<SwTableBoxFormat
*> aBoxFormatArr
;
473 SwTableBoxFormat
* pBoxFormat
= nullptr;
474 if( !bDfltBorders
&& !pTAFormat
)
476 pBoxFormat
= MakeTableBoxFormat();
477 pBoxFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, USHRT_MAX
/ nCols
, 0 ));
481 const sal_uInt16 nBoxArrLen
= pTAFormat
? 16 : 4;
482 aBoxFormatArr
.resize( nBoxArrLen
, nullptr );
484 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_PARATR_LIST_END
-1> aCharSet( GetAttrPool() );
486 SwNodeIndex
aNdIdx( *pTableNd
, 1 ); // Set to StartNode of first Box
487 SwTableLines
& rLines
= rNdTable
.GetTabLines();
488 for( sal_uInt16 n
= 0; n
< nRows
; ++n
)
490 SwTableLine
* pLine
= new SwTableLine( pLineFormat
, nCols
, nullptr );
491 rLines
.insert( rLines
.begin() + n
, pLine
);
492 SwTableBoxes
& rBoxes
= pLine
->GetTabBoxes();
493 for( sal_uInt16 i
= 0; i
< nCols
; ++i
)
495 SwTableBoxFormat
*pBoxF
;
498 sal_uInt8 nId
= SwTableAutoFormat::CountPos(i
, nCols
, n
, nRows
);
499 pBoxF
= ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr
, *pTAFormat
,
502 // Set the Paragraph/Character Attributes if needed
503 if( pTAFormat
->IsFont() || pTAFormat
->IsJustify() )
505 aCharSet
.ClearItem();
506 pTAFormat
->UpdateToSet( nId
, nRows
==1, nCols
==1, aCharSet
,
507 SwTableAutoFormatUpdateFlags::Char
, nullptr );
508 if( aCharSet
.Count() )
509 GetNodes()[ aNdIdx
.GetIndex()+1 ]->GetContentNode()->
513 else if( bDfltBorders
)
515 sal_uInt8 nBoxId
= (i
< nCols
- 1 ? 0 : 1) + (n
? 2 : 0 );
516 pBoxF
= ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr
, nCols
, nBoxId
);
521 // For AutoFormat on input: the columns are set when inserting the Table
522 // The Array contains the columns positions and not their widths!
525 nWidth
= (*pColArr
)[ i
+ 1 ] - (*pColArr
)[ i
];
526 if( pBoxF
->GetFrameSize().GetWidth() != nWidth
)
528 if( pBoxF
->HasWriterListeners() ) // Create new Format
530 SwTableBoxFormat
*pNewFormat
= MakeTableBoxFormat();
531 *pNewFormat
= *pBoxF
;
534 pBoxF
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nWidth
));
538 SwTableBox
*pBox
= new SwTableBox( pBoxF
, aNdIdx
, pLine
);
539 rBoxes
.insert( rBoxes
.begin() + i
, pBox
);
540 aNdIdx
+= SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
544 pTableNd
->MakeOwnFrames();
546 // To-Do - add 'SwExtraRedlineTable' also ?
547 if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
549 SwPaM
aPam( *pTableNd
->EndOfSectionNode(), *pTableNd
, SwNodeOffset(1) );
550 if( getIDocumentRedlineAccess().IsRedlineOn() )
551 getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, aPam
), true);
553 getIDocumentRedlineAccess().SplitRedline( aPam
);
556 getIDocumentState().SetModified();
557 CHECK_TABLE(rNdTable
);
561 SwTableNode
* SwNodes::InsertTable( SwNode
& rNd
,
563 SwTextFormatColl
* pContentTextColl
,
566 SwTextFormatColl
* pHeadlineTextColl
,
567 const SwAttrSet
* pAttrSet
)
572 // If Lines is given, create the Matrix from Lines and Boxes
573 if( !pHeadlineTextColl
|| !nLines
)
574 pHeadlineTextColl
= pContentTextColl
;
576 SwTableNode
* pTableNd
= new SwTableNode( rNd
);
577 SwEndNode
* pEndNd
= new SwEndNode( rNd
, *pTableNd
);
579 if( !nLines
) // For the for loop
582 SwTextFormatColl
* pTextColl
= pHeadlineTextColl
;
583 for( sal_uInt16 nL
= 0; nL
< nLines
; ++nL
)
585 for( sal_uInt16 nB
= 0; nB
< nBoxes
; ++nB
)
587 SwStartNode
* pSttNd
= new SwStartNode( *pEndNd
, SwNodeType::Start
,
588 SwTableBoxStartNode
);
589 pSttNd
->m_pStartOfSection
= pTableNd
;
591 SwTextNode
* pTmpNd
= new SwTextNode( *pEndNd
, pTextColl
);
593 // #i60422# Propagate some more attributes.
594 const SfxPoolItem
* pItem
= nullptr;
595 if ( nullptr != pAttrSet
)
597 static const sal_uInt16 aPropagateItems
[] = {
599 RES_CHRATR_FONT
, RES_CHRATR_FONTSIZE
,
600 RES_CHRATR_CJK_FONT
, RES_CHRATR_CJK_FONTSIZE
,
601 RES_CHRATR_CTL_FONT
, RES_CHRATR_CTL_FONTSIZE
, 0 };
603 const sal_uInt16
* pIdx
= aPropagateItems
;
606 if ( SfxItemState::SET
!= pTmpNd
->GetSwAttrSet().GetItemState( *pIdx
) &&
607 SfxItemState::SET
== pAttrSet
->GetItemState( *pIdx
, true, &pItem
) )
608 static_cast<SwContentNode
*>(pTmpNd
)->SetAttr(*pItem
);
613 new SwEndNode( *pEndNd
, *pSttNd
);
615 if ( nL
+ 1 >= nRepeat
)
616 pTextColl
= pContentTextColl
;
624 const SwTable
* SwDoc::TextToTable( const SwInsertTableOptions
& rInsTableOpts
,
625 const SwPaM
& rRange
, sal_Unicode cCh
,
627 const SwTableAutoFormat
* pTAFormat
)
629 // See if the selection contains a Table
630 auto [pStt
, pEnd
] = rRange
.StartEnd(); // SwPosition*
632 SwNodeOffset nCnt
= pStt
->GetNodeIndex();
633 for( ; nCnt
<= pEnd
->GetNodeIndex(); ++nCnt
)
634 if( !GetNodes()[ nCnt
]->IsTextNode() )
638 if (GetIDocumentUndoRedo().DoesUndo())
640 GetIDocumentUndoRedo().StartUndo(SwUndoId::TEXTTOTABLE
, nullptr);
643 // tdf#153115 first, remove all redlines; splitting them at cell boundaries
644 // would be tricky to implement, and it's unclear what the value of
645 // existing redlines is once it's been converted to a table
646 getIDocumentRedlineAccess().AcceptRedline(rRange
, true);
648 // Save first node in the selection if it is a context node
649 SwContentNode
* pSttContentNd
= pStt
->GetNode().GetContentNode();
651 SwPaM
aOriginal( *pStt
, *pEnd
);
652 pStt
= aOriginal
.GetMark();
653 pEnd
= aOriginal
.GetPoint();
655 SwUndoTextToTable
* pUndo
= nullptr;
656 if( GetIDocumentUndoRedo().DoesUndo() )
658 pUndo
= new SwUndoTextToTable( aOriginal
, rInsTableOpts
, cCh
,
659 o3tl::narrowing
<sal_uInt16
>(eAdjust
), pTAFormat
);
660 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
662 // Do not add splitting the TextNode to the Undo history
663 GetIDocumentUndoRedo().DoUndo( false );
666 ::PaMCorrAbs( aOriginal
, *pEnd
);
668 // Make sure that the range is on Node Edges
669 SwNodeRange
aRg( pStt
->GetNode(), pEnd
->GetNode() );
670 if( pStt
->GetContentIndex() )
671 getIDocumentContentOperations().SplitNode( *pStt
, false );
673 bool bEndContent
= 0 != pEnd
->GetContentIndex();
675 // Do not split at the End of a Line (except at the End of the Doc)
678 if( pEnd
->GetNode().GetContentNode()->Len() != pEnd
->GetContentIndex()
679 || pEnd
->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
681 getIDocumentContentOperations().SplitNode( *pEnd
, false );
682 const_cast<SwPosition
*>(pEnd
)->Adjust(SwNodeOffset(-1));
683 // A Node and at the End?
684 if( pStt
->GetNodeIndex() >= pEnd
->GetNodeIndex() )
691 if( aRg
.aEnd
.GetIndex() == aRg
.aStart
.GetIndex() )
693 OSL_FAIL( "empty range" );
697 // We always use Upper to insert the Table
698 SwNode2LayoutSaveUpperFrames
aNode2Layout( aRg
.aStart
.GetNode() );
700 GetIDocumentUndoRedo().DoUndo( nullptr != pUndo
);
702 // Create the Box/Line/Table construct
703 SwTableBoxFormat
* pBoxFormat
= MakeTableBoxFormat();
704 SwTableLineFormat
* pLineFormat
= MakeTableLineFormat();
705 SwTableFormat
* pTableFormat
= MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
707 // All Lines have a left-to-right Fill Order
708 pLineFormat
->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT
));
709 // The Table's SSize is USHRT_MAX
710 pTableFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, USHRT_MAX
));
711 if( !(rInsTableOpts
.mnInsMode
& SwInsertTableFlags::SplitLayout
) )
712 pTableFormat
->SetFormatAttr( SwFormatLayoutSplit( false ));
714 /* If the first node in the selection is a context node and if it
715 has an item FRAMEDIR set (no default) propagate the item to the
719 const SwAttrSet
& aNdSet
= pSttContentNd
->GetSwAttrSet();
720 if (const SvxFrameDirectionItem
*pItem
= aNdSet
.GetItemIfSet( RES_FRAMEDIR
) )
722 pTableFormat
->SetFormatAttr( *pItem
);
726 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
727 //until after RegisterToFormat is completed
728 bool bEnableSetModified
= getIDocumentState().IsEnableSetModified();
729 getIDocumentState().SetEnableSetModified(false);
731 SwTableNode
* pTableNd
= GetNodes().TextToTable(
732 aRg
, cCh
, pTableFormat
, pLineFormat
, pBoxFormat
,
733 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
), pUndo
);
735 SwTable
& rNdTable
= pTableNd
->GetTable();
737 const sal_uInt16 nRowsToRepeat
=
738 SwInsertTableFlags::Headline
== (rInsTableOpts
.mnInsMode
& SwInsertTableFlags::Headline
) ?
739 rInsTableOpts
.mnRowsToRepeat
:
741 rNdTable
.SetRowsToRepeat(nRowsToRepeat
);
743 bool bUseBoxFormat
= false;
744 if( !pBoxFormat
->HasWriterListeners() )
746 // The Box's Formats already have the right size, we must only set
747 // the right Border/AutoFormat.
748 bUseBoxFormat
= true;
749 pTableFormat
->SetFormatAttr( pBoxFormat
->GetFrameSize() );
751 eAdjust
= text::HoriOrientation::NONE
;
754 // Set Orientation in the Table's Format
755 pTableFormat
->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust
) );
756 rNdTable
.RegisterToFormat(*pTableFormat
);
758 if( pTAFormat
|| ( rInsTableOpts
.mnInsMode
& SwInsertTableFlags::DefaultBorder
) )
760 sal_uInt8 nBoxArrLen
= pTAFormat
? 16 : 4;
761 std::unique_ptr
< DfltBoxAttrList_t
> aBoxFormatArr1
;
762 std::optional
< std::vector
<SwTableBoxFormat
*> > aBoxFormatArr2
;
765 aBoxFormatArr1
.reset(new DfltBoxAttrList_t( nBoxArrLen
, nullptr ));
769 aBoxFormatArr2
= std::vector
<SwTableBoxFormat
*>( nBoxArrLen
, nullptr );
772 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_PARATR_LIST_END
-1> aCharSet( GetAttrPool() );
774 SwHistory
* pHistory
= pUndo
? &pUndo
->GetHistory() : nullptr;
776 SwTableBoxFormat
*pBoxF
= nullptr;
777 SwTableLines
& rLines
= rNdTable
.GetTabLines();
778 const SwTableLines::size_type nRows
= rLines
.size();
779 for( SwTableLines::size_type n
= 0; n
< nRows
; ++n
)
781 SwTableBoxes
& rBoxes
= rLines
[ n
]->GetTabBoxes();
782 const SwTableBoxes::size_type nCols
= rBoxes
.size();
783 for( SwTableBoxes::size_type i
= 0; i
< nCols
; ++i
)
785 SwTableBox
* pBox
= rBoxes
[ i
];
790 sal_uInt8 nId
= static_cast<sal_uInt8
>(!n
? 0 : (( n
+1 == nRows
)
791 ? 12 : (4 * (1 + ((n
-1) & 1 )))));
792 nId
= nId
+ static_cast<sal_uInt8
>(!i
? 0 :
793 ( i
+1 == nCols
? 3 : (1 + ((i
-1) & 1))));
795 ::lcl_SetDfltBoxAttr( *pBox
, *aBoxFormatArr1
, nId
, pTAFormat
);
798 bChgSz
= nullptr == (*aBoxFormatArr2
)[ nId
];
799 pBoxF
= ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2
,
800 *pTAFormat
, USHRT_MAX
, USHRT_MAX
, nId
);
803 // Set Paragraph/Character Attributes if needed
804 if( pTAFormat
->IsFont() || pTAFormat
->IsJustify() )
806 aCharSet
.ClearItem();
807 pTAFormat
->UpdateToSet( nId
, nRows
==1, nCols
==1, aCharSet
,
808 SwTableAutoFormatUpdateFlags::Char
, nullptr );
809 if( aCharSet
.Count() )
811 SwNodeOffset nSttNd
= pBox
->GetSttIdx()+1;
812 SwNodeOffset nEndNd
= pBox
->GetSttNd()->EndOfSectionIndex();
813 for( ; nSttNd
< nEndNd
; ++nSttNd
)
815 SwContentNode
* pNd
= GetNodes()[ nSttNd
]->GetContentNode();
820 SwRegHistory
aReg( pNd
, *pNd
, pHistory
);
821 pNd
->SetAttr( aCharSet
);
824 pNd
->SetAttr( aCharSet
);
832 sal_uInt8 nId
= (i
< nCols
- 1 ? 0 : 1) + (n
? 2 : 0 );
834 ::lcl_SetDfltBoxAttr( *pBox
, *aBoxFormatArr1
, nId
);
837 bChgSz
= nullptr == (*aBoxFormatArr2
)[ nId
];
838 pBoxF
= ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2
,
846 pBoxF
->SetFormatAttr( pBox
->GetFrameFormat()->GetFrameSize() );
847 pBox
->ChgFrameFormat( pBoxF
);
854 for( sal_uInt8 i
= 0; i
< nBoxArrLen
; ++i
)
856 delete (*aBoxFormatArr1
)[ i
];
861 // Check the boxes for numbers
862 if( IsInsTableFormatNum() )
864 for (size_t nBoxes
= rNdTable
.GetTabSortBoxes().size(); nBoxes
; )
866 ChkBoxNumFormat(*rNdTable
.GetTabSortBoxes()[ --nBoxes
], false);
870 SwNodeOffset nIdx
= pTableNd
->GetIndex();
871 aNode2Layout
.RestoreUpperFrames( GetNodes(), nIdx
, nIdx
+ 1 );
874 SwPaM
& rTmp
= const_cast<SwPaM
&>(rRange
); // Point always at the Start
876 rTmp
.GetPoint()->Assign( *pTableNd
);
877 GetNodes().GoNext( rTmp
.GetPoint() );
882 GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE
, nullptr );
885 getIDocumentState().SetEnableSetModified(bEnableSetModified
);
886 getIDocumentState().SetModified();
887 getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
891 static void lcl_RemoveBreaks(SwContentNode
& rNode
, SwTableFormat
*const pTableFormat
)
893 // delete old layout frames, new ones need to be created...
894 rNode
.DelFrames(nullptr);
896 if (!rNode
.IsTextNode())
901 SwTextNode
& rTextNode
= *rNode
.GetTextNode();
902 // remove PageBreaks/PageDesc/ColBreak
903 SfxItemSet
const* pSet
= rTextNode
.GetpSwAttrSet();
907 if (const SvxFormatBreakItem
* pItem
= pSet
->GetItemIfSet(RES_BREAK
, false))
911 pTableFormat
->SetFormatAttr(*pItem
);
913 rTextNode
.ResetAttr(RES_BREAK
);
914 pSet
= rTextNode
.GetpSwAttrSet();
917 const SwFormatPageDesc
* pPageDescItem
;
919 && (pPageDescItem
= pSet
->GetItemIfSet(RES_PAGEDESC
, false))
920 && pPageDescItem
->GetPageDesc())
924 pTableFormat
->SetFormatAttr(*pPageDescItem
);
926 rTextNode
.ResetAttr(RES_PAGEDESC
);
931 * balance lines in table, insert empty boxes so all lines have the size
934 lcl_BalanceTable(SwTable
& rTable
, size_t const nMaxBoxes
,
935 SwTableNode
& rTableNd
, SwTableBoxFormat
& rBoxFormat
, SwTextFormatColl
& rTextColl
,
936 SwUndoTextToTable
*const pUndo
, std::vector
<sal_uInt16
> *const pPositions
)
938 for (size_t n
= 0; n
< rTable
.GetTabLines().size(); ++n
)
940 SwTableLine
*const pCurrLine
= rTable
.GetTabLines()[ n
];
941 size_t const nBoxes
= pCurrLine
->GetTabBoxes().size();
942 if (nMaxBoxes
!= nBoxes
)
944 rTableNd
.GetNodes().InsBoxen(&rTableNd
, pCurrLine
, &rBoxFormat
, &rTextColl
,
945 nullptr, nBoxes
, nMaxBoxes
- nBoxes
);
949 for (size_t i
= nBoxes
; i
< nMaxBoxes
; ++i
)
951 pUndo
->AddFillBox( *pCurrLine
->GetTabBoxes()[i
] );
955 // if the first line is missing boxes, the width array is useless!
956 if (!n
&& pPositions
)
965 lcl_SetTableBoxWidths(SwTable
& rTable
, size_t const nMaxBoxes
,
966 SwTableBoxFormat
& rBoxFormat
, SwDoc
& rDoc
,
967 std::vector
<sal_uInt16
> *const pPositions
)
969 if (pPositions
&& !pPositions
->empty())
971 SwTableLines
& rLns
= rTable
.GetTabLines();
972 sal_uInt16 nLastPos
= 0;
973 for (size_t n
= 0; n
< pPositions
->size(); ++n
)
975 SwTableBoxFormat
*pNewFormat
= rDoc
.MakeTableBoxFormat();
976 pNewFormat
->SetFormatAttr(
977 SwFormatFrameSize(SwFrameSize::Variable
, (*pPositions
)[n
] - nLastPos
));
978 for (size_t nTmpLine
= 0; nTmpLine
< rLns
.size(); ++nTmpLine
)
980 // Have to do an Add here, because the BoxFormat
981 // is still needed by the caller
982 pNewFormat
->Add( rLns
[ nTmpLine
]->GetTabBoxes()[ n
] );
985 nLastPos
= (*pPositions
)[ n
];
988 // propagate size upwards from format, so the table gets the right size
989 SAL_WARN_IF(rBoxFormat
.HasWriterListeners(), "sw.core",
990 "who is still registered in the format?");
991 rBoxFormat
.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nLastPos
));
995 size_t nWidth
= nMaxBoxes
? USHRT_MAX
/ nMaxBoxes
: USHRT_MAX
;
996 rBoxFormat
.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable
, nWidth
));
1000 SwTableNode
* SwNodes::TextToTable( const SwNodeRange
& rRange
, sal_Unicode cCh
,
1001 SwTableFormat
* pTableFormat
,
1002 SwTableLineFormat
* pLineFormat
,
1003 SwTableBoxFormat
* pBoxFormat
,
1004 SwTextFormatColl
* pTextColl
,
1005 SwUndoTextToTable
* pUndo
)
1007 if( rRange
.aStart
>= rRange
.aEnd
)
1010 SwTableNode
* pTableNd
= new SwTableNode( rRange
.aStart
.GetNode() );
1011 new SwEndNode( rRange
.aEnd
.GetNode(), *pTableNd
);
1013 SwDoc
& rDoc
= GetDoc();
1014 std::vector
<sal_uInt16
> aPosArr
;
1015 SwTable
& rTable
= pTableNd
->GetTable();
1017 sal_uInt16 nBoxes
, nLines
, nMaxBoxes
= 0;
1019 SwNodeIndex
aSttIdx( *pTableNd
, 1 );
1020 SwNodeIndex
aEndIdx( rRange
.aEnd
, -1 );
1021 for( nLines
= 0, nBoxes
= 0;
1022 aSttIdx
.GetIndex() < aEndIdx
.GetIndex();
1023 aSttIdx
+= SwNodeOffset(2), nLines
++, nBoxes
= 0 )
1025 SwTextNode
* pTextNd
= aSttIdx
.GetNode().GetTextNode();
1026 OSL_ENSURE( pTextNd
, "Only add TextNodes to the Table" );
1028 if( !nLines
&& 0x0b == cCh
)
1032 // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
1033 SwTextFrameInfo
aFInfo( static_cast<SwTextFrame
*>(pTextNd
->getLayoutFrame( pTextNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
1034 if( aFInfo
.IsOneLine() ) // only makes sense in this case
1036 OUString
const& rText(pTextNd
->GetText());
1037 for (sal_Int32 nChPos
= 0; nChPos
< rText
.getLength(); ++nChPos
)
1039 if (rText
[nChPos
] == cCh
)
1041 // sw_redlinehide: no idea if this makes any sense...
1042 TextFrameIndex
const nPos(aFInfo
.GetFrame()->MapModelToView(pTextNd
, nChPos
));
1043 aPosArr
.push_back( o3tl::narrowing
<sal_uInt16
>(
1044 aFInfo
.GetCharPos(nPos
+TextFrameIndex(1), false)) );
1049 o3tl::narrowing
<sal_uInt16
>(aFInfo
.GetFrame()->IsVertical() ?
1050 aFInfo
.GetFrame()->getFramePrintArea().Bottom() :
1051 aFInfo
.GetFrame()->getFramePrintArea().Right()) );
1056 lcl_RemoveBreaks(*pTextNd
, (0 == nLines
) ? pTableFormat
: nullptr);
1058 // Set the TableNode as StartNode for all TextNodes in the Table
1059 pTextNd
->m_pStartOfSection
= pTableNd
;
1061 SwTableLine
* pLine
= new SwTableLine( pLineFormat
, 1, nullptr );
1062 rTable
.GetTabLines().insert(rTable
.GetTabLines().begin() + nLines
, pLine
);
1064 SwStartNode
* pSttNd
;
1065 SwPosition
aCntPos( aSttIdx
, pTextNd
, 0);
1067 const std::shared_ptr
< sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
1068 pContentStore
->Save(rDoc
, aSttIdx
.GetIndex(), SAL_MAX_INT32
);
1070 if( T2T_PARA
!= cCh
)
1072 for (sal_Int32 nChPos
= 0; nChPos
< pTextNd
->GetText().getLength();)
1074 if (pTextNd
->GetText()[nChPos
] == cCh
)
1076 aCntPos
.SetContent(nChPos
);
1077 std::function
<void (SwTextNode
*, sw::mark::RestoreMode
, bool)> restoreFunc(
1078 [&](SwTextNode
*const pNewNode
, sw::mark::RestoreMode
const eMode
, bool)
1080 if (!pContentStore
->Empty())
1082 pContentStore
->Restore(*pNewNode
, nChPos
, nChPos
+ 1, eMode
);
1085 SwContentNode
*const pNewNd
=
1086 pTextNd
->SplitContentNode(aCntPos
, &restoreFunc
);
1088 // Delete separator and correct search string
1089 pTextNd
->EraseText( aCntPos
, 1 );
1092 // Set the TableNode as StartNode for all TextNodes in the Table
1093 const SwNodeIndex
aTmpIdx( aCntPos
.GetNode(), -1 );
1094 pSttNd
= new SwStartNode( aTmpIdx
.GetNode(), SwNodeType::Start
,
1095 SwTableBoxStartNode
);
1096 new SwEndNode( aCntPos
.GetNode(), *pSttNd
);
1097 pNewNd
->m_pStartOfSection
= pSttNd
;
1099 // Assign Section to the Box
1100 pBox
= new SwTableBox( pBoxFormat
, *pSttNd
, pLine
);
1101 pLine
->GetTabBoxes().insert( pLine
->GetTabBoxes().begin() + nBoxes
++, pBox
);
1110 // Now for the last substring
1111 if( !pContentStore
->Empty())
1112 pContentStore
->Restore( *pTextNd
, pTextNd
->GetText().getLength(), pTextNd
->GetText().getLength()+1 );
1114 pSttNd
= new SwStartNode( aCntPos
.GetNode(), SwNodeType::Start
, SwTableBoxStartNode
);
1115 const SwNodeIndex
aTmpIdx( aCntPos
.GetNode(), 1 );
1116 new SwEndNode( aTmpIdx
.GetNode(), *pSttNd
);
1117 pTextNd
->m_pStartOfSection
= pSttNd
;
1119 pBox
= new SwTableBox( pBoxFormat
, *pSttNd
, pLine
);
1120 pLine
->GetTabBoxes().insert( pLine
->GetTabBoxes().begin() + nBoxes
++, pBox
);
1121 if( nMaxBoxes
< nBoxes
)
1125 lcl_BalanceTable(rTable
, nMaxBoxes
, *pTableNd
, *pBoxFormat
, *pTextColl
,
1127 lcl_SetTableBoxWidths(rTable
, nMaxBoxes
, *pBoxFormat
, rDoc
, &aPosArr
);
1132 const SwTable
* SwDoc::TextToTable( const std::vector
< std::vector
<SwNodeRange
> >& rTableNodes
)
1134 if (rTableNodes
.empty())
1137 const std::vector
<SwNodeRange
>& rFirstRange
= *rTableNodes
.begin();
1139 if (rFirstRange
.empty())
1142 const std::vector
<SwNodeRange
>& rLastRange
= *rTableNodes
.rbegin();
1144 if (rLastRange
.empty())
1147 /* Save first node in the selection if it is a content node. */
1148 SwContentNode
* pSttContentNd
= rFirstRange
.begin()->aStart
.GetNode().GetContentNode();
1150 const SwNodeRange
& rStartRange
= *rFirstRange
.begin();
1151 const SwNodeRange
& rEndRange
= *rLastRange
.rbegin();
1153 //!!! not necessarily TextNodes !!!
1154 SwPaM
aOriginal( rStartRange
.aStart
, rEndRange
.aEnd
);
1155 const SwPosition
*pStt
= aOriginal
.GetMark();
1156 SwPosition
*pEnd
= aOriginal
.GetPoint();
1158 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
1161 // Do not add splitting the TextNode to the Undo history
1162 GetIDocumentUndoRedo().DoUndo(false);
1165 ::PaMCorrAbs( aOriginal
, *pEnd
);
1167 // make sure that the range is on Node Edges
1168 SwNodeRange
aRg( pStt
->GetNode(), pEnd
->GetNode() );
1169 if( pStt
->GetContentIndex() )
1170 getIDocumentContentOperations().SplitNode( *pStt
, false );
1172 bool bEndContent
= 0 != pEnd
->GetContentIndex();
1174 // Do not split at the End of a Line (except at the End of the Doc)
1177 if( pEnd
->GetNode().GetContentNode()->Len() != pEnd
->GetContentIndex()
1178 || pEnd
->GetNodeIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
1180 getIDocumentContentOperations().SplitNode( *pEnd
, false );
1181 pEnd
->Adjust(SwNodeOffset(-1));
1182 // A Node and at the End?
1183 if( pStt
->GetNodeIndex() >= pEnd
->GetNodeIndex() )
1190 assert(aRg
.aEnd
.GetNode() == pEnd
->GetNode());
1191 assert(aRg
.aStart
.GetNode() == pStt
->GetNode());
1192 if( aRg
.aEnd
.GetIndex() == aRg
.aStart
.GetIndex() )
1194 OSL_FAIL( "empty range" );
1200 // TODO: this is not Undo-able - only good enough for file import
1201 IDocumentRedlineAccess
& rIDRA(getIDocumentRedlineAccess());
1202 SwNodeIndex
const prev(rTableNodes
.begin()->begin()->aStart
, -1);
1203 SwNodeIndex
const* pPrev(&prev
);
1204 // pPrev could point to non-textnode now
1205 for (const auto& rRow
: rTableNodes
)
1207 for (const auto& rCell
: rRow
)
1209 assert(SwNodeIndex(*pPrev
, +1) == rCell
.aStart
);
1210 SwPaM
pam(rCell
.aStart
, 0, *pPrev
,
1211 (pPrev
->GetNode().IsContentNode())
1212 ? pPrev
->GetNode().GetContentNode()->Len() : 0);
1213 rIDRA
.SplitRedline(pam
);
1214 pPrev
= &rCell
.aEnd
;
1217 // another one to break between last cell and node after table
1218 SwPaM
pam(pPrev
->GetNode(), SwNodeOffset(+1), 0,
1219 pPrev
->GetNode(), SwNodeOffset(0),
1220 (pPrev
->GetNode().IsContentNode())
1221 ? pPrev
->GetNode().GetContentNode()->Len() : 0);
1222 rIDRA
.SplitRedline(pam
);
1225 // We always use Upper to insert the Table
1226 SwNode2LayoutSaveUpperFrames
aNode2Layout( aRg
.aStart
.GetNode() );
1228 GetIDocumentUndoRedo().DoUndo(bUndo
);
1230 // Create the Box/Line/Table construct
1231 SwTableBoxFormat
* pBoxFormat
= MakeTableBoxFormat();
1232 SwTableLineFormat
* pLineFormat
= MakeTableLineFormat();
1233 SwTableFormat
* pTableFormat
= MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
1235 // All Lines have a left-to-right Fill Order
1236 pLineFormat
->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT
));
1237 // The Table's SSize is USHRT_MAX
1238 pTableFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, USHRT_MAX
));
1240 /* If the first node in the selection is a context node and if it
1241 has an item FRAMEDIR set (no default) propagate the item to the
1245 const SwAttrSet
& aNdSet
= pSttContentNd
->GetSwAttrSet();
1246 if (const SvxFrameDirectionItem
* pItem
= aNdSet
.GetItemIfSet( RES_FRAMEDIR
))
1248 pTableFormat
->SetFormatAttr( *pItem
);
1252 //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
1253 //until after RegisterToFormat is completed
1254 bool bEnableSetModified
= getIDocumentState().IsEnableSetModified();
1255 getIDocumentState().SetEnableSetModified(false);
1257 SwTableNode
* pTableNd
= GetNodes().TextToTable(
1258 rTableNodes
, pTableFormat
, pLineFormat
, pBoxFormat
);
1260 SwTable
& rNdTable
= pTableNd
->GetTable();
1261 rNdTable
.RegisterToFormat(*pTableFormat
);
1263 if( !pBoxFormat
->HasWriterListeners() )
1265 // The Box's Formats already have the right size, we must only set
1266 // the right Border/AutoFormat.
1267 pTableFormat
->SetFormatAttr( pBoxFormat
->GetFrameSize() );
1271 SwNodeOffset nIdx
= pTableNd
->GetIndex();
1272 aNode2Layout
.RestoreUpperFrames( GetNodes(), nIdx
, nIdx
+ 1 );
1274 getIDocumentState().SetEnableSetModified(bEnableSetModified
);
1275 getIDocumentState().SetModified();
1276 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1280 void SwNodes::ExpandRangeForTableBox(const SwNodeRange
& rRange
, std::optional
<SwNodeRange
>& rExpandedRange
)
1282 bool bChanged
= false;
1284 SwNodeIndex aNewStart
= rRange
.aStart
;
1285 SwNodeIndex aNewEnd
= rRange
.aEnd
;
1287 SwNodeIndex aEndIndex
= rRange
.aEnd
;
1288 SwNodeIndex aIndex
= rRange
.aStart
;
1290 while (aIndex
< aEndIndex
)
1292 SwNode
& rNode
= aIndex
.GetNode();
1294 if (rNode
.IsStartNode())
1296 // advance aIndex to the end node of this start node
1297 SwNode
* pEndNode
= rNode
.EndOfSectionNode();
1300 if (aIndex
> aNewEnd
)
1306 else if (rNode
.IsEndNode())
1308 SwNode
* pStartNode
= rNode
.StartOfSectionNode();
1309 if (pStartNode
->GetIndex() < aNewStart
.GetIndex())
1311 aNewStart
= *pStartNode
;
1316 if (aIndex
< aEndIndex
)
1320 SwNode
* pNode
= &aIndex
.GetNode();
1321 while (pNode
->IsEndNode() && aIndex
< Count() - 1)
1323 SwNode
* pStartNode
= pNode
->StartOfSectionNode();
1324 aNewStart
= *pStartNode
;
1329 pNode
= &aIndex
.GetNode();
1333 rExpandedRange
.emplace(aNewStart
, aNewEnd
);
1337 lcl_SetTableBoxWidths2(SwTable
& rTable
, size_t const nMaxBoxes
,
1338 SwTableBoxFormat
& rBoxFormat
, SwDoc
& rDoc
)
1340 // rhbz#820283, fdo#55462: set default box widths so table width is covered
1341 SwTableLines
& rLines
= rTable
.GetTabLines();
1342 for (size_t nTmpLine
= 0; nTmpLine
< rLines
.size(); ++nTmpLine
)
1344 SwTableBoxes
& rBoxes
= rLines
[nTmpLine
]->GetTabBoxes();
1345 assert(!rBoxes
.empty()); // ensured by convertToTable
1346 size_t const nMissing
= nMaxBoxes
- rBoxes
.size();
1349 // default width for box at the end of an incomplete line
1350 SwTableBoxFormat
*const pNewFormat
= rDoc
.MakeTableBoxFormat();
1351 size_t nWidth
= nMaxBoxes
? USHRT_MAX
/ nMaxBoxes
: USHRT_MAX
;
1352 pNewFormat
->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable
,
1353 nWidth
* (nMissing
+ 1)) );
1354 pNewFormat
->Add(rBoxes
.back());
1357 size_t nWidth
= nMaxBoxes
? USHRT_MAX
/ nMaxBoxes
: USHRT_MAX
;
1358 // default width for all boxes not at the end of an incomplete line
1359 rBoxFormat
.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable
, nWidth
));
1362 SwTableNode
* SwNodes::TextToTable( const SwNodes::TableRanges_t
& rTableNodes
,
1363 SwTableFormat
* pTableFormat
,
1364 SwTableLineFormat
* pLineFormat
,
1365 SwTableBoxFormat
* pBoxFormat
)
1367 if( rTableNodes
.empty() )
1370 SwTableNode
* pTableNd
= new SwTableNode( rTableNodes
.begin()->begin()->aStart
.GetNode() );
1371 //insert the end node after the last text node
1372 SwNodeIndex
aInsertIndex( rTableNodes
.rbegin()->rbegin()->aEnd
);
1375 //!! ownership will be transferred in c-tor to SwNodes array.
1376 //!! Thus no real problem here...
1377 new SwEndNode( aInsertIndex
.GetNode(), *pTableNd
);
1379 SwDoc
& rDoc
= GetDoc();
1380 SwTable
& rTable
= pTableNd
->GetTable();
1382 sal_uInt16 nLines
, nMaxBoxes
= 0;
1384 SwNodeIndex aNodeIndex
= rTableNodes
.begin()->begin()->aStart
;
1385 // delete frames of all contained content nodes
1386 for( nLines
= 0; aNodeIndex
<= rTableNodes
.rbegin()->rbegin()->aEnd
; ++aNodeIndex
,++nLines
)
1388 SwNode
& rNode
= aNodeIndex
.GetNode();
1389 if( rNode
.IsContentNode() )
1391 lcl_RemoveBreaks(static_cast<SwContentNode
&>(rNode
),
1392 (0 == nLines
) ? pTableFormat
: nullptr);
1397 for( const auto& rRow
: rTableNodes
)
1399 sal_uInt16 nBoxes
= 0;
1400 SwTableLine
* pLine
= new SwTableLine( pLineFormat
, 1, nullptr );
1401 rTable
.GetTabLines().insert(rTable
.GetTabLines().begin() + nLines
, pLine
);
1403 for( const auto& rCell
: rRow
)
1405 SwNodeIndex
aCellEndIdx(rCell
.aEnd
);
1407 SwStartNode
* pSttNd
= new SwStartNode( rCell
.aStart
.GetNode(), SwNodeType::Start
,
1408 SwTableBoxStartNode
);
1410 // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
1411 // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
1412 // So this statement is only executed for the side-effect.
1413 new SwEndNode( aCellEndIdx
.GetNode(), *pSttNd
);
1415 //set the start node on all node of the current cell
1416 SwNodeIndex aCellNodeIdx
= rCell
.aStart
;
1417 for(;aCellNodeIdx
<= rCell
.aEnd
; ++aCellNodeIdx
)
1419 aCellNodeIdx
.GetNode().m_pStartOfSection
= pSttNd
;
1420 //skip start/end node pairs
1421 if( aCellNodeIdx
.GetNode().IsStartNode() )
1422 aCellNodeIdx
.Assign(*aCellNodeIdx
.GetNode().EndOfSectionNode());
1425 // assign Section to the Box
1426 pBox
= new SwTableBox( pBoxFormat
, *pSttNd
, pLine
);
1427 pLine
->GetTabBoxes().insert( pLine
->GetTabBoxes().begin() + nBoxes
++, pBox
);
1429 if( nMaxBoxes
< nBoxes
)
1435 lcl_SetTableBoxWidths2(rTable
, nMaxBoxes
, *pBoxFormat
, rDoc
);
1443 bool SwDoc::TableToText( const SwTableNode
* pTableNd
, sal_Unicode cCh
)
1449 // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
1450 // the table cursor.
1451 SwEditShell
* pESh
= GetEditShell();
1452 if (pESh
&& pESh
->IsTableMode())
1455 SwNodeRange
aRg( *pTableNd
, SwNodeOffset(0), *pTableNd
->EndOfSectionNode() );
1456 std::unique_ptr
<SwUndoTableToText
> pUndo
;
1457 SwNodeRange
* pUndoRg
= nullptr;
1458 if (GetIDocumentUndoRedo().DoesUndo())
1460 GetIDocumentUndoRedo().ClearRedo();
1461 pUndoRg
= new SwNodeRange( aRg
.aStart
, SwNodeOffset(-1), aRg
.aEnd
, SwNodeOffset(+1) );
1462 pUndo
.reset(new SwUndoTableToText( pTableNd
->GetTable(), cCh
));
1465 const_cast<SwTable
*>(&pTableNd
->GetTable())->SwitchFormulasToExternalRepresentation();
1467 bool bRet
= GetNodes().TableToText( aRg
, cCh
, pUndo
.get() );
1472 pUndo
->SetRange( *pUndoRg
);
1473 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
1478 getIDocumentState().SetModified();
1486 * Use the ForEach method from PtrArray to recreate Text from a Table.
1487 * The Boxes can also contain Lines!
1491 SwTextNode
* pLastNd
;
1493 SwUndoTableToText
* pUndo
;
1496 DelTabPara( SwNodes
& rNodes
, sal_Unicode cChar
, SwUndoTableToText
* pU
) :
1497 pLastNd(nullptr), rNds( rNodes
), pUndo( pU
), cCh( cChar
) {}
1502 // Forward declare so that the Lines and Boxes can use recursion
1503 static void lcl_DelBox( SwTableBox
* pBox
, DelTabPara
* pDelPara
);
1505 static void lcl_DelLine( SwTableLine
* pLine
, DelTabPara
* pPara
)
1507 assert(pPara
&& "The parameters are missing!");
1508 DelTabPara
aPara( *pPara
);
1509 for( auto& rpBox
: pLine
->GetTabBoxes() )
1510 lcl_DelBox(rpBox
, &aPara
);
1511 if( pLine
->GetUpper() ) // Is there a parent Box?
1512 // Return the last TextNode
1513 pPara
->pLastNd
= aPara
.pLastNd
;
1516 static void lcl_DelBox( SwTableBox
* pBox
, DelTabPara
* pDelPara
)
1518 assert(pDelPara
&& "The parameters are missing");
1520 // Delete the Box's Lines
1521 if( !pBox
->GetTabLines().empty() )
1523 for( SwTableLine
* pLine
: pBox
->GetTabLines() )
1524 lcl_DelLine( pLine
, pDelPara
);
1528 SwDoc
& rDoc
= pDelPara
->rNds
.GetDoc();
1529 SwNodeRange
aDelRg( *pBox
->GetSttNd(), SwNodeOffset(0),
1530 *pBox
->GetSttNd()->EndOfSectionNode() );
1531 // Delete the Section
1532 pDelPara
->rNds
.SectionUp( &aDelRg
);
1533 const SwTextNode
* pCurTextNd
= nullptr;
1534 if (T2T_PARA
!= pDelPara
->cCh
&& pDelPara
->pLastNd
)
1535 pCurTextNd
= aDelRg
.aStart
.GetNode().GetTextNode();
1536 if (nullptr != pCurTextNd
)
1538 // Join the current text node with the last from the previous box if possible
1539 SwNodeOffset nNdIdx
= aDelRg
.aStart
.GetIndex();
1541 if( pDelPara
->pLastNd
== &aDelRg
.aStart
.GetNode() )
1543 // Inserting the separator
1544 SwContentIndex
aCntIdx( pDelPara
->pLastNd
,
1545 pDelPara
->pLastNd
->GetText().getLength());
1546 pDelPara
->pLastNd
->InsertText( OUString(pDelPara
->cCh
), aCntIdx
,
1547 SwInsertFlags::EMPTYEXPAND
);
1548 if( pDelPara
->pUndo
)
1549 pDelPara
->pUndo
->AddBoxPos( rDoc
, nNdIdx
, aDelRg
.aEnd
.GetIndex(),
1550 aCntIdx
.GetIndex() );
1552 const std::shared_ptr
<sw::mark::ContentIdxStore
> pContentStore(sw::mark::ContentIdxStore::Create());
1553 const sal_Int32 nOldTextLen
= aCntIdx
.GetIndex();
1554 pContentStore
->Save(rDoc
, nNdIdx
, SAL_MAX_INT32
);
1556 pDelPara
->pLastNd
->JoinNext();
1558 if( !pContentStore
->Empty() )
1559 pContentStore
->Restore( rDoc
, pDelPara
->pLastNd
->GetIndex(), nOldTextLen
);
1561 else if( pDelPara
->pUndo
)
1564 pDelPara
->pUndo
->AddBoxPos( rDoc
, nNdIdx
, aDelRg
.aEnd
.GetIndex() );
1567 else if( pDelPara
->pUndo
)
1568 pDelPara
->pUndo
->AddBoxPos( rDoc
, aDelRg
.aStart
.GetIndex(), aDelRg
.aEnd
.GetIndex() );
1570 pDelPara
->pLastNd
= aDelRg
.aEnd
.GetNode().GetTextNode();
1572 // Do not take over the NumberFormatting's adjustment
1573 if( pDelPara
->pLastNd
&& pDelPara
->pLastNd
->HasSwAttrSet() )
1574 pDelPara
->pLastNd
->ResetAttr( RES_PARATR_ADJUST
);
1578 bool SwNodes::TableToText( const SwNodeRange
& rRange
, sal_Unicode cCh
,
1579 SwUndoTableToText
* pUndo
)
1581 // Is a Table selected?
1582 if (rRange
.aStart
.GetIndex() >= rRange
.aEnd
.GetIndex())
1584 SwTableNode
*const pTableNd(rRange
.aStart
.GetNode().GetTableNode());
1585 if (nullptr == pTableNd
||
1586 &rRange
.aEnd
.GetNode() != pTableNd
->EndOfSectionNode() )
1589 // If the Table was alone in a Section, create the Frames via the Table's Upper
1590 std::optional
<SwNode2LayoutSaveUpperFrames
> oNode2Layout
;
1591 SwNode
* pFrameNd
= FindPrvNxtFrameNode( rRange
.aStart
.GetNode(), &rRange
.aEnd
.GetNode() );
1592 SwNodeIndex
aFrameIdx( pFrameNd
? *pFrameNd
: rRange
.aStart
.GetNode() );
1594 // Collect all Uppers
1595 oNode2Layout
.emplace(*pTableNd
);
1597 // Delete the Frames
1598 pTableNd
->DelFrames();
1600 // "Delete" the Table and merge all Lines/Boxes
1601 DelTabPara
aDelPara( *this, cCh
, pUndo
);
1602 for( SwTableLine
*pLine
: pTableNd
->m_pTable
->GetTabLines() )
1603 lcl_DelLine( pLine
, &aDelPara
);
1605 // We just created a TextNode with fitting separator for every TableLine.
1606 // Now we only need to delete the TableSection and create the Frames for the
1608 SwNodeRange
aDelRg( rRange
.aStart
, rRange
.aEnd
);
1610 // If the Table has PageDesc/Break Attributes, carry them over to the
1614 const SfxItemSet
& rTableSet
= pTableNd
->m_pTable
->GetFrameFormat()->GetAttrSet();
1615 const SvxFormatBreakItem
* pBreak
= rTableSet
.GetItemIfSet( RES_BREAK
, false );
1616 const SwFormatPageDesc
* pDesc
= rTableSet
.GetItemIfSet( RES_PAGEDESC
, false );
1618 if( pBreak
|| pDesc
)
1620 SwNodeIndex
aIdx( *pTableNd
);
1621 SwContentNode
* pCNd
= GoNext( &aIdx
);
1623 pCNd
->SetAttr( *pBreak
);
1625 pCNd
->SetAttr( *pDesc
);
1629 SectionUp( &aDelRg
); // Delete this Section and by that the Table
1631 SwNodeOffset nStt
= aDelRg
.aStart
.GetIndex(), nEnd
= aDelRg
.aEnd
.GetIndex();
1634 oNode2Layout
->RestoreUpperFrames( *this,
1635 aDelRg
.aStart
.GetIndex(), aDelRg
.aEnd
.GetIndex() );
1636 oNode2Layout
.reset();
1640 SwContentNode
*pCNd
;
1641 SwSectionNode
*pSNd
;
1642 while( aDelRg
.aStart
.GetIndex() < nEnd
)
1644 pCNd
= aDelRg
.aStart
.GetNode().GetContentNode();
1645 if( nullptr != pCNd
)
1647 if( pFrameNd
->IsContentNode() )
1648 static_cast<SwContentNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(*pCNd
);
1649 else if( pFrameNd
->IsTableNode() )
1650 static_cast<SwTableNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(aDelRg
.aStart
);
1651 else if( pFrameNd
->IsSectionNode() )
1652 static_cast<SwSectionNode
*>(pFrameNd
)->MakeFramesForAdjacentContentNode(aDelRg
.aStart
);
1657 pSNd
= aDelRg
.aStart
.GetNode().GetSectionNode();
1660 if( !pSNd
->GetSection().IsHidden() && !pSNd
->IsContentHidden() )
1662 pSNd
->MakeOwnFrames(&aFrameIdx
, &aDelRg
.aEnd
);
1665 aDelRg
.aStart
= *pSNd
->EndOfSectionNode();
1672 // #i28006# Fly frames have to be restored even if the table was
1673 // #alone in the section
1674 for(sw::SpzFrameFormat
* pFly
: *GetDoc().GetSpzFrameFormats())
1676 SwFrameFormat
*const pFormat
= pFly
;
1677 const SwFormatAnchor
& rAnchor
= pFormat
->GetAnchor();
1678 SwNode
const*const pAnchorNode
= rAnchor
.GetAnchorNode();
1680 ((RndStdIds::FLY_AT_PARA
== rAnchor
.GetAnchorId()) ||
1681 (RndStdIds::FLY_AT_CHAR
== rAnchor
.GetAnchorId())) &&
1682 nStt
<= pAnchorNode
->GetIndex() &&
1683 pAnchorNode
->GetIndex() < nEnd
)
1685 pFormat
->MakeFrames();
1693 * Inserting Columns/Rows
1695 void SwDoc::InsertCol( const SwCursor
& rCursor
, sal_uInt16 nCnt
, bool bBehind
)
1697 if( !::CheckSplitCells( rCursor
, nCnt
+ 1, SwTableSearchType::Col
) )
1700 // Find the Boxes via the Layout
1702 ::GetTableSel( rCursor
, aBoxes
, SwTableSearchType::Col
);
1704 if( !aBoxes
.empty() )
1705 InsertCol( aBoxes
, nCnt
, bBehind
);
1708 bool SwDoc::InsertCol( const SwSelBoxes
& rBoxes
, sal_uInt16 nCnt
, bool bBehind
, bool bInsertDummy
)
1710 OSL_ENSURE( !rBoxes
.empty(), "No valid Box list" );
1711 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
1715 SwTable
& rTable
= pTableNd
->GetTable();
1716 if( dynamic_cast<const SwDDETable
*>( &rTable
) != nullptr)
1719 SwTableSortBoxes aTmpLst
;
1720 std::unique_ptr
<SwUndoTableNdsChg
> pUndo
;
1721 if (GetIDocumentUndoRedo().DoesUndo())
1723 pUndo
.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL
, rBoxes
, *pTableNd
,
1724 0, 0, nCnt
, bBehind
, false ));
1725 aTmpLst
.insert( rTable
.GetTabSortBoxes() );
1730 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
1732 rTable
.SwitchFormulasToInternalRepresentation();
1733 bRet
= rTable
.InsertCol(*this, rBoxes
, nCnt
, bBehind
, bInsertDummy
);
1736 getIDocumentState().SetModified();
1737 ::ClearFEShellTabCols(*this, nullptr);
1738 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1744 pUndo
->SaveNewBoxes( *pTableNd
, aTmpLst
);
1745 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
1750 void SwDoc::InsertRow( const SwCursor
& rCursor
, sal_uInt16 nCnt
, bool bBehind
)
1752 // Find the Boxes via the Layout
1754 GetTableSel( rCursor
, aBoxes
, SwTableSearchType::Row
);
1756 if( !aBoxes
.empty() )
1757 InsertRow( aBoxes
, nCnt
, bBehind
);
1760 bool SwDoc::InsertRow( const SwSelBoxes
& rBoxes
, sal_uInt16 nCnt
, bool bBehind
, bool bInsertDummy
)
1762 OSL_ENSURE( !rBoxes
.empty(), "No valid Box list" );
1763 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
1767 SwTable
& rTable
= pTableNd
->GetTable();
1768 if( dynamic_cast<const SwDDETable
*>( &rTable
) != nullptr)
1771 SwTableSortBoxes aTmpLst
;
1772 std::unique_ptr
<SwUndoTableNdsChg
> pUndo
;
1773 if (GetIDocumentUndoRedo().DoesUndo())
1775 pUndo
.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW
,rBoxes
, *pTableNd
,
1776 0, 0, nCnt
, bBehind
, false ));
1777 aTmpLst
.insert( rTable
.GetTabSortBoxes() );
1782 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
1783 rTable
.SwitchFormulasToInternalRepresentation();
1785 bRet
= rTable
.InsertRow( this, rBoxes
, nCnt
, bBehind
, bInsertDummy
);
1788 getIDocumentState().SetModified();
1789 ::ClearFEShellTabCols(*this, nullptr);
1790 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
1796 pUndo
->SaveNewBoxes( *pTableNd
, aTmpLst
);
1797 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
1804 * Deleting Columns/Rows
1806 void SwDoc::DeleteRow( const SwCursor
& rCursor
)
1808 // Find the Boxes via the Layout
1810 GetTableSel( rCursor
, aBoxes
, SwTableSearchType::Row
);
1811 if( ::HasProtectedCells( aBoxes
))
1814 // Remove the Cursor from the to-be-deleted Section.
1815 // The Cursor is placed after the table, except for
1816 // - when there's another Line, we place it in that one
1817 // - when a Line precedes it, we place it in that one
1819 SwTableNode
* pTableNd
= rCursor
.GetPointNode().FindTableNode();
1821 if(dynamic_cast<const SwDDETable
*>( & pTableNd
->GetTable()) != nullptr)
1824 // Find all Boxes/Lines
1825 FndBox_
aFndBox( nullptr, nullptr );
1827 FndPara
aPara( aBoxes
, &aFndBox
);
1828 ForEach_FndLineCopyCol( pTableNd
->GetTable().GetTabLines(), &aPara
);
1831 if( aFndBox
.GetLines().empty() )
1834 if (SwEditShell
* pESh
= GetEditShell())
1837 // FIXME: actually we should be iterating over all Shells!
1840 FndBox_
* pFndBox
= &aFndBox
;
1841 while( 1 == pFndBox
->GetLines().size() &&
1842 1 == pFndBox
->GetLines().front()->GetBoxes().size() )
1844 FndBox_
*const pTmp
= pFndBox
->GetLines().front()->GetBoxes()[0].get();
1845 if( pTmp
->GetBox()->GetSttNd() )
1846 break; // Else it gets too far
1850 SwTableLine
* pDelLine
= pFndBox
->GetLines().back()->GetLine();
1851 SwTableBox
* pDelBox
= pDelLine
->GetTabBoxes().back();
1852 while( !pDelBox
->GetSttNd() )
1854 SwTableLine
* pLn
= pDelBox
->GetTabLines()[
1855 pDelBox
->GetTabLines().size()-1 ];
1856 pDelBox
= pLn
->GetTabBoxes().back();
1858 SwTableBox
* pNextBox
= pDelLine
->FindNextBox( pTableNd
->GetTable(),
1861 pNextBox
->GetFrameFormat()->GetProtect().IsContentProtected() )
1862 pNextBox
= pNextBox
->FindNextBox( pTableNd
->GetTable(), pNextBox
);
1864 if( !pNextBox
) // No succeeding Boxes? Then take the preceding one
1866 pDelLine
= pFndBox
->GetLines().front()->GetLine();
1867 pDelBox
= pDelLine
->GetTabBoxes()[ 0 ];
1868 while( !pDelBox
->GetSttNd() )
1869 pDelBox
= pDelBox
->GetTabLines()[0]->GetTabBoxes()[0];
1870 pNextBox
= pDelLine
->FindPreviousBox( pTableNd
->GetTable(),
1873 pNextBox
->GetFrameFormat()->GetProtect().IsContentProtected() )
1874 pNextBox
= pNextBox
->FindPreviousBox( pTableNd
->GetTable(), pNextBox
);
1878 if( pNextBox
) // Place the Cursor here
1879 nIdx
= pNextBox
->GetSttIdx() + 1;
1880 else // Else after the Table
1881 nIdx
= pTableNd
->EndOfSectionIndex() + 1;
1883 SwNodeIndex
aIdx( GetNodes(), nIdx
);
1884 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
1886 pCNd
= GetNodes().GoNext( &aIdx
);
1890 // Change the Shell's Cursor or the one passed?
1891 SwPaM
* pPam
= const_cast<SwPaM
*>(static_cast<SwPaM
const *>(&rCursor
));
1892 pPam
->GetPoint()->Assign(aIdx
);
1893 pPam
->SetMark(); // Both want a part of it
1898 // Thus delete the Rows
1899 GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE
, nullptr);
1900 DeleteRowCol( aBoxes
);
1901 GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE
, nullptr);
1904 void SwDoc::DeleteCol( const SwCursor
& rCursor
)
1906 // Find the Boxes via the Layout
1908 GetTableSel( rCursor
, aBoxes
, SwTableSearchType::Col
);
1909 if( ::HasProtectedCells( aBoxes
))
1912 // The Cursors need to be removed from the to-be-deleted range.
1913 // Always place them after/on top of the Table; they are always set
1914 // to the old position via the document position.
1915 if (SwEditShell
* pESh
= GetEditShell())
1917 const SwNode
* pNd
= rCursor
.GetPointNode().FindTableBoxStartNode();
1918 pESh
->ParkCursor( *pNd
);
1921 // Thus delete the Columns
1922 GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE
, nullptr);
1923 DeleteRowCol(aBoxes
, SwDoc::RowColMode::DeleteColumn
);
1924 GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE
, nullptr);
1927 void SwDoc::DelTable(SwTableNode
*const pTableNd
)
1930 // tdf#156267 remove DdeBookmarks before deleting nodes
1931 SwPaM
aTmpPaM(*pTableNd
, *pTableNd
->EndOfSectionNode());
1932 SwDataChanged
aTmp(aTmpPaM
);
1935 bool bNewTextNd
= false;
1936 // Is it alone in a FlyFrame?
1937 SwNodeIndex
aIdx( *pTableNd
, -1 );
1938 const SwStartNode
* pSttNd
= aIdx
.GetNode().GetStartNode();
1941 const SwNodeOffset nTableEnd
= pTableNd
->EndOfSectionIndex() + 1;
1942 const SwNodeOffset nSectEnd
= pSttNd
->EndOfSectionIndex();
1943 if (nTableEnd
== nSectEnd
)
1945 if (SwFlyStartNode
== pSttNd
->GetStartNodeType())
1947 SwFrameFormat
* pFormat
= pSttNd
->GetFlyFormat();
1950 // That's the FlyFormat we're looking for
1951 getIDocumentLayoutAccess().DelLayoutFormat( pFormat
);
1955 // No Fly? Thus Header or Footer: always leave a TextNode
1956 // We can forget about Undo then!
1961 // No Fly? Then it is a Header or Footer, so keep always a TextNode
1963 if (GetIDocumentUndoRedo().DoesUndo())
1965 GetIDocumentUndoRedo().ClearRedo();
1966 SwPaM
aPaM( *pTableNd
->EndOfSectionNode(), aIdx
.GetNode() );
1970 const SwNodeIndex
aTmpIdx( *pTableNd
->EndOfSectionNode(), 1 );
1971 GetNodes().MakeTextNode( aTmpIdx
.GetNode(),
1972 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
) );
1975 // Save the cursors (UNO and otherwise)
1976 SwPaM
const* pSavePaM(nullptr);
1977 SwPaM
forwardPaM(*pTableNd
->EndOfSectionNode());
1978 if (forwardPaM
.Move(fnMoveForward
, GoInNode
))
1980 pSavePaM
= &forwardPaM
;
1982 SwPaM
backwardPaM(*pTableNd
);
1983 if (backwardPaM
.Move(fnMoveBackward
, GoInNode
))
1985 if (pSavePaM
== nullptr
1986 // try to stay in the same outer table cell
1987 || (forwardPaM
.GetPoint()->GetNode().FindTableNode() != pTableNd
->StartOfSectionNode()->FindTableNode()
1988 && forwardPaM
.GetPoint()->GetNode().StartOfSectionIndex()
1989 < backwardPaM
.GetPoint()->GetNode().StartOfSectionIndex()))
1991 pSavePaM
= &backwardPaM
;
1994 assert(pSavePaM
); // due to bNewTextNd this must succeed
1996 SwPaM
const tmpPaM(*pTableNd
, *pTableNd
->EndOfSectionNode());
1997 ::PaMCorrAbs(tmpPaM
, *pSavePaM
->GetPoint());
2000 // Move hard PageBreaks to the succeeding Node
2001 bool bSavePageBreak
= false, bSavePageDesc
= false;
2002 SwNodeOffset nNextNd
= pTableNd
->EndOfSectionIndex()+1;
2003 SwContentNode
* pNextNd
= GetNodes()[ nNextNd
]->GetContentNode();
2006 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
2007 const SfxPoolItem
*pItem
;
2008 if (SfxItemState::SET
== pTableFormat
->GetItemState(RES_PAGEDESC
,
2011 pNextNd
->SetAttr( *pItem
);
2012 bSavePageDesc
= true;
2015 if (SfxItemState::SET
== pTableFormat
->GetItemState(RES_BREAK
,
2018 pNextNd
->SetAttr( *pItem
);
2019 bSavePageBreak
= true;
2022 std::unique_ptr
<SwUndoDelete
> pUndo(new SwUndoDelete(aPaM
, SwDeleteFlags::Default
));
2024 pUndo
->SetTableDelLastNd();
2025 pUndo
->SetPgBrkFlags( bSavePageBreak
, bSavePageDesc
);
2026 pUndo
->SetTableName(pTableNd
->GetTable().GetFrameFormat()->GetName());
2027 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
2033 const SwNodeIndex
aTmpIdx( *pTableNd
->EndOfSectionNode(), 1 );
2034 GetNodes().MakeTextNode( aTmpIdx
.GetNode(),
2035 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
) );
2038 // Save the cursors (UNO and otherwise)
2039 SwPaM
const* pSavePaM(nullptr);
2040 SwPaM
forwardPaM(*pTableNd
->EndOfSectionNode());
2041 if (forwardPaM
.Move(fnMoveForward
, GoInNode
))
2043 pSavePaM
= &forwardPaM
;
2045 SwPaM
backwardPaM(*pTableNd
);
2046 if (backwardPaM
.Move(fnMoveBackward
, GoInNode
))
2048 if (pSavePaM
== nullptr
2049 // try to stay in the same outer table cell
2050 || (forwardPaM
.GetPoint()->GetNode().FindTableNode() != pTableNd
->StartOfSectionNode()->FindTableNode()
2051 && forwardPaM
.GetPoint()->GetNode().StartOfSectionIndex()
2052 < backwardPaM
.GetPoint()->GetNode().StartOfSectionIndex()))
2054 pSavePaM
= &backwardPaM
;
2057 assert(pSavePaM
); // due to bNewTextNd this must succeed
2059 SwPaM
const tmpPaM(*pTableNd
, *pTableNd
->EndOfSectionNode());
2060 ::PaMCorrAbs(tmpPaM
, *pSavePaM
->GetPoint());
2063 // Move hard PageBreaks to the succeeding Node
2064 SwContentNode
* pNextNd
= GetNodes()[ pTableNd
->EndOfSectionIndex()+1 ]->GetContentNode();
2067 SwFrameFormat
* pTableFormat
= pTableNd
->GetTable().GetFrameFormat();
2068 const SfxPoolItem
*pItem
;
2069 if (SfxItemState::SET
== pTableFormat
->GetItemState(RES_PAGEDESC
,
2072 pNextNd
->SetAttr( *pItem
);
2075 if (SfxItemState::SET
== pTableFormat
->GetItemState(RES_BREAK
,
2078 pNextNd
->SetAttr( *pItem
);
2082 pTableNd
->DelFrames();
2083 getIDocumentContentOperations().DeleteSection( pTableNd
);
2086 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
2087 pFEShell
->UpdateTableStyleFormatting();
2089 getIDocumentState().SetModified();
2090 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2093 bool SwDoc::DeleteRowCol(const SwSelBoxes
& rBoxes
, RowColMode
const eMode
)
2095 if (!(eMode
& SwDoc::RowColMode::DeleteProtected
)
2096 && ::HasProtectedCells(rBoxes
))
2101 OSL_ENSURE( !rBoxes
.empty(), "No valid Box list" );
2102 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
2106 if (!(eMode
& SwDoc::RowColMode::DeleteProtected
)
2107 && dynamic_cast<const SwDDETable
*>(&pTableNd
->GetTable()) != nullptr)
2112 ::ClearFEShellTabCols(*this, nullptr);
2113 SwSelBoxes
aSelBoxes( rBoxes
);
2114 SwTable
&rTable
= pTableNd
->GetTable();
2115 tools::Long nMin
= 0;
2116 tools::Long nMax
= 0;
2117 if( rTable
.IsNewModel() )
2119 if (eMode
& SwDoc::RowColMode::DeleteColumn
)
2120 rTable
.ExpandColumnSelection( aSelBoxes
, nMin
, nMax
);
2122 rTable
.FindSuperfluousRows( aSelBoxes
);
2125 // Are we deleting the whole Table?
2126 const SwNodeOffset nTmpIdx1
= pTableNd
->GetIndex();
2127 const SwNodeOffset nTmpIdx2
= aSelBoxes
.back()->GetSttNd()->EndOfSectionIndex() + 1;
2128 if( pTableNd
->GetTable().GetTabSortBoxes().size() == aSelBoxes
.size() &&
2129 aSelBoxes
[0]->GetSttIdx()-1 == nTmpIdx1
&&
2130 nTmpIdx2
== pTableNd
->EndOfSectionIndex() )
2136 std::unique_ptr
<SwUndoTableNdsChg
> pUndo
;
2137 if (GetIDocumentUndoRedo().DoesUndo())
2139 pUndo
.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX
, aSelBoxes
, *pTableNd
,
2140 nMin
, nMax
, 0, false, false ));
2145 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
2146 rTable
.SwitchFormulasToInternalRepresentation();
2148 if (rTable
.IsNewModel())
2150 if (eMode
& SwDoc::RowColMode::DeleteColumn
)
2151 rTable
.PrepareDeleteCol( nMin
, nMax
);
2152 rTable
.FindSuperfluousRows( aSelBoxes
);
2154 pUndo
->ReNewBoxes( aSelBoxes
);
2156 bRet
= rTable
.DeleteSel( this, aSelBoxes
, nullptr, pUndo
.get(), true, true );
2159 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
2160 pFEShell
->UpdateTableStyleFormatting();
2162 getIDocumentState().SetModified();
2163 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2169 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
2176 * Split up/merge Boxes in the Table
2178 bool SwDoc::SplitTable( const SwSelBoxes
& rBoxes
, bool bVert
, sal_uInt16 nCnt
,
2181 OSL_ENSURE( !rBoxes
.empty() && nCnt
, "No valid Box list" );
2182 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
2186 SwTable
& rTable
= pTableNd
->GetTable();
2187 if( dynamic_cast<const SwDDETable
*>( &rTable
) != nullptr)
2190 std::vector
<SwNodeOffset
> aNdsCnts
;
2191 SwTableSortBoxes aTmpLst
;
2192 std::unique_ptr
<SwUndoTableNdsChg
> pUndo
;
2193 if (GetIDocumentUndoRedo().DoesUndo())
2195 pUndo
.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT
, rBoxes
, *pTableNd
, 0, 0,
2196 nCnt
, bVert
, bSameHeight
));
2198 aTmpLst
.insert( rTable
.GetTabSortBoxes() );
2201 for (size_t n
= 0; n
< rBoxes
.size(); ++n
)
2203 const SwStartNode
* pSttNd
= rBoxes
[ n
]->GetSttNd();
2204 aNdsCnts
.push_back( pSttNd
->EndOfSectionIndex() -
2205 pSttNd
->GetIndex() );
2212 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
2213 rTable
.SwitchFormulasToInternalRepresentation();
2216 bRet
= rTable
.SplitCol(*this, rBoxes
, nCnt
);
2218 bRet
= rTable
.SplitRow(*this, rBoxes
, nCnt
, bSameHeight
);
2222 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
2223 pFEShell
->UpdateTableStyleFormatting();
2225 getIDocumentState().SetModified();
2226 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2233 pUndo
->SaveNewBoxes( *pTableNd
, aTmpLst
);
2235 pUndo
->SaveNewBoxes( *pTableNd
, aTmpLst
, rBoxes
, aNdsCnts
);
2236 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
2242 TableMergeErr
SwDoc::MergeTable( SwPaM
& rPam
)
2244 // Check if the current cursor's Point/Mark are inside a Table
2245 SwTableNode
* pTableNd
= rPam
.GetPointNode().FindTableNode();
2247 return TableMergeErr::NoSelection
;
2248 SwTable
& rTable
= pTableNd
->GetTable();
2249 if( dynamic_cast<const SwDDETable
*>( &rTable
) != nullptr )
2250 return TableMergeErr::NoSelection
;
2251 TableMergeErr nRet
= TableMergeErr::NoSelection
;
2252 if( !rTable
.IsNewModel() )
2254 nRet
=::CheckMergeSel( rPam
);
2255 if( TableMergeErr::Ok
!= nRet
)
2257 nRet
= TableMergeErr::NoSelection
;
2261 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE
, nullptr );
2263 RedlineFlags eOld
= getIDocumentRedlineAccess().GetRedlineFlags();
2264 getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld
| RedlineFlags::Ignore
);
2266 std::unique_ptr
<SwUndoTableMerge
> pUndo
;
2267 if (GetIDocumentUndoRedo().DoesUndo())
2268 pUndo
.reset(new SwUndoTableMerge( rPam
));
2270 // Find the Boxes via the Layout
2273 SwTableBox
* pMergeBox
;
2275 if( !rTable
.PrepareMerge( rPam
, aBoxes
, aMerged
, &pMergeBox
, pUndo
.get() ) )
2276 { // No cells found to merge
2277 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
2281 SwUndoId
nLastUndoId(SwUndoId::EMPTY
);
2282 if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId
)
2283 && (SwUndoId::REDLINE
== nLastUndoId
))
2285 // FIXME: why is this horrible cleanup necessary?
2286 SwUndoRedline
*const pU
= dynamic_cast<SwUndoRedline
*>(
2287 GetUndoManager().RemoveLastUndo());
2288 if (pU
&& pU
->GetRedlSaveCount())
2290 SwEditShell
*const pEditShell(GetEditShell());
2292 ::sw::UndoRedoContext
context(*this, *pEditShell
);
2293 static_cast<SfxUndoAction
*>(pU
)->UndoWithContext(context
);
2301 // The PaMs need to be removed from the to-be-deleted range. Thus always place
2302 // them at the end of/on top of the Table; it's always set to the old position via
2303 // the Document Position.
2304 // For a start remember an index for the temporary position, because we cannot
2305 // access it after GetMergeSel
2308 rPam
.GetPoint()->Assign(*pMergeBox
->GetSttNd());
2312 SwPaM
* pTmp
= &rPam
;
2313 while( &rPam
!= ( pTmp
= pTmp
->GetNext() ))
2314 for( int i
= 0; i
< 2; ++i
)
2315 pTmp
->GetBound( static_cast<bool>(i
) ) = *rPam
.GetPoint();
2317 if (SwTableCursor
* pTableCursor
= dynamic_cast<SwTableCursor
*>(&rPam
))
2319 // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
2320 // to not contain the soon to-be-deleted SwTableBox so if the rPam
2321 // is queried via a11y it doesn't claim the deleted cell still
2323 pTableCursor
->NewTableSelection();
2328 pTableNd
->GetTable().SwitchFormulasToInternalRepresentation();
2330 if( pTableNd
->GetTable().Merge( this, aBoxes
, aMerged
, pMergeBox
, pUndo
.get() ))
2332 nRet
= TableMergeErr::Ok
;
2334 getIDocumentState().SetModified();
2335 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
2338 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
2342 rPam
.GetPoint()->Assign( *pMergeBox
->GetSttNd() );
2345 ::ClearFEShellTabCols(*this, nullptr);
2346 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
2348 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE
, nullptr );
2352 SwTableNode::SwTableNode( const SwNode
& rWhere
)
2353 : SwStartNode( rWhere
, SwNodeType::Table
)
2355 m_pTable
.reset(new SwTable
);
2358 SwTableNode::~SwTableNode()
2360 // Notify UNO wrappers
2361 GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying
));
2363 m_pTable
->SetTableNode(this); // set this so that ~SwDDETable can read it!
2367 SwTabFrame
*SwTableNode::MakeFrame( SwFrame
* pSib
)
2369 return new SwTabFrame( *m_pTable
, pSib
);
2373 * Creates all Views from the Document for the preceding Node. The resulting ContentFrames
2374 * are added to the corresponding Layout.
2376 void SwTableNode::MakeFramesForAdjacentContentNode(const SwNodeIndex
& rIdx
)
2378 if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
2382 SwContentNode
* pNode
= rIdx
.GetNode().GetContentNode();
2384 OSL_ENSURE( pNode
, "No ContentNode or CopyNode and new Node is identical");
2386 bool bBefore
= rIdx
< GetIndex();
2388 SwNode2Layout
aNode2Layout( *this, rIdx
.GetIndex() );
2390 while( nullptr != (pFrame
= aNode2Layout
.NextFrame()) )
2392 if ( ( pFrame
->getRootFrame()->HasMergedParas() &&
2393 !pNode
->IsCreateFrameWhenHidingRedlines() ) ||
2394 // tdf#153819 table deletion with change tracking:
2395 // table node without frames in Hide Changes mode
2396 !pFrame
->GetUpper() )
2400 SwFrame
*pNew
= pNode
->MakeFrame( pFrame
);
2401 // Will the Node receive Frames before or after?
2403 // The new one precedes me
2404 pNew
->Paste( pFrame
->GetUpper(), pFrame
);
2406 // The new one succeeds me
2407 pNew
->Paste( pFrame
->GetUpper(), pFrame
->GetNext() );
2412 * Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
2414 void SwTableNode::MakeOwnFrames(SwPosition
* pIdxBehind
)
2416 SwNode
*pNd
= GetNodes().FindPrvNxtFrameNode( *this, EndOfSectionNode() );
2420 pIdxBehind
->Assign(*this);
2424 pIdxBehind
->Assign(*pNd
);
2426 SwFrame
*pFrame( nullptr );
2427 SwLayoutFrame
*pUpper( nullptr );
2428 SwNode2Layout
aNode2Layout( *pNd
, GetIndex() );
2429 while( nullptr != (pUpper
= aNode2Layout
.UpperFrame( pFrame
, *this )) )
2431 if (pUpper
->getRootFrame()->HasMergedParas()
2432 && !IsCreateFrameWhenHidingRedlines())
2436 SwTabFrame
* pNew
= MakeFrame( pUpper
);
2437 pNew
->Paste( pUpper
, pFrame
);
2439 // notify accessibility paragraphs objects about changed
2440 // CONTENT_FLOWS_FROM/_TO relation.
2441 // Relation CONTENT_FLOWS_FROM for next paragraph will change
2442 // and relation CONTENT_FLOWS_TO for previous paragraph will change.
2443 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2445 SwViewShell
* pViewShell( pNew
->getRootFrame()->GetCurrShell() );
2446 if ( pViewShell
&& pViewShell
->GetLayout() &&
2447 pViewShell
->GetLayout()->IsAnyShellAccessible() )
2449 auto pNext
= pNew
->FindNextCnt( true );
2450 auto pPrev
= pNew
->FindPrevCnt();
2451 pViewShell
->InvalidateAccessibleParaFlowRelation(
2452 pNext
? pNext
->DynCastTextFrame() : nullptr,
2453 pPrev
? pPrev
->DynCastTextFrame() : nullptr );
2461 void SwTableNode::DelFrames(SwRootFrame
const*const pLayout
)
2463 /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
2464 The TabFrames are attached to the FrameFormat of the SwTable.
2465 We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
2467 SwIterator
<SwTabFrame
,SwFormat
> aIter( *(m_pTable
->GetFrameFormat()) );
2468 SwTabFrame
*pFrame
= aIter
.First();
2471 bool bAgain
= false;
2473 if (!pFrame
->IsFollow() && (!pLayout
|| pLayout
== pFrame
->getRootFrame()))
2475 while ( pFrame
->HasFollow() )
2476 pFrame
->JoinAndDelFollows();
2478 // notify accessibility paragraphs objects about changed
2479 // CONTENT_FLOWS_FROM/_TO relation.
2480 // Relation CONTENT_FLOWS_FROM for current next paragraph will change
2481 // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
2482 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2483 if (!GetDoc().IsInDtor())
2485 SwViewShell
* pViewShell( pFrame
->getRootFrame()->GetCurrShell() );
2486 if ( pViewShell
&& pViewShell
->GetLayout() &&
2487 pViewShell
->GetLayout()->IsAnyShellAccessible() )
2489 auto pNext
= pFrame
->FindNextCnt( true );
2490 auto pPrev
= pFrame
->FindPrevCnt();
2491 pViewShell
->InvalidateAccessibleParaFlowRelation(
2492 pNext
? pNext
->DynCastTextFrame() : nullptr,
2493 pPrev
? pPrev
->DynCastTextFrame() : nullptr );
2497 if (pFrame
->GetUpper())
2499 SwFrame::DestroyFrame(pFrame
);
2503 pFrame
= bAgain
? aIter
.First() : aIter
.Next();
2507 void SwTableNode::SetNewTable( std::unique_ptr
<SwTable
> pNewTable
, bool bNewFrames
)
2510 m_pTable
->SetTableNode(this);
2511 m_pTable
= std::move(pNewTable
);
2518 void SwTableNode::RemoveRedlines()
2520 SwDoc
& rDoc
= GetDoc();
2521 SwTable
& rTable
= GetTable();
2522 rDoc
.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc
, rTable
, true, RedlineType::Any
);
2525 void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter
) const
2527 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwTableNode"));
2528 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
2529 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
2533 m_pTable
->dumpAsXml(pWriter
);
2536 // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
2539 void SwDoc::GetTabCols( SwTabCols
&rFill
, const SwCellFrame
* pBoxFrame
)
2541 OSL_ENSURE( pBoxFrame
, "pBoxFrame needs to be specified!" );
2545 SwTabFrame
*pTab
= const_cast<SwFrame
*>(static_cast<SwFrame
const *>(pBoxFrame
))->ImplFindTabFrame();
2546 const SwTableBox
* pBox
= pBoxFrame
->GetTabBox();
2548 // Set fixed points, LeftMin in Document coordinates, all others relative
2549 SwRectFnSet
aRectFnSet(pTab
);
2550 const SwPageFrame
* pPage
= pTab
->FindPageFrame();
2551 const sal_uLong nLeftMin
= aRectFnSet
.GetLeft(pTab
->getFrameArea()) -
2552 aRectFnSet
.GetLeft(pPage
->getFrameArea());
2553 const sal_uLong nRightMax
= aRectFnSet
.GetRight(pTab
->getFrameArea()) -
2554 aRectFnSet
.GetLeft(pPage
->getFrameArea());
2556 rFill
.SetLeftMin ( nLeftMin
);
2557 rFill
.SetLeft ( aRectFnSet
.GetLeft(pTab
->getFramePrintArea()) );
2558 rFill
.SetRight ( aRectFnSet
.GetRight(pTab
->getFramePrintArea()));
2559 rFill
.SetRightMax( nRightMax
- nLeftMin
);
2561 pTab
->GetTable()->GetTabCols( rFill
, pBox
);
2564 // Here are some little helpers used in SwDoc::GetTabRows
2572 bool operator() ( tools::Long s1
, tools::Long s2
) const;
2577 bool FuzzyCompare::operator() ( tools::Long s1
, tools::Long s2
) const
2579 return ( s1
< s2
&& std::abs( s1
- s2
) > ROWFUZZY
);
2582 static bool lcl_IsFrameInColumn( const SwCellFrame
& rFrame
, SwSelBoxes
const & rBoxes
)
2584 for (size_t i
= 0; i
< rBoxes
.size(); ++i
)
2586 if ( rFrame
.GetTabBox() == rBoxes
[ i
] )
2593 void SwDoc::GetTabRows( SwTabCols
&rFill
, const SwCellFrame
* pBoxFrame
)
2595 OSL_ENSURE( pBoxFrame
, "GetTabRows called without pBoxFrame" );
2597 // Make code robust:
2601 // #i39552# Collection of the boxes of the current
2602 // column has to be done at the beginning of this function, because
2603 // the table may be formatted in ::GetTableSel.
2604 SwDeletionChecker
aDelCheck( pBoxFrame
);
2607 const SwContentFrame
* pContent
= ::GetCellContent( *pBoxFrame
);
2608 if ( pContent
&& pContent
->IsTextFrame() )
2610 const SwPosition
aPos(*static_cast<const SwTextFrame
*>(pContent
)->GetTextNodeFirst());
2611 const SwCursor
aTmpCursor( aPos
, nullptr );
2612 ::GetTableSel( aTmpCursor
, aBoxes
, SwTableSearchType::Col
);
2615 // Make code robust:
2616 if ( aDelCheck
.HasBeenDeleted() )
2618 OSL_FAIL( "Current box has been deleted during GetTabRows()" );
2622 // Make code robust:
2623 const SwTabFrame
* pTab
= pBoxFrame
->FindTabFrame();
2624 OSL_ENSURE( pTab
, "GetTabRows called without a table" );
2628 const SwFrame
* pFrame
= pTab
->GetNextLayoutLeaf();
2630 // Set fixed points, LeftMin in Document coordinates, all others relative
2631 SwRectFnSet
aRectFnSet(pTab
);
2632 const SwPageFrame
* pPage
= pTab
->FindPageFrame();
2633 const tools::Long nLeftMin
= ( aRectFnSet
.IsVert() ?
2634 pTab
->GetPrtLeft() - pPage
->getFrameArea().Left() :
2635 pTab
->GetPrtTop() - pPage
->getFrameArea().Top() );
2636 const tools::Long nLeft
= aRectFnSet
.IsVert() ? LONG_MAX
: 0;
2637 const tools::Long nRight
= aRectFnSet
.GetHeight(pTab
->getFramePrintArea());
2638 const tools::Long nRightMax
= aRectFnSet
.IsVert() ? nRight
: LONG_MAX
;
2640 rFill
.SetLeftMin( nLeftMin
);
2641 rFill
.SetLeft( nLeft
);
2642 rFill
.SetRight( nRight
);
2643 rFill
.SetRightMax( nRightMax
);
2645 typedef std::map
< tools::Long
, std::pair
< tools::Long
, long >, FuzzyCompare
> BoundaryMap
;
2646 BoundaryMap aBoundaries
;
2647 BoundaryMap::iterator aIter
;
2648 std::pair
< tools::Long
, long > aPair
;
2650 typedef std::map
< tools::Long
, bool > HiddenMap
;
2652 HiddenMap::iterator aHiddenIter
;
2654 while ( pFrame
&& pTab
->IsAnLower( pFrame
) )
2656 if ( pFrame
->IsCellFrame() && pFrame
->FindTabFrame() == pTab
)
2658 // upper and lower borders of current cell frame:
2659 tools::Long nUpperBorder
= aRectFnSet
.GetTop(pFrame
->getFrameArea());
2660 tools::Long nLowerBorder
= aRectFnSet
.GetBottom(pFrame
->getFrameArea());
2662 // get boundaries for nUpperBorder:
2663 aIter
= aBoundaries
.find( nUpperBorder
);
2664 if ( aIter
== aBoundaries
.end() )
2666 aPair
.first
= nUpperBorder
; aPair
.second
= LONG_MAX
;
2667 aBoundaries
[ nUpperBorder
] = aPair
;
2670 // get boundaries for nLowerBorder:
2671 aIter
= aBoundaries
.find( nLowerBorder
);
2672 if ( aIter
== aBoundaries
.end() )
2674 aPair
.first
= nUpperBorder
; aPair
.second
= LONG_MAX
;
2678 nLowerBorder
= (*aIter
).first
;
2679 tools::Long nNewLowerBorderUpperBoundary
= std::max( (*aIter
).second
.first
, nUpperBorder
);
2680 aPair
.first
= nNewLowerBorderUpperBoundary
; aPair
.second
= LONG_MAX
;
2682 aBoundaries
[ nLowerBorder
] = aPair
;
2684 // calculate hidden flags for entry nUpperBorder/nLowerBorder:
2685 tools::Long nTmpVal
= nUpperBorder
;
2686 for ( sal_uInt8 i
= 0; i
< 2; ++i
)
2688 aHiddenIter
= aHidden
.find( nTmpVal
);
2689 if ( aHiddenIter
== aHidden
.end() )
2690 aHidden
[ nTmpVal
] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame
*>(pFrame
), aBoxes
);
2693 if ( aHidden
[ nTmpVal
] &&
2694 lcl_IsFrameInColumn( *static_cast<const SwCellFrame
*>(pFrame
), aBoxes
) )
2695 aHidden
[ nTmpVal
] = false;
2697 nTmpVal
= nLowerBorder
;
2701 pFrame
= pFrame
->GetNextLayoutLeaf();
2704 // transfer calculated values from BoundaryMap and HiddenMap into rFill:
2706 for ( const auto& rEntry
: aBoundaries
)
2708 const tools::Long nTabTop
= aRectFnSet
.GetPrtTop(*pTab
);
2709 const tools::Long nKey
= aRectFnSet
.YDiff( rEntry
.first
, nTabTop
);
2710 const std::pair
< tools::Long
, long > aTmpPair
= rEntry
.second
;
2711 const tools::Long nFirst
= aRectFnSet
.YDiff( aTmpPair
.first
, nTabTop
);
2712 const tools::Long nSecond
= aTmpPair
.second
;
2714 aHiddenIter
= aHidden
.find( rEntry
.first
);
2715 const bool bHidden
= aHiddenIter
!= aHidden
.end() && (*aHiddenIter
).second
;
2716 rFill
.Insert( nKey
, nFirst
, nSecond
, bHidden
, nIdx
++ );
2719 // delete first and last entry
2720 OSL_ENSURE( rFill
.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
2721 // #i60818# There may be only one entry in rFill. Make
2722 // code robust by checking count of rFill.
2723 if ( rFill
.Count() ) rFill
.Remove( 0 );
2724 if ( rFill
.Count() ) rFill
.Remove( rFill
.Count() - 1 );
2725 rFill
.SetLastRowAllowedToChange( !pTab
->HasFollowFlowLine() );
2728 void SwDoc::SetTabCols( const SwTabCols
&rNew
, bool bCurRowOnly
,
2729 const SwCellFrame
* pBoxFrame
)
2731 const SwTableBox
* pBox
= nullptr;
2732 SwTabFrame
*pTab
= nullptr;
2736 pTab
= const_cast<SwFrame
*>(static_cast<SwFrame
const *>(pBoxFrame
))->ImplFindTabFrame();
2737 pBox
= pBoxFrame
->GetTabBox();
2741 OSL_ENSURE( false, "must specify pBoxFrame" );
2745 // If the Table is still using relative values (USHRT_MAX)
2746 // we need to switch to absolute ones.
2747 SwTable
& rTab
= *pTab
->GetTable();
2748 const SwFormatFrameSize
& rTableFrameSz
= rTab
.GetFrameFormat()->GetFrameSize();
2749 SwRectFnSet
aRectFnSet(pTab
);
2750 // #i17174# - With fix for #i9040# the shadow size is taken
2751 // from the table width. Thus, add its left and right size to current table
2752 // printing area width in order to get the correct table size attribute.
2753 SwTwips nPrtWidth
= aRectFnSet
.GetWidth(pTab
->getFramePrintArea());
2755 SvxShadowItem
aShadow( rTab
.GetFrameFormat()->GetShadow() );
2756 nPrtWidth
+= aShadow
.CalcShadowSpace( SvxShadowItemSide::LEFT
) +
2757 aShadow
.CalcShadowSpace( SvxShadowItemSide::RIGHT
);
2759 if( nPrtWidth
!= rTableFrameSz
.GetWidth() )
2761 SwFormatFrameSize
aSz( rTableFrameSz
);
2762 aSz
.SetWidth( nPrtWidth
);
2763 rTab
.GetFrameFormat()->SetFormatAttr( aSz
);
2766 SwTabCols
aOld( rNew
.Count() );
2768 const SwPageFrame
* pPage
= pTab
->FindPageFrame();
2769 const sal_uLong nLeftMin
= aRectFnSet
.GetLeft(pTab
->getFrameArea()) -
2770 aRectFnSet
.GetLeft(pPage
->getFrameArea());
2771 const sal_uLong nRightMax
= aRectFnSet
.GetRight(pTab
->getFrameArea()) -
2772 aRectFnSet
.GetLeft(pPage
->getFrameArea());
2774 // Set fixed points, LeftMin in Document coordinates, all others relative
2775 aOld
.SetLeftMin ( nLeftMin
);
2776 aOld
.SetLeft ( aRectFnSet
.GetLeft(pTab
->getFramePrintArea()) );
2777 aOld
.SetRight ( aRectFnSet
.GetRight(pTab
->getFramePrintArea()));
2778 aOld
.SetRightMax( nRightMax
- nLeftMin
);
2780 rTab
.GetTabCols( aOld
, pBox
);
2781 SetTabCols(rTab
, rNew
, aOld
, pBox
, bCurRowOnly
);
2784 void SwDoc::SetTabRows( const SwTabCols
&rNew
, bool bCurColOnly
,
2785 const SwCellFrame
* pBoxFrame
)
2787 SwTabFrame
*pTab
= nullptr;
2791 pTab
= const_cast<SwFrame
*>(static_cast<SwFrame
const *>(pBoxFrame
))->ImplFindTabFrame();
2795 OSL_ENSURE( false, "must specify pBoxFrame" );
2799 // If the Table is still using relative values (USHRT_MAX)
2800 // we need to switch to absolute ones.
2801 SwRectFnSet
aRectFnSet(pTab
);
2802 SwTabCols
aOld( rNew
.Count() );
2804 // Set fixed points, LeftMin in Document coordinates, all others relative
2805 const SwPageFrame
* pPage
= pTab
->FindPageFrame();
2807 aOld
.SetRight( aRectFnSet
.GetHeight(pTab
->getFramePrintArea()) );
2808 tools::Long nLeftMin
;
2809 if ( aRectFnSet
.IsVert() )
2811 nLeftMin
= pTab
->GetPrtLeft() - pPage
->getFrameArea().Left();
2812 aOld
.SetLeft ( LONG_MAX
);
2813 aOld
.SetRightMax( aOld
.GetRight() );
2818 nLeftMin
= pTab
->GetPrtTop() - pPage
->getFrameArea().Top();
2820 aOld
.SetRightMax( LONG_MAX
);
2822 aOld
.SetLeftMin ( nLeftMin
);
2824 GetTabRows( aOld
, pBoxFrame
);
2826 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR
, nullptr );
2828 // check for differences between aOld and rNew:
2829 const size_t nCount
= rNew
.Count();
2830 const SwTable
* pTable
= pTab
->GetTable();
2831 OSL_ENSURE( pTable
, "My colleague told me, this couldn't happen" );
2833 for ( size_t i
= 0; i
<= nCount
; ++i
)
2835 const size_t nIdxStt
= aRectFnSet
.IsVert() ? nCount
- i
: i
- 1;
2836 const size_t nIdxEnd
= aRectFnSet
.IsVert() ? nCount
- i
- 1 : i
;
2838 const tools::Long nOldRowStart
= i
== 0 ? 0 : aOld
[ nIdxStt
];
2839 const tools::Long nOldRowEnd
= i
== nCount
? aOld
.GetRight() : aOld
[ nIdxEnd
];
2840 const tools::Long nOldRowHeight
= nOldRowEnd
- nOldRowStart
;
2842 const tools::Long nNewRowStart
= i
== 0 ? 0 : rNew
[ nIdxStt
];
2843 const tools::Long nNewRowEnd
= i
== nCount
? rNew
.GetRight() : rNew
[ nIdxEnd
];
2844 const tools::Long nNewRowHeight
= nNewRowEnd
- nNewRowStart
;
2846 const tools::Long nDiff
= nNewRowHeight
- nOldRowHeight
;
2847 if ( std::abs( nDiff
) >= ROWFUZZY
)
2849 // For the old table model pTextFrame and pLine will be set for every box.
2850 // For the new table model pTextFrame will be set if the box is not covered,
2851 // but the pLine will be set if the box is not an overlapping box
2852 // In the new table model the row height can be adjusted,
2853 // when both variables are set.
2854 const SwTextFrame
* pTextFrame
= nullptr;
2855 const SwTableLine
* pLine
= nullptr;
2857 // Iterate over all SwCellFrames with Bottom = nOldPos
2858 const SwFrame
* pFrame
= pTab
->GetNextLayoutLeaf();
2859 while ( pFrame
&& pTab
->IsAnLower( pFrame
) )
2861 if ( pFrame
->IsCellFrame() && pFrame
->FindTabFrame() == pTab
)
2863 const tools::Long nLowerBorder
= aRectFnSet
.GetBottom(pFrame
->getFrameArea());
2864 const sal_uLong nTabTop
= aRectFnSet
.GetPrtTop(*pTab
);
2865 if ( std::abs( aRectFnSet
.YInc( nTabTop
, nOldRowEnd
) - nLowerBorder
) <= ROWFUZZY
)
2867 if ( !bCurColOnly
|| pFrame
== pBoxFrame
)
2869 const SwFrame
* pContent
= ::GetCellContent( static_cast<const SwCellFrame
&>(*pFrame
) );
2871 if ( pContent
&& pContent
->IsTextFrame() )
2873 const SwTableBox
* pBox
= static_cast<const SwCellFrame
*>(pFrame
)->GetTabBox();
2874 const sal_Int32 nRowSpan
= pBox
->getRowSpan();
2875 if( nRowSpan
> 0 ) // Not overlapped
2876 pTextFrame
= static_cast<const SwTextFrame
*>(pContent
);
2877 if( nRowSpan
< 2 ) // Not overlapping for row height
2878 pLine
= pBox
->GetUpper();
2879 if( pLine
&& pTextFrame
) // always for old table model
2881 // The new row height must not to be calculated from an overlapping box
2882 SwFormatFrameSize
aNew( pLine
->GetFrameFormat()->GetFrameSize() );
2883 const tools::Long nNewSize
= aRectFnSet
.GetHeight(pFrame
->getFrameArea()) + nDiff
;
2884 if( nNewSize
!= aNew
.GetHeight() )
2886 aNew
.SetHeight( nNewSize
);
2887 if ( SwFrameSize::Variable
== aNew
.GetHeightSizeType() )
2888 aNew
.SetHeightSizeType( SwFrameSize::Minimum
);
2889 // This position must not be in an overlapped box
2890 const SwPosition
aPos(*static_cast<const SwTextFrame
*>(pContent
)->GetTextNodeFirst());
2891 const SwCursor
aTmpCursor( aPos
, nullptr );
2892 SetRowHeight( aTmpCursor
, aNew
);
2893 // For the new table model we're done, for the old one
2894 // there might be another (sub)row to adjust...
2895 if( pTable
->IsNewModel() )
2904 pFrame
= pFrame
->GetNextLayoutLeaf();
2909 GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR
, nullptr );
2911 ::ClearFEShellTabCols(*this, nullptr);
2915 * Direct access for UNO
2917 void SwDoc::SetTabCols(SwTable
& rTab
, const SwTabCols
&rNew
, const SwTabCols
&rOld
,
2918 const SwTableBox
*pStart
, bool bCurRowOnly
)
2920 if (GetIDocumentUndoRedo().DoesUndo())
2922 GetIDocumentUndoRedo().AppendUndo(
2923 std::make_unique
<SwUndoAttrTable
>( *rTab
.GetTableNode(), true ));
2925 rTab
.SetTabCols( rNew
, rOld
, pStart
, bCurRowOnly
);
2926 ::ClearFEShellTabCols(*this, nullptr);
2927 getIDocumentState().SetModified();
2930 void SwDoc::SetRowsToRepeat( SwTable
&rTable
, sal_uInt16 nSet
)
2932 if( nSet
== rTable
.GetRowsToRepeat() )
2935 if (GetIDocumentUndoRedo().DoesUndo())
2937 GetIDocumentUndoRedo().AppendUndo(
2938 std::make_unique
<SwUndoTableHeadline
>(rTable
, rTable
.GetRowsToRepeat(), nSet
) );
2941 rTable
.SetRowsToRepeat(nSet
);
2942 rTable
.GetFrameFormat()->CallSwClientNotify(sw::TableHeadingChange());
2943 getIDocumentState().SetModified();
2946 void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode
& rNd
)
2949 m_pHistory
->Add( rNd
.GetFormatColl(), rNd
.GetIndex(), SwNodeType::Text
);
2952 void SwCollectTableLineBoxes::AddBox( const SwTableBox
& rBox
)
2954 m_aPositionArr
.push_back(m_nWidth
);
2955 SwTableBox
* p
= const_cast<SwTableBox
*>(&rBox
);
2956 m_Boxes
.push_back(p
);
2957 m_nWidth
= m_nWidth
+ o3tl::narrowing
<sal_uInt16
>(rBox
.GetFrameFormat()->GetFrameSize().GetWidth());
2960 const SwTableBox
* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox
& rBox
)
2962 const SwTableBox
* pRet
= nullptr;
2964 if( !m_aPositionArr
.empty() )
2966 std::vector
<sal_uInt16
>::size_type n
;
2967 for( n
= 0; n
< m_aPositionArr
.size(); ++n
)
2968 if( m_aPositionArr
[ n
] == m_nWidth
)
2970 else if( m_aPositionArr
[ n
] > m_nWidth
)
2977 if( n
>= m_aPositionArr
.size() )
2980 m_nWidth
= m_nWidth
+ o3tl::narrowing
<sal_uInt16
>(rBox
.GetFrameFormat()->GetFrameSize().GetWidth());
2981 pRet
= m_Boxes
[ n
];
2986 bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset
, sal_uInt16 nOldWidth
)
2988 if( !m_aPositionArr
.empty() )
2990 std::vector
<sal_uInt16
>::size_type n
;
2991 for( n
= 0; n
< m_aPositionArr
.size(); ++n
)
2993 if( m_aPositionArr
[ n
] == nOffset
)
2995 else if( m_aPositionArr
[ n
] > nOffset
)
3003 m_aPositionArr
.erase( m_aPositionArr
.begin(), m_aPositionArr
.begin() + n
);
3004 m_Boxes
.erase(m_Boxes
.begin(), m_Boxes
.begin() + n
);
3006 size_t nArrSize
= m_aPositionArr
.size();
3010 throw o3tl::divide_by_zero();
3012 // Adapt the positions to the new Size
3013 for( n
= 0; n
< nArrSize
; ++n
)
3015 sal_uLong nSize
= m_nWidth
;
3016 nSize
*= ( m_aPositionArr
[ n
] - nOffset
);
3018 m_aPositionArr
[ n
] = sal_uInt16( nSize
);
3022 return !m_aPositionArr
.empty();
3025 bool sw_Line_CollectBox( const SwTableLine
*& rpLine
, void* pPara
)
3027 SwCollectTableLineBoxes
* pSplPara
= static_cast<SwCollectTableLineBoxes
*>(pPara
);
3028 if( pSplPara
->IsGetValues() )
3029 for( const auto& rpBox
: const_cast<SwTableLine
*>(rpLine
)->GetTabBoxes() )
3030 sw_Box_CollectBox(rpBox
, pSplPara
);
3032 for( auto& rpBox
: const_cast<SwTableLine
*>(rpLine
)->GetTabBoxes() )
3033 sw_BoxSetSplitBoxFormats(rpBox
, pSplPara
);
3037 void sw_Box_CollectBox( const SwTableBox
* pBox
, SwCollectTableLineBoxes
* pSplPara
)
3039 auto nLen
= pBox
->GetTabLines().size();
3042 // Continue with the actual Line
3043 if( pSplPara
->IsGetFromTop() )
3048 const SwTableLine
* pLn
= pBox
->GetTabLines()[ nLen
];
3049 sw_Line_CollectBox( pLn
, pSplPara
);
3052 pSplPara
->AddBox( *pBox
);
3055 void sw_BoxSetSplitBoxFormats( SwTableBox
* pBox
, SwCollectTableLineBoxes
* pSplPara
)
3057 auto nLen
= pBox
->GetTabLines().size();
3060 // Continue with the actual Line
3061 if( pSplPara
->IsGetFromTop() )
3066 const SwTableLine
* pLn
= pBox
->GetTabLines()[ nLen
];
3067 sw_Line_CollectBox( pLn
, pSplPara
);
3071 const SwTableBox
* pSrcBox
= pSplPara
->GetBoxOfPos( *pBox
);
3072 SwFrameFormat
* pFormat
= pSrcBox
->GetFrameFormat();
3074 if( SplitTable_HeadlineOption::BorderCopy
== pSplPara
->GetMode() )
3076 const SvxBoxItem
& rBoxItem
= pBox
->GetFrameFormat()->GetBox();
3077 if( !rBoxItem
.GetTop() )
3079 SvxBoxItem
aNew( rBoxItem
);
3080 aNew
.SetLine( pFormat
->GetBox().GetBottom(), SvxBoxItemLine::TOP
);
3081 if( aNew
!= rBoxItem
)
3082 pBox
->ClaimFrameFormat()->SetFormatAttr( aNew
);
3087 SfxItemSetFixed
<RES_LR_SPACE
, RES_UL_SPACE
,
3088 RES_PROTECT
, RES_PROTECT
,
3089 RES_VERT_ORIENT
, RES_VERT_ORIENT
,
3090 RES_BACKGROUND
, RES_SHADOW
>
3091 aTmpSet( pFormat
->GetDoc()->GetAttrPool() );
3092 aTmpSet
.Put( pFormat
->GetAttrSet() );
3093 if( aTmpSet
.Count() )
3094 pBox
->ClaimFrameFormat()->SetFormatAttr( aTmpSet
);
3096 if( SplitTable_HeadlineOption::BoxAttrAllCopy
== pSplPara
->GetMode() )
3098 SwNodeIndex
aIdx( *pSrcBox
->GetSttNd(), 1 );
3099 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
3101 pCNd
= aIdx
.GetNodes().GoNext( &aIdx
);
3102 aIdx
= *pBox
->GetSttNd();
3103 SwContentNode
* pDNd
= aIdx
.GetNodes().GoNext( &aIdx
);
3105 // If the Node is alone in the Section
3106 if( SwNodeOffset(2) == pDNd
->EndOfSectionIndex() -
3107 pDNd
->StartOfSectionIndex() )
3109 pSplPara
->AddToUndoHistory( *pDNd
);
3110 pDNd
->ChgFormatColl( pCNd
->GetFormatColl() );
3114 // note conditional template
3115 pBox
->GetSttNd()->CheckSectionCondColl();
3121 * Splits a Table in the top-level Line which contains the Index.
3122 * All succeeding top-level Lines go into a new Table/Node.
3124 * @param bCalcNewSize true
3125 * Calculate the new Size for both from the
3126 * Boxes' Max; but only if Size is using absolute
3127 * values (USHRT_MAX)
3129 void SwDoc::SplitTable( const SwPosition
& rPos
, SplitTable_HeadlineOption eHdlnMode
,
3132 SwNode
* pNd
= &rPos
.GetNode();
3133 SwTableNode
* pTNd
= pNd
->FindTableNode();
3134 if( !pTNd
|| pNd
->IsTableNode() )
3137 if( dynamic_cast<const SwDDETable
*>( &pTNd
->GetTable() ) != nullptr)
3140 SwTable
& rTable
= pTNd
->GetTable();
3141 rTable
.SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
>()); // Delete HTML Layout
3145 SwNodeOffset nSttIdx
= pNd
->FindTableBoxStartNode()->GetIndex();
3146 // Find top-level Line
3147 SwTableBox
* pBox
= rTable
.GetTableBox(nSttIdx
);
3148 sal_uInt16 nSplitLine
= 0;
3151 SwTableLine
* pLine
= pBox
->GetUpper();
3152 while(pLine
->GetUpper())
3153 pLine
= pLine
->GetUpper()->GetUpper();
3155 // pLine contains the top-level Line now
3156 nSplitLine
= rTable
.GetTabLines().GetPos(pLine
);
3158 rTable
.Split(GetUniqueTableName(), nSplitLine
, GetIDocumentUndoRedo().DoesUndo() ? &aHistory
: nullptr);
3161 // Find Lines for the Layout update
3162 FndBox_
aFndBox( nullptr, nullptr );
3163 aFndBox
.SetTableLines( rTable
);
3164 aFndBox
.DelFrames( rTable
);
3166 SwTableNode
* pNew
= GetNodes().SplitTable( rPos
.GetNode(), false, bCalcNewSize
);
3170 std::unique_ptr
<SwSaveRowSpan
> pSaveRowSp
= pNew
->GetTable().CleanUpTopRowSpan( rTable
.GetTabLines().size() );
3171 SwUndoSplitTable
* pUndo
= nullptr;
3172 if (GetIDocumentUndoRedo().DoesUndo())
3174 pUndo
= new SwUndoSplitTable(
3175 *pNew
, std::move(pSaveRowSp
), eHdlnMode
, bCalcNewSize
);
3176 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3177 if( aHistory
.Count() )
3178 pUndo
->SaveFormula( aHistory
);
3183 // Set the lower Border of the preceding Line to
3184 // the upper Border of the current one
3185 case SplitTable_HeadlineOption::BorderCopy
:
3187 SwCollectTableLineBoxes
aPara( false, eHdlnMode
);
3188 SwTableLine
* pLn
= rTable
.GetTabLines()[
3189 rTable
.GetTabLines().size() - 1 ];
3190 for( const auto& rpBox
: pLn
->GetTabBoxes() )
3191 sw_Box_CollectBox(rpBox
, &aPara
);
3193 aPara
.SetValues( true );
3194 pLn
= pNew
->GetTable().GetTabLines()[ 0 ];
3195 for( auto& rpBox
: pLn
->GetTabBoxes() )
3196 sw_BoxSetSplitBoxFormats(rpBox
, &aPara
);
3198 // Switch off repeating Header
3199 pNew
->GetTable().SetRowsToRepeat( 0 );
3203 // Take over the Attributes of the first Line to the new one
3204 case SplitTable_HeadlineOption::BoxAttrCopy
:
3205 case SplitTable_HeadlineOption::BoxAttrAllCopy
:
3207 SwHistory
* pHst
= nullptr;
3208 if( SplitTable_HeadlineOption::BoxAttrAllCopy
== eHdlnMode
&& pUndo
)
3209 pHst
= pUndo
->GetHistory();
3211 SwCollectTableLineBoxes
aPara( true, eHdlnMode
, pHst
);
3212 SwTableLine
* pLn
= rTable
.GetTabLines()[ 0 ];
3213 for( const auto& rpBox
: pLn
->GetTabBoxes() )
3214 sw_Box_CollectBox(rpBox
, &aPara
);
3216 aPara
.SetValues( true );
3217 pLn
= pNew
->GetTable().GetTabLines()[ 0 ];
3218 for( auto& rpBox
: pLn
->GetTabBoxes() )
3219 sw_BoxSetSplitBoxFormats(rpBox
, &aPara
);
3223 case SplitTable_HeadlineOption::ContentCopy
:
3224 rTable
.CopyHeadlineIntoTable( *pNew
);
3226 pUndo
->SetTableNodeOffset( pNew
->GetIndex() );
3229 case SplitTable_HeadlineOption::NONE
:
3230 // Switch off repeating the Header
3231 pNew
->GetTable().SetRowsToRepeat( 0 );
3235 // And insert Frames
3236 pNew
->MakeOwnFrames();
3238 // Insert a paragraph between the Table
3239 GetNodes().MakeTextNode( *pNew
,
3240 getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT
) );
3244 aFndBox
.MakeFrames( rTable
);
3246 // TL_CHART2: need to inform chart of probably changed cell names
3247 UpdateCharts( rTable
.GetFrameFormat()->GetName() );
3249 // update table style formatting of both the tables
3250 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
3252 pFEShell
->UpdateTableStyleFormatting(pTNd
);
3253 pFEShell
->UpdateTableStyleFormatting(pNew
);
3256 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3259 static bool lcl_ChgTableSize( SwTable
& rTable
)
3261 // The Attribute must not be set via the Modify or else all Boxes are
3263 // So lock the Format.
3264 SwFrameFormat
* pFormat
= rTable
.GetFrameFormat();
3265 SwFormatFrameSize
aTableMaxSz( pFormat
->GetFrameSize() );
3267 if( USHRT_MAX
== aTableMaxSz
.GetWidth() )
3270 bool bLocked
= pFormat
->IsModifyLocked();
3271 pFormat
->LockModify();
3273 aTableMaxSz
.SetWidth( 0 );
3275 SwTableLines
& rLns
= rTable
.GetTabLines();
3276 for( auto pLn
: rLns
)
3278 SwTwips nMaxLnWidth
= 0;
3279 SwTableBoxes
& rBoxes
= pLn
->GetTabBoxes();
3280 for( auto pBox
: rBoxes
)
3281 nMaxLnWidth
+= pBox
->GetFrameFormat()->GetFrameSize().GetWidth();
3283 if( nMaxLnWidth
> aTableMaxSz
.GetWidth() )
3284 aTableMaxSz
.SetWidth( nMaxLnWidth
);
3286 pFormat
->SetFormatAttr( aTableMaxSz
);
3287 if( !bLocked
) // Release the Lock if appropriate
3288 pFormat
->UnlockModify();
3295 class SplitTable_Para
3297 std::map
<SwFrameFormat
const*, SwFrameFormat
*> m_aSrcDestMap
;
3298 SwTableNode
* m_pNewTableNode
;
3299 SwTable
& m_rOldTable
;
3302 SplitTable_Para(SwTableNode
* pNew
, SwTable
& rOld
)
3303 : m_pNewTableNode(pNew
)
3306 SwFrameFormat
* GetDestFormat( SwFrameFormat
* pSrcFormat
) const
3308 auto it
= m_aSrcDestMap
.find(pSrcFormat
);
3309 return it
== m_aSrcDestMap
.end() ? nullptr : it
->second
;
3312 void InsertSrcDest( SwFrameFormat
const * pSrcFormat
, SwFrameFormat
* pDestFormat
)
3314 m_aSrcDestMap
[pSrcFormat
] = pDestFormat
;
3317 void ChgBox( SwTableBox
* pBox
)
3319 m_rOldTable
.GetTabSortBoxes().erase(pBox
);
3320 m_pNewTableNode
->GetTable().GetTabSortBoxes().insert(pBox
);
3326 static void lcl_SplitTable_CpyBox( SwTableBox
* pBox
, SplitTable_Para
* pPara
);
3328 static void lcl_SplitTable_CpyLine( SwTableLine
* pLn
, SplitTable_Para
* pPara
)
3330 SwFrameFormat
*pSrcFormat
= pLn
->GetFrameFormat();
3331 SwTableLineFormat
* pDestFormat
= static_cast<SwTableLineFormat
*>( pPara
->GetDestFormat( pSrcFormat
) );
3332 if( pDestFormat
== nullptr )
3334 pPara
->InsertSrcDest( pSrcFormat
, pLn
->ClaimFrameFormat() );
3337 pLn
->ChgFrameFormat( pDestFormat
);
3339 for( auto& rpBox
: pLn
->GetTabBoxes() )
3340 lcl_SplitTable_CpyBox(rpBox
, pPara
);
3343 static void lcl_SplitTable_CpyBox( SwTableBox
* pBox
, SplitTable_Para
* pPara
)
3345 SwFrameFormat
*pSrcFormat
= pBox
->GetFrameFormat();
3346 SwTableBoxFormat
* pDestFormat
= static_cast<SwTableBoxFormat
*>(pPara
->GetDestFormat( pSrcFormat
));
3347 if( pDestFormat
== nullptr )
3349 pPara
->InsertSrcDest( pSrcFormat
, pBox
->ClaimFrameFormat() );
3352 pBox
->ChgFrameFormat( pDestFormat
);
3354 if( pBox
->GetSttNd() )
3355 pPara
->ChgBox( pBox
);
3357 for( SwTableLine
* pLine
: pBox
->GetTabLines() )
3358 lcl_SplitTable_CpyLine( pLine
, pPara
);
3361 SwTableNode
* SwNodes::SplitTable( SwNode
& rPos
, bool bAfter
,
3364 SwNode
* pNd
= &rPos
;
3365 SwTableNode
* pTNd
= pNd
->FindTableNode();
3366 if( !pTNd
|| pNd
->IsTableNode() )
3369 SwNodeOffset nSttIdx
= pNd
->FindTableBoxStartNode()->GetIndex();
3371 // Find this Box/top-level line
3372 SwTable
& rTable
= pTNd
->GetTable();
3373 SwTableBox
* pBox
= rTable
.GetTableBox( nSttIdx
);
3377 SwTableLine
* pLine
= pBox
->GetUpper();
3378 while( pLine
->GetUpper() )
3379 pLine
= pLine
->GetUpper()->GetUpper();
3381 // pLine now contains the top-level line
3382 sal_uInt16 nLinePos
= rTable
.GetTabLines().GetPos( pLine
);
3383 if( USHRT_MAX
== nLinePos
||
3384 ( bAfter
? ++nLinePos
>= rTable
.GetTabLines().size() : !nLinePos
))
3385 return nullptr; // Not found or last Line!
3387 // Find the first Box of the succeeding Line
3388 SwTableLine
* pNextLine
= rTable
.GetTabLines()[ nLinePos
];
3389 pBox
= pNextLine
->GetTabBoxes()[0];
3390 while( !pBox
->GetSttNd() )
3391 pBox
= pBox
->GetTabLines()[0]->GetTabBoxes()[0];
3393 // Insert an EndNode and TableNode into the Nodes Array
3394 SwTableNode
* pNewTableNd
;
3396 SwEndNode
* pOldTableEndNd
= pTNd
->EndOfSectionNode()->GetEndNode();
3397 assert(pOldTableEndNd
&& "Where is the EndNode?");
3399 new SwEndNode( *pBox
->GetSttNd(), *pTNd
);
3400 pNewTableNd
= new SwTableNode( *pBox
->GetSttNd() );
3401 pNewTableNd
->GetTable().SetTableModel( rTable
.IsNewModel() );
3403 pOldTableEndNd
->m_pStartOfSection
= pNewTableNd
;
3404 pNewTableNd
->m_pEndOfSection
= pOldTableEndNd
;
3406 SwNode
* pBoxNd
= const_cast<SwStartNode
*>(pBox
->GetSttNd()->GetStartNode());
3408 OSL_ENSURE( pBoxNd
->IsStartNode(), "This needs to be a StartNode!" );
3409 pBoxNd
->m_pStartOfSection
= pNewTableNd
;
3410 pBoxNd
= (*this)[ pBoxNd
->EndOfSectionIndex() + 1 ];
3411 } while( pBoxNd
!= pOldTableEndNd
);
3416 SwTable
& rNewTable
= pNewTableNd
->GetTable();
3417 rNewTable
.GetTabLines().insert( rNewTable
.GetTabLines().begin(),
3418 rTable
.GetTabLines().begin() + nLinePos
, rTable
.GetTabLines().end() );
3420 /* From the back (bottom right) to the front (top left) deregister all Boxes from the
3421 Chart Data Provider. The Modify event is triggered in the calling function.
3423 SwChartDataProvider
*pPCD
= rTable
.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
3426 for (SwTableLines::size_type k
= nLinePos
; k
< rTable
.GetTabLines().size(); ++k
)
3428 const SwTableLines::size_type nLineIdx
= (rTable
.GetTabLines().size() - 1) - k
+ nLinePos
;
3429 const SwTableBoxes::size_type nBoxCnt
= rTable
.GetTabLines()[ nLineIdx
]->GetTabBoxes().size();
3430 for (SwTableBoxes::size_type j
= 0; j
< nBoxCnt
; ++j
)
3432 const SwTableBoxes::size_type nIdx
= nBoxCnt
- 1 - j
;
3433 pPCD
->DeleteBox( &rTable
, *rTable
.GetTabLines()[ nLineIdx
]->GetTabBoxes()[nIdx
] );
3439 sal_uInt16 nDeleted
= rTable
.GetTabLines().size() - nLinePos
;
3440 rTable
.GetTabLines().erase( rTable
.GetTabLines().begin() + nLinePos
, rTable
.GetTabLines().end() );
3442 // Move the affected Boxes. Make the Formats unique and correct the StartNodes
3443 SplitTable_Para
aPara( pNewTableNd
, rTable
);
3444 for( SwTableLine
* pNewLine
: rNewTable
.GetTabLines() )
3445 lcl_SplitTable_CpyLine( pNewLine
, &aPara
);
3446 rTable
.CleanUpBottomRowSpan( nDeleted
);
3450 // Copy the Table FrameFormat
3451 SwFrameFormat
* pOldTableFormat
= rTable
.GetFrameFormat();
3452 SwFrameFormat
* pNewTableFormat
= pOldTableFormat
->GetDoc()->MakeTableFrameFormat(
3453 pOldTableFormat
->GetDoc()->GetUniqueTableName(),
3454 pOldTableFormat
->GetDoc()->GetDfltFrameFormat() );
3456 *pNewTableFormat
= *pOldTableFormat
;
3457 pNewTableNd
->GetTable().RegisterToFormat( *pNewTableFormat
);
3459 pNewTableNd
->GetTable().SetTableStyleName(rTable
.GetTableStyleName());
3461 // Calculate a new Size?
3462 // lcl_ChgTableSize: Only execute the second call if the first call was
3463 // successful, thus has an absolute Size
3464 if( bCalcNewSize
&& lcl_ChgTableSize( rTable
) )
3465 lcl_ChgTableSize( pNewTableNd
->GetTable() );
3468 // TL_CHART2: need to inform chart of probably changed cell names
3469 rTable
.UpdateCharts();
3471 return pNewTableNd
; // That's it!
3475 * rPos needs to be in the Table that remains
3477 * @param bWithPrev merge the current Table with the preceding
3480 bool SwDoc::MergeTable( const SwPosition
& rPos
, bool bWithPrev
)
3482 SwTableNode
* pTableNd
= rPos
.GetNode().FindTableNode(), *pDelTableNd
;
3486 SwNodes
& rNds
= GetNodes();
3488 pDelTableNd
= rNds
[ pTableNd
->GetIndex() - 1 ]->FindTableNode();
3490 pDelTableNd
= rNds
[ pTableNd
->EndOfSectionIndex() + 1 ]->GetTableNode();
3494 if( dynamic_cast<const SwDDETable
*>( &pTableNd
->GetTable() ) != nullptr ||
3495 dynamic_cast<const SwDDETable
*>( &pDelTableNd
->GetTable() ) != nullptr)
3498 // Delete HTML Layout
3499 pTableNd
->GetTable().SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
>());
3500 pDelTableNd
->GetTable().SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
>());
3502 // Both Tables are present; we can start
3503 SwUndoMergeTable
* pUndo
= nullptr;
3504 std::unique_ptr
<SwHistory
> pHistory
;
3505 if (GetIDocumentUndoRedo().DoesUndo())
3507 pUndo
= new SwUndoMergeTable( *pTableNd
, *pDelTableNd
, bWithPrev
);
3508 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3509 pHistory
.reset(new SwHistory
);
3512 // Adapt all "TableFormulas"
3513 pTableNd
->GetTable().Merge(pDelTableNd
->GetTable(), pHistory
.get());
3516 bool bRet
= rNds
.MergeTable( bWithPrev
? *pTableNd
: *pDelTableNd
, !bWithPrev
);
3520 if( pHistory
->Count() )
3521 pUndo
->SaveFormula( *pHistory
);
3526 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
3527 pFEShell
->UpdateTableStyleFormatting();
3529 getIDocumentState().SetModified();
3530 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3535 bool SwNodes::MergeTable( SwNode
& rPos
, bool bWithPrev
)
3537 SwTableNode
* pDelTableNd
= rPos
.GetTableNode();
3538 OSL_ENSURE( pDelTableNd
, "Where did the TableNode go?" );
3540 SwTableNode
* pTableNd
= (*this)[ rPos
.GetIndex() - 1]->FindTableNode();
3541 OSL_ENSURE( pTableNd
, "Where did the TableNode go?" );
3543 if( !pDelTableNd
|| !pTableNd
)
3546 pDelTableNd
->DelFrames();
3548 SwTable
& rDelTable
= pDelTableNd
->GetTable();
3549 SwTable
& rTable
= pTableNd
->GetTable();
3551 // Find Lines for the Layout update
3552 FndBox_
aFndBox( nullptr, nullptr );
3553 aFndBox
.SetTableLines( rTable
);
3554 aFndBox
.DelFrames( rTable
);
3557 // tell the charts about the table to be deleted and have them use their own data
3558 GetDoc().getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable
);
3560 // Sync the TableFormat's Width
3562 const SwFormatFrameSize
& rTableSz
= rTable
.GetFrameFormat()->GetFrameSize();
3563 const SwFormatFrameSize
& rDelTableSz
= rDelTable
.GetFrameFormat()->GetFrameSize();
3564 if( rTableSz
!= rDelTableSz
)
3566 // The needs correction
3568 rDelTable
.GetFrameFormat()->SetFormatAttr( rTableSz
);
3570 rTable
.GetFrameFormat()->SetFormatAttr( rDelTableSz
);
3576 // Transfer all Attributes of the succeeding Table to the preceding one
3577 // We do this, because the succeeding one is deleted when deleting the Node
3578 rTable
.SetRowsToRepeat( rDelTable
.GetRowsToRepeat() );
3579 rTable
.SetTableChgMode( rDelTable
.GetTableChgMode() );
3581 rTable
.GetFrameFormat()->LockModify();
3582 *rTable
.GetFrameFormat() = *rDelTable
.GetFrameFormat();
3583 // Also switch the Name
3584 rTable
.GetFrameFormat()->SetFormatName( rDelTable
.GetFrameFormat()->GetName() );
3585 rTable
.GetFrameFormat()->UnlockModify();
3588 // Move the Lines and Boxes
3589 SwTableLines::size_type nOldSize
= rTable
.GetTabLines().size();
3590 rTable
.GetTabLines().insert( rTable
.GetTabLines().begin() + nOldSize
,
3591 rDelTable
.GetTabLines().begin(), rDelTable
.GetTabLines().end() );
3592 rDelTable
.GetTabLines().clear();
3594 rTable
.GetTabSortBoxes().insert( rDelTable
.GetTabSortBoxes() );
3595 rDelTable
.GetTabSortBoxes().clear();
3597 // The preceding Table always remains, while the succeeding one is deleted
3598 SwEndNode
* pTableEndNd
= pDelTableNd
->EndOfSectionNode();
3599 pTableNd
->m_pEndOfSection
= pTableEndNd
;
3601 SwNodeIndex
aIdx( *pDelTableNd
, 1 );
3603 SwNode
* pBoxNd
= aIdx
.GetNode().GetStartNode();
3605 OSL_ENSURE( pBoxNd
->IsStartNode(), "This needs to be a StartNode!" );
3606 pBoxNd
->m_pStartOfSection
= pTableNd
;
3607 pBoxNd
= (*this)[ pBoxNd
->EndOfSectionIndex() + 1 ];
3608 } while( pBoxNd
!= pTableEndNd
);
3609 pBoxNd
->m_pStartOfSection
= pTableNd
;
3611 aIdx
-= SwNodeOffset(2);
3612 DelNodes( aIdx
, SwNodeOffset(2) );
3614 // tweak the conditional styles at the first inserted Line
3615 const SwTableLine
* pFirstLn
= rTable
.GetTabLines()[ nOldSize
];
3616 sw_LineSetHeadCondColl( pFirstLn
);
3618 // Clean up the Borders
3621 SwGCLineBorder
aPara( rTable
);
3622 aPara
.nLinePos
= --nOldSize
;
3623 pFirstLn
= rTable
.GetTabLines()[ nOldSize
];
3624 sw_GC_Line_Border( pFirstLn
, &aPara
);
3628 aFndBox
.MakeFrames( rTable
);
3635 // Use the PtrArray's ForEach method
3636 struct SetAFormatTabPara
3638 SwTableAutoFormat
& rTableFormat
;
3639 SwUndoTableAutoFormat
* pUndo
;
3640 sal_uInt16 nEndBox
, nCurBox
;
3641 sal_uInt8 nAFormatLine
, nAFormatBox
;
3642 bool bSingleRowTable
;
3644 explicit SetAFormatTabPara( const SwTableAutoFormat
& rNew
)
3645 : rTableFormat( const_cast<SwTableAutoFormat
&>(rNew
) ), pUndo( nullptr ),
3646 nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
3652 // Forward declare so that the Lines and Boxes can use recursion
3653 static bool lcl_SetAFormatBox(FndBox_
&, SetAFormatTabPara
*pSetPara
, bool bResetDirect
);
3654 static bool lcl_SetAFormatLine(FndLine_
&, SetAFormatTabPara
*pPara
, bool bResetDirect
);
3656 static bool lcl_SetAFormatLine(FndLine_
& rLine
, SetAFormatTabPara
*pPara
, bool bResetDirect
)
3658 for (auto const& it
: rLine
.GetBoxes())
3660 lcl_SetAFormatBox(*it
, pPara
, bResetDirect
);
3665 static bool lcl_SetAFormatBox(FndBox_
& rBox
, SetAFormatTabPara
*pSetPara
, bool bResetDirect
)
3667 if (!rBox
.GetUpper()->GetUpper()) // Box on first level?
3669 if( !pSetPara
->nCurBox
)
3670 pSetPara
->nAFormatBox
= 0;
3671 else if( pSetPara
->nCurBox
== pSetPara
->nEndBox
)
3672 pSetPara
->nAFormatBox
= 3;
3673 else //Even column(1) or Odd column(2)
3674 pSetPara
->nAFormatBox
= static_cast<sal_uInt8
>(1 + ((pSetPara
->nCurBox
-1) & 1));
3677 if (rBox
.GetBox()->GetSttNd())
3679 SwTableBox
* pSetBox
= rBox
.GetBox();
3680 if (!pSetBox
->HasDirectFormatting() || bResetDirect
)
3683 pSetBox
->SetDirectFormatting(false);
3685 SwDoc
* pDoc
= pSetBox
->GetFrameFormat()->GetDoc();
3686 SfxItemSetFixed
<RES_CHRATR_BEGIN
, RES_PARATR_LIST_END
-1> aCharSet(pDoc
->GetAttrPool());
3687 SfxItemSet
aBoxSet(pDoc
->GetAttrPool(), aTableBoxSetRange
);
3688 sal_uInt8 nPos
= pSetPara
->nAFormatLine
* 4 + pSetPara
->nAFormatBox
;
3689 const bool bSingleRowTable
= pSetPara
->bSingleRowTable
;
3690 const bool bSingleColTable
= pSetPara
->nEndBox
== 0;
3691 pSetPara
->rTableFormat
.UpdateToSet(nPos
, bSingleRowTable
, bSingleColTable
, aCharSet
, SwTableAutoFormatUpdateFlags::Char
, nullptr);
3692 pSetPara
->rTableFormat
.UpdateToSet(nPos
, bSingleRowTable
, bSingleColTable
, aBoxSet
, SwTableAutoFormatUpdateFlags::Box
, pDoc
->GetNumberFormatter());
3694 if (aCharSet
.Count())
3696 SwNodeOffset nSttNd
= pSetBox
->GetSttIdx()+1;
3697 SwNodeOffset nEndNd
= pSetBox
->GetSttNd()->EndOfSectionIndex();
3698 for (; nSttNd
< nEndNd
; ++nSttNd
)
3700 SwContentNode
* pNd
= pDoc
->GetNodes()[ nSttNd
]->GetContentNode();
3702 pNd
->SetAttr(aCharSet
);
3706 if (aBoxSet
.Count())
3708 if (pSetPara
->pUndo
&& SfxItemState::SET
== aBoxSet
.GetItemState(RES_BOXATR_FORMAT
))
3709 pSetPara
->pUndo
->SaveBoxContent( *pSetBox
);
3711 pSetBox
->ClaimFrameFormat()->SetFormatAttr(aBoxSet
);
3717 // Not sure how this situation can occur, but apparently we have some kind of table in table.
3718 // I am guessing at how to best handle singlerow in this situation.
3719 const bool bOrigSingleRowTable
= pSetPara
->bSingleRowTable
;
3720 pSetPara
->bSingleRowTable
= rBox
.GetLines().size() == 1;
3721 for (auto const& rpFndLine
: rBox
.GetLines())
3723 lcl_SetAFormatLine(*rpFndLine
, pSetPara
, bResetDirect
);
3725 pSetPara
->bSingleRowTable
= bOrigSingleRowTable
;
3728 if (!rBox
.GetUpper()->GetUpper()) // a BaseLine
3729 ++pSetPara
->nCurBox
;
3733 bool SwDoc::SetTableAutoFormat(const SwSelBoxes
& rBoxes
, const SwTableAutoFormat
& rNew
, bool bResetDirect
, bool const isSetStyleName
)
3735 OSL_ENSURE( !rBoxes
.empty(), "No valid Box list" );
3736 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
3740 // Find all Boxes/Lines
3741 FndBox_
aFndBox( nullptr, nullptr );
3743 FndPara
aPara( rBoxes
, &aFndBox
);
3744 ForEach_FndLineCopyCol( pTableNd
->GetTable().GetTabLines(), &aPara
);
3746 if( aFndBox
.GetLines().empty() )
3749 SwTable
&table
= pTableNd
->GetTable();
3750 table
.SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
>());
3752 FndBox_
* pFndBox
= &aFndBox
;
3753 while( 1 == pFndBox
->GetLines().size() &&
3754 1 == pFndBox
->GetLines().front()->GetBoxes().size())
3756 pFndBox
= pFndBox
->GetLines().front()->GetBoxes()[0].get();
3759 if( pFndBox
->GetLines().empty() ) // One too far? (only one sel. Box)
3760 pFndBox
= pFndBox
->GetUpper()->GetUpper();
3762 // Disable Undo, but first store parameters
3763 SwUndoTableAutoFormat
* pUndo
= nullptr;
3764 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
3767 pUndo
= new SwUndoTableAutoFormat( *pTableNd
, rNew
);
3768 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndo
));
3769 GetIDocumentUndoRedo().DoUndo(false);
3773 { // tdf#98226 do this here where undo can record it
3774 pTableNd
->GetTable().SetTableStyleName(rNew
.GetName());
3777 rNew
.RestoreTableProperties(table
);
3779 SetAFormatTabPara
aPara( rNew
);
3780 FndLines_t
& rFLns
= pFndBox
->GetLines();
3781 aPara
.bSingleRowTable
= rFLns
.size() == 1;
3783 for (FndLines_t::size_type n
= 0; n
< rFLns
.size(); ++n
)
3785 FndLine_
* pLine
= rFLns
[n
].get();
3787 // Set Upper to 0 (thus simulate BaseLine)
3788 FndBox_
* pSaveBox
= pLine
->GetUpper();
3789 pLine
->SetUpper( nullptr );
3792 aPara
.nAFormatLine
= 0;
3793 else if (static_cast<size_t>(n
+1) == rFLns
.size())
3794 aPara
.nAFormatLine
= 3;
3796 aPara
.nAFormatLine
= static_cast<sal_uInt8
>(1 + ((n
-1) & 1 ));
3798 aPara
.nAFormatBox
= 0;
3800 aPara
.nEndBox
= pLine
->GetBoxes().size()-1;
3801 aPara
.pUndo
= pUndo
;
3802 for (auto const& it
: pLine
->GetBoxes())
3804 lcl_SetAFormatBox(*it
, &aPara
, bResetDirect
);
3807 pLine
->SetUpper( pSaveBox
);
3812 GetIDocumentUndoRedo().DoUndo(bUndo
);
3815 getIDocumentState().SetModified();
3816 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
3822 * Find out who has the Attributes
3824 bool SwDoc::GetTableAutoFormat( const SwSelBoxes
& rBoxes
, SwTableAutoFormat
& rGet
)
3826 OSL_ENSURE( !rBoxes
.empty(), "No valid Box list" );
3827 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
3831 // Find all Boxes/Lines
3832 FndBox_
aFndBox( nullptr, nullptr );
3834 FndPara
aPara( rBoxes
, &aFndBox
);
3835 ForEach_FndLineCopyCol( pTableNd
->GetTable().GetTabLines(), &aPara
);
3837 if( aFndBox
.GetLines().empty() )
3840 // Store table properties
3841 SwTable
&table
= pTableNd
->GetTable();
3842 rGet
.StoreTableProperties(table
);
3844 FndBox_
* pFndBox
= &aFndBox
;
3845 while( 1 == pFndBox
->GetLines().size() &&
3846 1 == pFndBox
->GetLines().front()->GetBoxes().size())
3848 pFndBox
= pFndBox
->GetLines().front()->GetBoxes()[0].get();
3851 if( pFndBox
->GetLines().empty() ) // One too far? (only one sel. Box)
3852 pFndBox
= pFndBox
->GetUpper()->GetUpper();
3854 FndLines_t
& rFLns
= pFndBox
->GetLines();
3856 sal_uInt16 aLnArr
[4];
3858 aLnArr
[1] = 1 < rFLns
.size() ? 1 : 0;
3859 aLnArr
[2] = 2 < rFLns
.size() ? 2 : aLnArr
[1];
3860 aLnArr
[3] = rFLns
.size() - 1;
3862 for( sal_uInt8 nLine
= 0; nLine
< 4; ++nLine
)
3864 FndLine_
& rLine
= *rFLns
[ aLnArr
[ nLine
] ];
3866 sal_uInt16 aBoxArr
[4];
3868 aBoxArr
[1] = 1 < rLine
.GetBoxes().size() ? 1 : 0;
3869 aBoxArr
[2] = 2 < rLine
.GetBoxes().size() ? 2 : aBoxArr
[1];
3870 aBoxArr
[3] = rLine
.GetBoxes().size() - 1;
3872 for( sal_uInt8 nBox
= 0; nBox
< 4; ++nBox
)
3874 SwTableBox
* pFBox
= rLine
.GetBoxes()[ aBoxArr
[ nBox
] ]->GetBox();
3875 // Always apply to the first ones
3876 while( !pFBox
->GetSttNd() )
3877 pFBox
= pFBox
->GetTabLines()[0]->GetTabBoxes()[0];
3879 sal_uInt8 nPos
= nLine
* 4 + nBox
;
3880 SwNodeIndex
aIdx( *pFBox
->GetSttNd(), 1 );
3881 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
3883 pCNd
= GetNodes().GoNext( &aIdx
);
3886 rGet
.UpdateFromSet( nPos
, pCNd
->GetSwAttrSet(),
3887 SwTableAutoFormatUpdateFlags::Char
, nullptr );
3888 rGet
.UpdateFromSet( nPos
, pFBox
->GetFrameFormat()->GetAttrSet(),
3889 SwTableAutoFormatUpdateFlags::Box
,
3890 GetNumberFormatter() );
3897 SwTableAutoFormatTable
& SwDoc::GetTableStyles()
3899 if (!m_pTableStyles
)
3901 m_pTableStyles
.reset(new SwTableAutoFormatTable
);
3902 m_pTableStyles
->Load();
3904 return *m_pTableStyles
;
3907 OUString
SwDoc::GetUniqueTableName() const
3909 if( IsInMailMerge())
3911 OUString newName
= "MailMergeTable"
3912 + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM
)), RTL_TEXTENCODING_ASCII_US
)
3913 + OUString::number( mpTableFrameFormatTable
->size() + 1 );
3917 const OUString
aName(SwResId(STR_TABLE_DEFNAME
));
3919 const size_t nFlagSize
= ( mpTableFrameFormatTable
->size() / 8 ) + 2;
3921 std::unique_ptr
<sal_uInt8
[]> pSetFlags( new sal_uInt8
[ nFlagSize
] );
3922 memset( pSetFlags
.get(), 0, nFlagSize
);
3924 for( size_t n
= 0; n
< mpTableFrameFormatTable
->size(); ++n
)
3926 const SwTableFormat
* pFormat
= (*mpTableFrameFormatTable
)[ n
];
3927 if( !pFormat
->IsDefault() && IsUsed( *pFormat
) &&
3928 pFormat
->GetName().startsWith( aName
) )
3930 // Get number and set the Flag
3931 const sal_Int32 nNmLen
= aName
.getLength();
3932 size_t nNum
= o3tl::toInt32(pFormat
->GetName().subView( nNmLen
));
3933 if( nNum
-- && nNum
< mpTableFrameFormatTable
->size() )
3934 pSetFlags
[ nNum
/ 8 ] |= (0x01 << ( nNum
& 0x07 ));
3938 // All numbers are flagged properly, thus calculate the right number
3939 size_t nNum
= mpTableFrameFormatTable
->size();
3940 for( size_t n
= 0; n
< nFlagSize
; ++n
)
3942 auto nTmp
= pSetFlags
[ n
];
3945 // Calculate the number
3956 return aName
+ OUString::number( ++nNum
);
3959 SwTableFormat
* SwDoc::FindTableFormatByName( const OUString
& rName
, bool bAll
) const
3961 const SwFormat
* pRet
= nullptr;
3963 pRet
= mpTableFrameFormatTable
->FindFormatByName( rName
);
3966 auto [it
, itEnd
] = mpTableFrameFormatTable
->findRangeByName(rName
);
3967 // Only the ones set in the Doc
3968 for( ; it
!= itEnd
; ++it
)
3970 const SwFrameFormat
* pFormat
= *it
;
3971 if( !pFormat
->IsDefault() && IsUsed( *pFormat
) &&
3972 pFormat
->GetName() == rName
)
3979 return const_cast<SwTableFormat
*>(static_cast<const SwTableFormat
*>(pRet
));
3982 void SwDoc::SetColRowWidthHeight( SwTableBox
& rCurrentBox
, TableChgWidthHeightType eType
,
3983 SwTwips nAbsDiff
, SwTwips nRelDiff
)
3985 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rCurrentBox
.GetSttNd()->FindTableNode());
3986 std::unique_ptr
<SwUndo
> pUndo
;
3988 pTableNd
->GetTable().SwitchFormulasToInternalRepresentation();
3989 bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
3991 switch( extractPosition(eType
) )
3993 case TableChgWidthHeightType::ColLeft
:
3994 case TableChgWidthHeightType::ColRight
:
3995 case TableChgWidthHeightType::CellLeft
:
3996 case TableChgWidthHeightType::CellRight
:
3998 bRet
= pTableNd
->GetTable().SetColWidth( rCurrentBox
,
3999 eType
, nAbsDiff
, nRelDiff
,
4000 bUndo
? &pUndo
: nullptr );
4003 case TableChgWidthHeightType::RowBottom
:
4004 case TableChgWidthHeightType::CellTop
:
4005 case TableChgWidthHeightType::CellBottom
:
4006 bRet
= pTableNd
->GetTable().SetRowHeight( rCurrentBox
,
4007 eType
, nAbsDiff
, nRelDiff
,
4008 bUndo
? &pUndo
: nullptr );
4013 GetIDocumentUndoRedo().DoUndo(bUndo
); // SetColWidth can turn it off
4016 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
4021 getIDocumentState().SetModified();
4025 bool SwDoc::IsNumberFormat( std::u16string_view aString
, sal_uInt32
& F_Index
, double& fOutNumber
)
4027 if( aString
.size() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
4030 // remove any comment anchor marks
4031 OUStringBuffer
sStringBuffer(aString
);
4032 sal_Int32 nCommentPosition
= sStringBuffer
.indexOf( CH_TXTATR_INWORD
);
4033 while( nCommentPosition
!= -1 )
4035 sStringBuffer
.remove( nCommentPosition
, 1 );
4036 nCommentPosition
= sStringBuffer
.indexOf( CH_TXTATR_INWORD
, nCommentPosition
);
4039 return GetNumberFormatter()->IsNumberFormat( sStringBuffer
.makeStringAndClear(), F_Index
, fOutNumber
);
4042 void SwDoc::ChkBoxNumFormat( SwTableBox
& rBox
, bool bCallUpdate
)
4044 // Optimization: If the Box says it's Text, it remains Text
4045 const SwTableBoxNumFormat
* pNumFormatItem
= rBox
.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT
,
4047 if( pNumFormatItem
&& GetNumberFormatter()->IsTextFormat(pNumFormatItem
->GetValue()) )
4050 std::unique_ptr
<SwUndoTableNumFormat
> pUndo
;
4052 bool bIsEmptyTextNd
;
4054 sal_uInt32 nFormatIdx
;
4056 if( rBox
.HasNumContent( fNumber
, nFormatIdx
, bIsEmptyTextNd
) )
4058 if( !rBox
.IsNumberChanged() )
4062 if (GetIDocumentUndoRedo().DoesUndo())
4064 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT
, nullptr );
4065 pUndo
.reset(new SwUndoTableNumFormat( rBox
));
4066 pUndo
->SetNumFormat( nFormatIdx
, fNumber
);
4069 SwTableBoxFormat
* pBoxFormat
= static_cast<SwTableBoxFormat
*>(rBox
.GetFrameFormat());
4070 SfxItemSetFixed
<RES_BOXATR_FORMAT
, RES_BOXATR_VALUE
> aBoxSet( GetAttrPool() );
4072 bool bLockModify
= true;
4073 bool bSetNumberFormat
= IsInsTableFormatNum();
4074 const bool bForceNumberFormat
= IsInsTableFormatNum() && IsInsTableChangeNumFormat();
4076 // if the user forced a number format in this cell previously,
4077 // keep it, unless the user set that she wants the full number
4078 // format recognition
4079 if( pNumFormatItem
&& !bForceNumberFormat
)
4081 sal_uLong nOldNumFormat
= pNumFormatItem
->GetValue();
4082 SvNumberFormatter
* pNumFormatr
= GetNumberFormatter();
4084 SvNumFormatType nFormatType
= pNumFormatr
->GetType( nFormatIdx
);
4085 if( nFormatType
== pNumFormatr
->GetType( nOldNumFormat
) || SvNumFormatType::NUMBER
== nFormatType
)
4087 // Current and specified NumFormat match
4088 // -> keep old Format
4089 nFormatIdx
= nOldNumFormat
;
4090 bSetNumberFormat
= true;
4094 // Current and specified NumFormat do not match
4095 // -> insert as Text
4096 bLockModify
= bSetNumberFormat
= false;
4100 if( bSetNumberFormat
|| bForceNumberFormat
)
4102 pBoxFormat
= static_cast<SwTableBoxFormat
*>(rBox
.ClaimFrameFormat());
4104 aBoxSet
.Put( SwTableBoxValue( fNumber
));
4105 aBoxSet
.Put( SwTableBoxNumFormat( nFormatIdx
));
4108 // It's not enough to only reset the Formula.
4109 // Make sure that the Text is formatted accordingly
4110 if( !bSetNumberFormat
&& !bIsEmptyTextNd
&& pNumFormatItem
)
4112 // Just resetting Attributes is not enough
4113 // Make sure that the Text is formatted accordingly
4114 pBoxFormat
->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT
));
4117 if( bLockModify
) pBoxFormat
->LockModify();
4118 pBoxFormat
->ResetFormatAttr( RES_BOXATR_FORMAT
, RES_BOXATR_VALUE
);
4119 if( bLockModify
) pBoxFormat
->UnlockModify();
4121 if( bSetNumberFormat
)
4122 pBoxFormat
->SetFormatAttr( aBoxSet
);
4127 // It's not a number
4128 SwTableBoxFormat
* pBoxFormat
= static_cast<SwTableBoxFormat
*>(rBox
.GetFrameFormat());
4129 if( SfxItemState::SET
== pBoxFormat
->GetItemState( RES_BOXATR_FORMAT
, false ) ||
4130 SfxItemState::SET
== pBoxFormat
->GetItemState( RES_BOXATR_VALUE
, false ) )
4132 if (GetIDocumentUndoRedo().DoesUndo())
4134 GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT
, nullptr );
4135 pUndo
.reset(new SwUndoTableNumFormat( rBox
));
4138 pBoxFormat
= static_cast<SwTableBoxFormat
*>(rBox
.ClaimFrameFormat());
4140 // Remove all number formats
4141 sal_uInt16 nWhich1
= RES_BOXATR_FORMULA
;
4142 if( !bIsEmptyTextNd
)
4144 nWhich1
= RES_BOXATR_FORMAT
;
4146 // Just resetting Attributes is not enough
4147 // Make sure that the Text is formatted accordingly
4148 pBoxFormat
->SetFormatAttr( *GetDfltAttr( nWhich1
));
4150 pBoxFormat
->ResetFormatAttr( nWhich1
, RES_BOXATR_VALUE
);
4161 pUndo
->SetBox( rBox
);
4162 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
4163 GetIDocumentUndoRedo().EndUndo( SwUndoId::END
, nullptr );
4166 const SwTableNode
* pTableNd
= rBox
.GetSttNd()->FindTableNode();
4169 getIDocumentFieldsAccess().UpdateTableFields(&pTableNd
->GetTable());
4171 // TL_CHART2: update charts (when cursor leaves cell and
4172 // automatic update is enabled)
4173 if (AUTOUPD_FIELD_AND_CHARTS
== GetDocumentSettingManager().getFieldUpdateFlags(true))
4174 pTableNd
->GetTable().UpdateCharts();
4176 getIDocumentState().SetModified();
4179 void SwDoc::SetTableBoxFormulaAttrs( SwTableBox
& rBox
, const SfxItemSet
& rSet
)
4181 if (GetIDocumentUndoRedo().DoesUndo())
4183 GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoTableNumFormat
>(rBox
, &rSet
) );
4186 SwFrameFormat
* pBoxFormat
= rBox
.ClaimFrameFormat();
4187 if( SfxItemState::SET
== rSet
.GetItemState( RES_BOXATR_FORMULA
))
4189 pBoxFormat
->LockModify();
4190 pBoxFormat
->ResetFormatAttr( RES_BOXATR_VALUE
);
4191 pBoxFormat
->UnlockModify();
4193 else if( SfxItemState::SET
== rSet
.GetItemState( RES_BOXATR_VALUE
))
4195 pBoxFormat
->LockModify();
4196 pBoxFormat
->ResetFormatAttr( RES_BOXATR_FORMULA
);
4197 pBoxFormat
->UnlockModify();
4199 pBoxFormat
->SetFormatAttr( rSet
);
4200 getIDocumentState().SetModified();
4203 void SwDoc::ClearLineNumAttrs( SwPosition
const & rPos
)
4206 aPam
.Move(fnMoveBackward
);
4207 SwContentNode
*pNode
= aPam
.GetPointContentNode();
4208 if ( nullptr == pNode
)
4210 if( !pNode
->IsTextNode() )
4213 SwTextNode
* pTextNode
= pNode
->GetTextNode();
4214 if (!(pTextNode
&& pTextNode
->IsNumbered()
4215 && pTextNode
->GetText().isEmpty()))
4218 SfxItemSetFixed
<RES_PARATR_BEGIN
, RES_PARATR_END
- 1>
4219 rSet( pTextNode
->GetDoc().GetAttrPool() );
4220 pTextNode
->SwContentNode::GetAttr( rSet
);
4221 const SfxStringItem
* pFormatItem
= rSet
.GetItemIfSet( RES_PARATR_NUMRULE
, false );
4225 SwUndoDelNum
* pUndo
;
4226 if( GetIDocumentUndoRedo().DoesUndo() )
4228 GetIDocumentUndoRedo().ClearRedo();
4229 pUndo
= new SwUndoDelNum( aPam
);
4230 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pUndo
) );
4234 SwRegHistory
aRegH( pUndo
? pUndo
->GetHistory() : nullptr );
4235 aRegH
.RegisterInModify( pTextNode
, *pTextNode
);
4237 pUndo
->AddNode( *pTextNode
);
4238 std::unique_ptr
<SfxStringItem
> pNewItem(pFormatItem
->Clone());
4239 pNewItem
->SetValue(OUString());
4240 rSet
.Put( std::move(pNewItem
) );
4241 pTextNode
->SetAttr( rSet
);
4244 void SwDoc::ClearBoxNumAttrs( SwNode
& rNode
)
4246 SwStartNode
* pSttNd
= rNode
.FindSttNodeByType( SwTableBoxStartNode
);
4247 if( nullptr == pSttNd
||
4248 SwNodeOffset(2) != pSttNd
->EndOfSectionIndex() - pSttNd
->GetIndex())
4251 SwTableBox
* pBox
= pSttNd
->FindTableNode()->GetTable().
4252 GetTableBox( pSttNd
->GetIndex() );
4254 const SfxItemSet
& rSet
= pBox
->GetFrameFormat()->GetAttrSet();
4255 const SwTableBoxNumFormat
* pFormatItem
= rSet
.GetItemIfSet( RES_BOXATR_FORMAT
, false );
4257 SfxItemState::SET
== rSet
.GetItemState( RES_BOXATR_FORMULA
, false ) ||
4258 SfxItemState::SET
== rSet
.GetItemState( RES_BOXATR_VALUE
, false ))
4261 if (GetIDocumentUndoRedo().DoesUndo())
4263 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoTableNumFormat
>(*pBox
));
4266 SwFrameFormat
* pBoxFormat
= pBox
->ClaimFrameFormat();
4268 // Keep TextFormats!
4269 sal_uInt16 nWhich1
= RES_BOXATR_FORMAT
;
4270 if( pFormatItem
&& GetNumberFormatter()->IsTextFormat(
4271 pFormatItem
->GetValue() ))
4272 nWhich1
= RES_BOXATR_FORMULA
;
4274 // Just resetting Attributes is not enough
4275 // Make sure that the Text is formatted accordingly
4276 pBoxFormat
->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT
));
4278 pBoxFormat
->ResetFormatAttr( nWhich1
, RES_BOXATR_VALUE
);
4279 getIDocumentState().SetModified();
4283 * Copies a Table from the same or another Doc into itself
4284 * We create a new Table or an existing one is filled with the Content.
4285 * We either fill in the Content from a certain Box or a certain TableSelection
4287 * This method is called by edglss.cxx/fecopy.cxx
4289 bool SwDoc::InsCopyOfTable( SwPosition
& rInsPos
, const SwSelBoxes
& rBoxes
,
4290 const SwTable
* pCpyTable
, bool bCpyName
, bool bCorrPos
, const OUString
& rStyleName
)
4294 const SwTableNode
* pSrcTableNd
= pCpyTable
4295 ? pCpyTable
->GetTableNode()
4296 : rBoxes
[ 0 ]->GetSttNd()->FindTableNode();
4298 SwTableNode
* pInsTableNd
= rInsPos
.GetNode().FindTableNode();
4300 bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
4301 if( !pCpyTable
&& !pInsTableNd
)
4303 std::unique_ptr
<SwUndoCpyTable
> pUndo
;
4306 GetIDocumentUndoRedo().ClearRedo();
4307 pUndo
.reset(new SwUndoCpyTable(*this));
4311 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
4312 bRet
= pSrcTableNd
->GetTable().MakeCopy( *this, rInsPos
, rBoxes
,
4313 bCpyName
, rStyleName
);
4318 pInsTableNd
= GetNodes()[ rInsPos
.GetNodeIndex() - 1 ]->FindTableNode();
4320 pUndo
->SetTableSttIdx( pInsTableNd
->GetIndex() );
4321 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
4326 RedlineFlags eOld
= getIDocumentRedlineAccess().GetRedlineFlags();
4327 if( getIDocumentRedlineAccess().IsRedlineOn() )
4328 getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On
|
4329 RedlineFlags::ShowInsert
|
4330 RedlineFlags::ShowDelete
);
4332 std::unique_ptr
<SwUndoTableCpyTable
> pUndo
;
4335 GetIDocumentUndoRedo().ClearRedo();
4336 pUndo
.reset(new SwUndoTableCpyTable(*this));
4337 GetIDocumentUndoRedo().DoUndo(false);
4340 rtl::Reference
<SwDoc
> xCpyDoc(&const_cast<SwDoc
&>(pSrcTableNd
->GetDoc()));
4341 bool bDelCpyDoc
= xCpyDoc
== this;
4345 // Copy the Table into a temporary Doc
4346 xCpyDoc
= new SwDoc
;
4348 SwPosition
aPos( xCpyDoc
->GetNodes().GetEndOfContent() );
4349 if( !pSrcTableNd
->GetTable().MakeCopy( *xCpyDoc
, aPos
, rBoxes
, true ))
4355 GetIDocumentUndoRedo().DoUndo(bUndo
);
4359 aPos
.Adjust(SwNodeOffset(-1)); // Set to the Table's EndNode
4360 pSrcTableNd
= aPos
.GetNode().FindTableNode();
4363 const SwStartNode
* pSttNd
= rInsPos
.GetNode().FindTableBoxStartNode();
4365 rInsPos
.nContent
.Assign( nullptr, 0 );
4367 // no complex into complex, but copy into or from new model is welcome
4368 if( ( !pSrcTableNd
->GetTable().IsTableComplex() || pInsTableNd
->GetTable().IsNewModel() )
4369 && ( bDelCpyDoc
|| !rBoxes
.empty() ) )
4371 // Copy the Table "relatively"
4372 const SwSelBoxes
* pBoxes
;
4377 SwTableBox
* pBox
= pInsTableNd
->GetTable().GetTableBox(
4378 pSttNd
->GetIndex() );
4379 OSL_ENSURE( pBox
, "Box is not in this Table" );
4380 aBoxes
.insert( pBox
);
4386 // Copy Table to the selected Lines
4387 bRet
= pInsTableNd
->GetTable().InsTable( pSrcTableNd
->GetTable(),
4388 *pBoxes
, pUndo
.get() );
4392 SwNodeIndex
aNdIdx( *pSttNd
, 1 );
4393 bRet
= pInsTableNd
->GetTable().InsTable( pSrcTableNd
->GetTable(),
4394 aNdIdx
, pUndo
.get() );
4401 // If the Table could not be copied, delete the Undo object
4402 GetIDocumentUndoRedo().DoUndo(bUndo
);
4403 if( bRet
|| !pUndo
->IsEmpty() )
4405 GetIDocumentUndoRedo().AppendUndo(std::move(pUndo
));
4411 rInsPos
.Assign( *pSttNd
);
4412 GetNodes().GoNext( &rInsPos
);
4414 getIDocumentRedlineAccess().SetRedlineFlags( eOld
);
4419 getIDocumentState().SetModified();
4420 getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
4425 bool SwDoc::UnProtectTableCells( SwTable
& rTable
)
4428 std::unique_ptr
<SwUndoAttrTable
> pUndo
;
4429 if (GetIDocumentUndoRedo().DoesUndo())
4430 pUndo
.reset(new SwUndoAttrTable( *rTable
.GetTableNode() ));
4432 SwTableSortBoxes
& rSrtBox
= rTable
.GetTabSortBoxes();
4433 for (size_t i
= rSrtBox
.size(); i
; )
4435 SwFrameFormat
*pBoxFormat
= rSrtBox
[ --i
]->GetFrameFormat();
4436 if( pBoxFormat
->GetProtect().IsContentProtected() )
4438 pBoxFormat
->ResetFormatAttr( RES_PROTECT
);
4443 if( pUndo
&& bChgd
)
4444 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
4448 void SwDoc::UnProtectCells( const OUString
& rName
)
4450 SwTableFormat
* pFormat
= FindTableFormatByName( rName
);
4453 bool bChgd
= UnProtectTableCells( *SwTable::FindTable( pFormat
) );
4455 getIDocumentState().SetModified();
4459 bool SwDoc::UnProtectCells( const SwSelBoxes
& rBoxes
)
4462 if( !rBoxes
.empty() )
4464 std::unique_ptr
<SwUndoAttrTable
> pUndo
;
4465 if (GetIDocumentUndoRedo().DoesUndo())
4466 pUndo
.reset(new SwUndoAttrTable( *rBoxes
[0]->GetSttNd()->FindTableNode() ));
4468 std::map
<SwFrameFormat
*, SwTableBoxFormat
*> aFormatsMap
;
4469 for (size_t i
= rBoxes
.size(); i
; )
4471 SwTableBox
* pBox
= rBoxes
[ --i
];
4472 SwFrameFormat
* pBoxFormat
= pBox
->GetFrameFormat();
4473 if( pBoxFormat
->GetProtect().IsContentProtected() )
4475 std::map
<SwFrameFormat
*, SwTableBoxFormat
*>::const_iterator
const it
=
4476 aFormatsMap
.find(pBoxFormat
);
4477 if (aFormatsMap
.end() != it
)
4478 pBox
->ChgFrameFormat(it
->second
);
4481 SwTableBoxFormat
*const pNewBoxFormat(
4482 static_cast<SwTableBoxFormat
*>(pBox
->ClaimFrameFormat()));
4483 pNewBoxFormat
->ResetFormatAttr( RES_PROTECT
);
4484 aFormatsMap
.insert(std::make_pair(pBoxFormat
, pNewBoxFormat
));
4490 if( pUndo
&& bChgd
)
4491 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo
) );
4496 void SwDoc::UnProtectTables( const SwPaM
& rPam
)
4498 GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY
, nullptr);
4500 bool bChgd
= false, bHasSel
= rPam
.HasMark() ||
4501 rPam
.GetNext() != &rPam
;
4502 sw::TableFrameFormats
& rFormats
= *GetTableFrameFormats();
4504 const SwTableNode
* pTableNd
;
4505 for( auto n
= rFormats
.size(); n
; )
4506 if( nullptr != (pTable
= SwTable::FindTable( rFormats
[ --n
])) &&
4507 nullptr != (pTableNd
= pTable
->GetTableNode() ) &&
4508 pTableNd
->GetNodes().IsDocNodes() )
4510 SwNodeOffset nTableIdx
= pTableNd
->GetIndex();
4512 // Check whether the Table is within the Selection
4515 bool bFound
= false;
4516 SwPaM
* pTmp
= const_cast<SwPaM
*>(&rPam
);
4518 auto [pStt
, pEnd
] = pTmp
->StartEnd(); // SwPosition*
4519 bFound
= pStt
->GetNodeIndex() < nTableIdx
&&
4520 nTableIdx
< pEnd
->GetNodeIndex();
4522 } while( !bFound
&& &rPam
!= ( pTmp
= pTmp
->GetNext() ) );
4524 continue; // Continue searching
4527 // Lift the protection
4528 bChgd
|= UnProtectTableCells( *pTable
);
4531 GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY
, nullptr);
4533 getIDocumentState().SetModified();
4536 bool SwDoc::HasTableAnyProtection( const SwPosition
* pPos
,
4537 const OUString
* pTableName
,
4538 bool* pFullTableProtection
)
4540 bool bHasProtection
= false;
4541 SwTable
* pTable
= nullptr;
4543 pTable
= SwTable::FindTable( FindTableFormatByName( *pTableName
) );
4546 SwTableNode
* pTableNd
= pPos
->GetNode().FindTableNode();
4548 pTable
= &pTableNd
->GetTable();
4553 SwTableSortBoxes
& rSrtBox
= pTable
->GetTabSortBoxes();
4554 for (size_t i
= rSrtBox
.size(); i
; )
4556 SwFrameFormat
*pBoxFormat
= rSrtBox
[ --i
]->GetFrameFormat();
4557 if( pBoxFormat
->GetProtect().IsContentProtected() )
4559 if( !bHasProtection
)
4561 bHasProtection
= true;
4562 if( !pFullTableProtection
)
4564 *pFullTableProtection
= true;
4567 else if( bHasProtection
&& pFullTableProtection
)
4569 *pFullTableProtection
= false;
4574 return bHasProtection
;
4577 SwTableAutoFormat
* SwDoc::MakeTableStyle(const OUString
& rName
, bool bBroadcast
)
4579 SwTableAutoFormat
aTableFormat(rName
);
4580 GetTableStyles().AddAutoFormat(aTableFormat
);
4581 SwTableAutoFormat
* pTableFormat
= GetTableStyles().FindAutoFormat(rName
);
4583 getIDocumentState().SetModified();
4585 if (GetIDocumentUndoRedo().DoesUndo())
4587 GetIDocumentUndoRedo().AppendUndo(
4588 std::make_unique
<SwUndoTableStyleMake
>(rName
, *this));
4592 BroadcastStyleOperation(rName
, SfxStyleFamily::Table
, SfxHintId::StyleSheetCreated
);
4594 return pTableFormat
;
4597 std::unique_ptr
<SwTableAutoFormat
> SwDoc::DelTableStyle(const OUString
& rName
, bool bBroadcast
)
4600 BroadcastStyleOperation(rName
, SfxStyleFamily::Table
, SfxHintId::StyleSheetErased
);
4602 std::unique_ptr
<SwTableAutoFormat
> pReleasedFormat
= GetTableStyles().ReleaseAutoFormat(rName
);
4604 std::vector
<SwTable
*> vAffectedTables
;
4605 if (pReleasedFormat
)
4607 size_t nTableCount
= GetTableFrameFormatCount(true);
4608 for (size_t i
=0; i
< nTableCount
; ++i
)
4610 SwFrameFormat
* pFrameFormat
= &GetTableFrameFormat(i
, true);
4611 SwTable
* pTable
= SwTable::FindTable(pFrameFormat
);
4612 if (pTable
->GetTableStyleName() == pReleasedFormat
->GetName())
4614 pTable
->SetTableStyleName("");
4615 vAffectedTables
.push_back(pTable
);
4619 getIDocumentState().SetModified();
4621 if (GetIDocumentUndoRedo().DoesUndo())
4623 GetIDocumentUndoRedo().AppendUndo(
4624 std::make_unique
<SwUndoTableStyleDelete
>(std::move(pReleasedFormat
), std::move(vAffectedTables
), *this));
4628 return pReleasedFormat
;
4631 void SwDoc::ChgTableStyle(const OUString
& rName
, const SwTableAutoFormat
& rNewFormat
)
4633 SwTableAutoFormat
* pFormat
= GetTableStyles().FindAutoFormat(rName
);
4637 SwTableAutoFormat aOldFormat
= *pFormat
;
4638 *pFormat
= rNewFormat
;
4639 pFormat
->SetName(rName
);
4641 size_t nTableCount
= GetTableFrameFormatCount(true);
4642 for (size_t i
=0; i
< nTableCount
; ++i
)
4644 SwFrameFormat
* pFrameFormat
= &GetTableFrameFormat(i
, true);
4645 SwTable
* pTable
= SwTable::FindTable(pFrameFormat
);
4646 if (pTable
->GetTableStyleName() == rName
)
4647 if (SwFEShell
* pFEShell
= GetDocShell()->GetFEShell())
4648 pFEShell
->UpdateTableStyleFormatting(pTable
->GetTableNode());
4651 getIDocumentState().SetModified();
4653 if (GetIDocumentUndoRedo().DoesUndo())
4655 GetIDocumentUndoRedo().AppendUndo(
4656 std::make_unique
<SwUndoTableStyleUpdate
>(*pFormat
, aOldFormat
, *this));
4660 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */