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 <hintids.hxx>
21 #include <editeng/boxitem.hxx>
22 #include <editeng/brushitem.hxx>
23 #include <editeng/frmdiritem.hxx>
25 #include <fmtornt.hxx>
26 #include <fmtfsize.hxx>
27 #include <fmtrowsplt.hxx>
30 #include <cellfrm.hxx>
34 #include <svx/svxids.hrc>
36 #include <IDocumentUndoRedo.hxx>
37 #include <IDocumentState.hxx>
38 #include <IDocumentContentOperations.hxx>
39 #include <IDocumentRedlineAccess.hxx>
40 #include <IDocumentLayoutAccess.hxx>
44 #include <swtable.hxx>
45 #include <htmltbl.hxx>
47 #include <swtblfmt.hxx>
48 #include <ndindex.hxx>
51 #include <UndoTable.hxx>
52 #include <o3tl/enumrange.hxx>
53 #include <o3tl/safeint.hxx>
54 #include <osl/diagnose.h>
55 #include <redline.hxx>
57 using ::editeng::SvxBorderLine
;
58 using namespace ::com::sun::star
;
60 // See swtable.cxx too
63 static bool IsSame( tools::Long nA
, tools::Long nB
) { return std::abs(nA
-nB
) <= COLFUZZY
; }
67 // SwTableLine::ChgFrameFormat may delete old format which doesn't have writer listeners anymore.
68 // This may invalidate my pointers, and lead to use-after-free. For this reason, I register myself
69 // as a writer listener for the old format here, and take care to delete formats without listeners
71 class SwTableFormatCmp
: public SwClient
74 SwTableFormatCmp( SwFrameFormat
*pOld
, SwFrameFormat
*pNew
, sal_Int16 nType
);
75 ~SwTableFormatCmp() override
;
77 static SwFrameFormat
* FindNewFormat(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rArr
,
78 SwFrameFormat
const* pOld
, sal_Int16 nType
);
81 SwFrameFormat
*m_pOld
, *m_pNew
;
87 SwTableFormatCmp::SwTableFormatCmp(SwFrameFormat
* pO
, SwFrameFormat
* pN
, sal_Int16 nT
)
96 SwTableFormatCmp::~SwTableFormatCmp()
100 m_pOld
->Remove(this);
101 if (!m_pOld
->HasWriterListeners())
107 SwFrameFormat
* SwTableFormatCmp::FindNewFormat(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rArr
,
108 SwFrameFormat
const* pOld
, sal_Int16 nType
)
110 for (const auto& pCmp
: rArr
)
112 if (pCmp
->m_pOld
== pOld
&& pCmp
->m_nType
== nType
)
118 static void lcl_GetStartEndCell( const SwCursor
& rCursor
,
119 SwLayoutFrame
*&prStart
, SwLayoutFrame
*&prEnd
)
121 OSL_ENSURE( rCursor
.GetPointContentNode() && rCursor
.GetMarkContentNode(),
122 "Tab selection not at ContentNode" );
124 Point aPtPos
, aMkPos
;
125 const SwShellCursor
* pShCursor
= dynamic_cast<const SwShellCursor
*>(&rCursor
);
128 aPtPos
= pShCursor
->GetPtPos();
129 aMkPos
= pShCursor
->GetMkPos();
133 SwContentNode
* pPointNd
= rCursor
.GetPointContentNode();
134 SwContentNode
* pMarkNd
= rCursor
.GetMarkContentNode();
136 std::pair
<Point
, bool> tmp(aPtPos
, true);
137 SwFrame
*const pPointFrame
= pPointNd
? pPointNd
->getLayoutFrame(pPointNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp
) : nullptr;
139 SwFrame
*const pMarkFrame
= pMarkNd
? pMarkNd
->getLayoutFrame(pMarkNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp
) : nullptr;
141 prStart
= pPointFrame
? pPointFrame
->GetUpper() : nullptr;
142 prEnd
= pMarkFrame
? pMarkFrame
->GetUpper() : nullptr;
145 static bool lcl_GetBoxSel( const SwCursor
& rCursor
, SwSelBoxes
& rBoxes
,
146 bool bAllCursor
= false )
148 const SwTableCursor
* pTableCursor
=
149 dynamic_cast<const SwTableCursor
*>(&rCursor
);
151 ::GetTableSelCrs( *pTableCursor
, rBoxes
);
154 const SwPaM
*pCurPam
= &rCursor
, *pSttPam
= pCurPam
;
156 const SwNode
* pNd
= pCurPam
->GetPointNode().FindTableBoxStartNode();
159 SwTableBox
* pBox
= const_cast<SwTableBox
*>(pNd
->FindTableNode()->GetTable().
160 GetTableBox( pNd
->GetIndex() ));
161 rBoxes
.insert( pBox
);
163 } while( bAllCursor
&&
164 pSttPam
!= ( pCurPam
= pCurPam
->GetNext()) );
166 return !rBoxes
.empty();
169 static void InsertLine( std::vector
<SwTableLine
*>& rLineArr
, SwTableLine
* pLine
)
171 if( rLineArr
.end() == std::find( rLineArr
.begin(), rLineArr
.end(), pLine
) )
172 rLineArr
.push_back( pLine
);
175 static bool lcl_IsAnLower( const SwTableLine
*pLine
, const SwTableLine
*pAssumed
)
177 const SwTableLine
*pTmp
= pAssumed
->GetUpper() ?
178 pAssumed
->GetUpper()->GetUpper() : nullptr;
183 pTmp
= pTmp
->GetUpper() ? pTmp
->GetUpper()->GetUpper() : nullptr;
192 std::vector
<SwTableLine
*> &m_rLines
;
193 const SwTable
&m_rTable
;
196 LinesAndTable(std::vector
<SwTableLine
*> &rL
, const SwTable
&rTable
) :
197 m_rLines(rL
), m_rTable(rTable
), m_bInsertLines(true) {}
202 static bool FindLine_( FndLine_
& rLine
, LinesAndTable
* pPara
);
204 static bool FindBox_( FndBox_
& rBox
, LinesAndTable
* pPara
)
206 if (!rBox
.GetLines().empty())
208 pPara
->m_bInsertLines
= true;
209 for (auto const& rpFndLine
: rBox
.GetLines())
211 FindLine_(*rpFndLine
, pPara
);
214 if (pPara
->m_bInsertLines
)
216 const SwTableLines
&rLines
= (rBox
.GetBox())
217 ? rBox
.GetBox()->GetTabLines()
218 : pPara
->m_rTable
.GetTabLines();
219 if (rBox
.GetLines().size() == rLines
.size())
221 for ( auto pLine
: rLines
)
222 ::InsertLine(pPara
->m_rLines
, pLine
);
225 pPara
->m_bInsertLines
= false;
228 else if (rBox
.GetBox())
230 ::InsertLine(pPara
->m_rLines
, rBox
.GetBox()->GetUpper());
235 bool FindLine_( FndLine_
& rLine
, LinesAndTable
* pPara
)
237 for (auto const& it
: rLine
.GetBoxes())
239 FindBox_(*it
, pPara
);
244 static void lcl_CollectLines( std::vector
<SwTableLine
*> &rArr
, const SwCursor
& rCursor
, bool bRemoveLines
)
246 // Collect the selected Boxes first
248 if( !::lcl_GetBoxSel( rCursor
, aBoxes
))
251 // Copy the selected structure
252 const SwTable
&rTable
= aBoxes
[0]->GetSttNd()->FindTableNode()->GetTable();
253 LinesAndTable
aPara( rArr
, rTable
);
254 FndBox_
aFndBox( nullptr, nullptr );
256 FndPara
aTmpPara( aBoxes
, &aFndBox
);
257 ForEach_FndLineCopyCol( const_cast<SwTableLines
&>(rTable
.GetTabLines()), &aTmpPara
);
260 // Collect the Lines which only contain selected Boxes
261 ::FindBox_(aFndBox
, &aPara
);
263 // Remove lines, that have a common superordinate row.
264 // (Not for row split)
268 for ( std::vector
<SwTableLine
*>::size_type i
= 0; i
< rArr
.size(); ++i
)
270 SwTableLine
*pUpLine
= rArr
[i
];
271 for ( std::vector
<SwTableLine
*>::size_type k
= 0; k
< rArr
.size(); ++k
)
273 if ( k
!= i
&& ::lcl_IsAnLower( pUpLine
, rArr
[k
] ) )
275 rArr
.erase( rArr
.begin() + k
);
284 static void lcl_ProcessRowAttr(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rFormatCmp
,
285 SwTableLine
* pLine
, const SfxPoolItem
& rNew
)
287 SwFrameFormat
*pNewFormat
= SwTableFormatCmp::FindNewFormat( rFormatCmp
, pLine
->GetFrameFormat(), 0 );
288 if ( nullptr != pNewFormat
)
289 pLine
->ChgFrameFormat( static_cast<SwTableLineFormat
*>(pNewFormat
) );
292 SwFrameFormat
*pOld
= pLine
->GetFrameFormat();
293 SwFrameFormat
*pNew
= pLine
->ClaimFrameFormat();
294 pNew
->SetFormatAttr( rNew
);
295 rFormatCmp
.push_back(std::make_unique
<SwTableFormatCmp
>(pOld
, pNew
, 0));
299 static void lcl_ProcessBoxSize(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rFormatCmp
,
300 SwTableBox
* pBox
, const SwFormatFrameSize
& rNew
);
302 static void lcl_ProcessRowSize(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rFormatCmp
,
303 SwTableLine
* pLine
, const SwFormatFrameSize
& rNew
)
305 lcl_ProcessRowAttr( rFormatCmp
, pLine
, rNew
);
306 SwTableBoxes
&rBoxes
= pLine
->GetTabBoxes();
307 for ( auto pBox
: rBoxes
)
308 ::lcl_ProcessBoxSize( rFormatCmp
, pBox
, rNew
);
311 static void lcl_ProcessBoxSize(std::vector
<std::unique_ptr
<SwTableFormatCmp
>>& rFormatCmp
,
312 SwTableBox
* pBox
, const SwFormatFrameSize
& rNew
)
314 SwTableLines
&rLines
= pBox
->GetTabLines();
315 if ( !rLines
.empty() )
317 SwFormatFrameSize
aSz( rNew
);
318 aSz
.SetHeight( rNew
.GetHeight() ? rNew
.GetHeight() / rLines
.size() : 0 );
319 for ( auto pLine
: rLines
)
320 ::lcl_ProcessRowSize( rFormatCmp
, pLine
, aSz
);
324 void SwDoc::SetRowSplit( const SwCursor
& rCursor
, const SwFormatRowSplit
&rNew
)
326 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
330 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
331 ::lcl_CollectLines( aRowArr
, rCursor
, false );
333 if( aRowArr
.empty() )
336 if (GetIDocumentUndoRedo().DoesUndo())
338 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
341 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
342 aFormatCmp
.reserve( std::max( 255, static_cast<int>(aRowArr
.size()) ) );
344 for( auto pLn
: aRowArr
)
345 ::lcl_ProcessRowAttr( aFormatCmp
, pLn
, rNew
);
347 getIDocumentState().SetModified();
350 std::unique_ptr
<SwFormatRowSplit
> SwDoc::GetRowSplit( const SwCursor
& rCursor
)
352 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
356 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
357 ::lcl_CollectLines( aRowArr
, rCursor
, false );
359 if( aRowArr
.empty() )
362 SwFormatRowSplit
* pSz
= &const_cast<SwFormatRowSplit
&>(aRowArr
[0]->GetFrameFormat()->GetRowSplit());
364 for ( auto pLn
: aRowArr
)
366 if ( pSz
->GetValue() != pLn
->GetFrameFormat()->GetRowSplit().GetValue() )
371 return std::make_unique
<SwFormatRowSplit
>( *pSz
);
375 * Methods: SetRowHeight(), GetRowHeight()
377 * The line height is calculated from the Selection.
378 * Starting with every Cell within the Selection, all Cells are iterated
379 * through in an upwards fashion.
381 * The topmost Line gets the requested value, all Lines below it get
382 * a respective value that is calculated from the relation of the old and
383 * new size of the topmost Line in the lower line's own size.
385 * All changed Lines may get an own FrameFormat.
386 * Of course we can only touch every Line once.
389 void SwDoc::SetRowHeight( const SwCursor
& rCursor
, const SwFormatFrameSize
&rNew
)
391 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
395 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
396 ::lcl_CollectLines( aRowArr
, rCursor
, true );
398 if( aRowArr
.empty() )
401 if (GetIDocumentUndoRedo().DoesUndo())
403 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
406 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
407 aFormatCmp
.reserve( std::max( 255, static_cast<int>(aRowArr
.size()) ) );
408 for ( auto pLn
: aRowArr
)
409 ::lcl_ProcessRowSize( aFormatCmp
, pLn
, rNew
);
411 getIDocumentState().SetModified();
414 std::unique_ptr
<SwFormatFrameSize
> SwDoc::GetRowHeight( const SwCursor
& rCursor
)
416 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
420 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
421 ::lcl_CollectLines( aRowArr
, rCursor
, true );
423 if( aRowArr
.empty() )
426 SwFormatFrameSize
* pSz
= &const_cast<SwFormatFrameSize
&>(aRowArr
[0]->GetFrameFormat()->GetFrameSize());
428 for ( auto pLn
: aRowArr
)
430 if ( *pSz
!= pLn
->GetFrameFormat()->GetFrameSize() )
433 return std::make_unique
<SwFormatFrameSize
>( *pSz
);
436 bool SwDoc::BalanceRowHeight( const SwCursor
& rCursor
, bool bTstOnly
, const bool bOptimize
)
439 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
442 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
443 ::lcl_CollectLines( aRowArr
, rCursor
, true );
445 if( 1 < aRowArr
.size() )
449 tools::Long nHeight
= 0;
450 sal_Int32 nTotalHeight
= 0;
451 for ( auto pLn
: aRowArr
)
455 SwIterator
<SwFrame
,SwFormat
> aIter( *pLn
->GetFrameFormat() );
456 SwFrame
* pFrame
= aIter
.First();
459 nHeight
= std::max( nHeight
, pFrame
->getFrameArea().Height() );
460 pFrame
= aIter
.Next();
462 nTotalHeight
+= nHeight
;
466 nHeight
= nTotalHeight
/ aRowArr
.size();
468 SwFormatFrameSize
aNew( SwFrameSize::Minimum
, 0, nHeight
);
470 if (GetIDocumentUndoRedo().DoesUndo())
472 GetIDocumentUndoRedo().AppendUndo(
473 std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
476 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
477 aFormatCmp
.reserve( std::max( 255, static_cast<int>(aRowArr
.size()) ) );
478 for( auto pLn
: aRowArr
)
479 ::lcl_ProcessRowSize( aFormatCmp
, pLn
, aNew
);
481 getIDocumentState().SetModified();
489 void SwDoc::SetRowBackground( const SwCursor
& rCursor
, const SvxBrushItem
&rNew
)
491 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
495 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
496 ::lcl_CollectLines( aRowArr
, rCursor
, true );
498 if( aRowArr
.empty() )
501 if (GetIDocumentUndoRedo().DoesUndo())
503 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
506 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
507 aFormatCmp
.reserve( std::max( 255, static_cast<int>(aRowArr
.size()) ) );
509 for( auto pLn
: aRowArr
)
510 ::lcl_ProcessRowAttr( aFormatCmp
, pLn
, rNew
);
512 getIDocumentState().SetModified();
515 bool SwDoc::GetRowBackground( const SwCursor
& rCursor
, std::unique_ptr
<SvxBrushItem
>& rToFill
)
518 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
521 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
522 ::lcl_CollectLines( aRowArr
, rCursor
, true );
524 if( !aRowArr
.empty() )
526 rToFill
= aRowArr
[0]->GetFrameFormat()->makeBackgroundBrushItem();
529 for ( std::vector
<SwTableLine
*>::size_type i
= 1; i
< aRowArr
.size(); ++i
)
531 std::unique_ptr
<SvxBrushItem
> aAlternative(aRowArr
[i
]->GetFrameFormat()->makeBackgroundBrushItem());
533 if ( *rToFill
!= *aAlternative
)
544 // has a table row, which is not a tracked deletion
545 bool SwDoc::HasRowNotTracked( const SwCursor
& rCursor
)
547 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
551 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
552 ::lcl_CollectLines( aRowArr
, rCursor
, true );
554 if( aRowArr
.empty() )
557 SwRedlineTable::size_type nRedlinePos
= 0;
558 SwDoc
* pDoc
= aRowArr
[0]->GetFrameFormat()->GetDoc();
559 const IDocumentRedlineAccess
& rIDRA
= pDoc
->getIDocumentRedlineAccess();
561 for( auto pLn
: aRowArr
)
563 auto pHasTextChangesOnlyProp
= pLn
->GetFrameFormat()->GetAttrSet().GetItem
<SvxPrintItem
>(RES_PRINT
);
564 if ( !pHasTextChangesOnlyProp
|| pHasTextChangesOnlyProp
->GetValue() )
565 // there is a not tracked row in the table selection
568 // tdf#150666 examine tracked row: it's possible to delete a tracked insertion
569 SwRedlineTable::size_type nPos
= pLn
->UpdateTextChangesOnly(nRedlinePos
);
570 if ( nPos
!= SwRedlineTable::npos
)
572 const SwRedlineTable
& aRedlineTable
= rIDRA
.GetRedlineTable();
573 SwRangeRedline
* pTmp
= aRedlineTable
[ nPos
];
574 if ( RedlineType::Insert
== pTmp
->GetType() )
581 void SwDoc::SetRowNotTracked( const SwCursor
& rCursor
,
582 const SvxPrintItem
&rNew
, bool bAll
, bool bIns
)
584 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
588 std::vector
<SwTableLine
*> aRowArr
; // For Lines collecting
591 const SwTableLines
&rLines
= pTableNd
->GetTable().GetTabLines();
592 aRowArr
.insert(aRowArr
.end(), rLines
.begin(), rLines
.end());
595 ::lcl_CollectLines( aRowArr
, rCursor
, true );
597 if( aRowArr
.empty() )
600 if (GetIDocumentUndoRedo().DoesUndo())
602 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
605 bool bInsertDummy
= !bAll
&& !bIns
&&
606 // HasTextChangesOnly == false, i.e. a tracked row change (deletion, if bIns == false)
608 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
609 aFormatCmp
.reserve( std::max( 255, static_cast<int>(aRowArr
.size()) ) );
611 SwRedlineTable::size_type nRedlinePos
= 0;
612 for( auto pLn
: aRowArr
)
614 // tdf#150666 deleting row insertion from the same author needs special handling,
615 // because removing redlines of the author can result an empty line,
616 // which doesn't contain any redline for the tracked row
617 bool bDeletionOfOwnRowInsertion
= false;
620 SwRedlineTable::size_type nPos
= pLn
->UpdateTextChangesOnly(nRedlinePos
);
621 if ( nPos
!= SwRedlineTable::npos
)
623 SwDoc
* pDoc
= pLn
->GetFrameFormat()->GetDoc();
624 IDocumentRedlineAccess
& rIDRA
= pDoc
->getIDocumentRedlineAccess();
625 const SwRedlineTable
& aRedlineTable
= rIDRA
.GetRedlineTable();
626 SwRangeRedline
* pTmp
= aRedlineTable
[ nPos
];
627 if ( RedlineType::Insert
== pTmp
->GetType() &&
628 rIDRA
.GetRedlineAuthor() == pTmp
->GetRedlineData().GetAuthor() &&
629 pTmp
->GetText()[0] == CH_TXT_TRACKED_DUMMY_CHAR
)
631 bDeletionOfOwnRowInsertion
= true;
636 ::lcl_ProcessRowAttr( aFormatCmp
, pLn
, rNew
);
637 // as a workaround for the rows without text content,
638 // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
639 // (unless the table is part of a bigger deletion, where the
640 // new redline can cause a problem)
641 if ( bInsertDummy
&& (pLn
->IsEmpty() || bDeletionOfOwnRowInsertion
) )
643 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
644 SwNodeIndex
aInsPos( *(pLn
->GetTabBoxes()[0]->GetSttNd()), 1 );
645 RedlineFlags eOld
= getIDocumentRedlineAccess().GetRedlineFlags();
646 getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE
);
648 getIDocumentContentOperations().InsertString( aPaM
,
649 OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR
) );
651 getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
652 getIDocumentContentOperations().DeleteAndJoin( aPaM
);
656 getIDocumentState().SetModified();
659 static void InsertCell( std::vector
<SwCellFrame
*>& rCellArr
, SwCellFrame
* pCellFrame
)
661 if( rCellArr
.end() == std::find( rCellArr
.begin(), rCellArr
.end(), pCellFrame
) )
662 rCellArr
.push_back( pCellFrame
);
665 static void lcl_CollectCells( std::vector
<SwCellFrame
*> &rArr
, const SwRect
&rUnion
,
668 SwLayoutFrame
*pCell
= pTab
->FirstCell();
671 // If the Cell contains a CellFrame, we need to use it
672 // in order to get to the Cell
673 while ( !pCell
->IsCellFrame() )
674 pCell
= pCell
->GetUpper();
675 OSL_ENSURE( pCell
, "Frame is not a Cell" );
676 if ( rUnion
.Overlaps( pCell
->getFrameArea() ) )
677 ::InsertCell( rArr
, static_cast<SwCellFrame
*>(pCell
) );
679 // Make sure the Cell is left (Areas)
680 SwLayoutFrame
*pTmp
= pCell
;
682 { pTmp
= pTmp
->GetNextLayoutLeaf();
683 } while ( pCell
->IsAnLower( pTmp
) );
685 } while( pCell
&& pTab
->IsAnLower( pCell
) );
688 void SwDoc::SetTabBorders( const SwCursor
& rCursor
, const SfxItemSet
& rSet
)
690 SwContentNode
* pCntNd
= rCursor
.GetPoint()->GetNode().GetContentNode();
691 SwTableNode
* pTableNd
= pCntNd
? pCntNd
->FindTableNode() : nullptr;
695 SwLayoutFrame
*pStart
, *pEnd
;
696 ::lcl_GetStartEndCell( rCursor
, pStart
, pEnd
);
699 ::MakeSelUnions( aUnions
, pStart
, pEnd
);
701 if( aUnions
.empty() )
704 SwTable
& rTable
= pTableNd
->GetTable();
705 if (GetIDocumentUndoRedo().DoesUndo())
707 GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoAttrTable
>(*pTableNd
) );
710 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
711 aFormatCmp
.reserve( 255 );
712 const SvxBoxItem
* pSetBox
;
713 const SvxBoxInfoItem
*pSetBoxInfo
;
715 const SvxBorderLine
* pLeft
= nullptr;
716 const SvxBorderLine
* pRight
= nullptr;
717 const SvxBorderLine
* pTop
= nullptr;
718 const SvxBorderLine
* pBottom
= nullptr;
719 const SvxBorderLine
* pHori
= nullptr;
720 const SvxBorderLine
* pVert
= nullptr;
721 bool bHoriValid
= true, bVertValid
= true,
722 bTopValid
= true, bBottomValid
= true,
723 bLeftValid
= true, bRightValid
= true;
725 // The Flags in the BoxInfo Item decide whether a BorderLine is valid!
726 pSetBoxInfo
= rSet
.GetItemIfSet( SID_ATTR_BORDER_INNER
, false );
729 pHori
= pSetBoxInfo
->GetHori();
730 pVert
= pSetBoxInfo
->GetVert();
732 bHoriValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::HORI
);
733 bVertValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::VERT
);
735 // Do we want to evaluate these?
736 bTopValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::TOP
);
737 bBottomValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::BOTTOM
);
738 bLeftValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::LEFT
);
739 bRightValid
= pSetBoxInfo
->IsValid(SvxBoxInfoItemValidFlags::RIGHT
);
742 pSetBox
= rSet
.GetItemIfSet( RES_BOX
, false );
745 pLeft
= pSetBox
->GetLeft();
746 pRight
= pSetBox
->GetRight();
747 pTop
= pSetBox
->GetTop();
748 pBottom
= pSetBox
->GetBottom();
752 // Not set, thus not valid values
753 bTopValid
= bBottomValid
= bLeftValid
= bRightValid
= false;
758 for ( SwSelUnions::size_type i
= 0; i
< aUnions
.size(); ++i
)
760 SwSelUnion
*pUnion
= &aUnions
[i
];
761 SwTabFrame
*pTab
= pUnion
->GetTable();
762 const SwRect
&rUnion
= pUnion
->GetUnion();
763 const bool bLast
= (i
== aUnions
.size() - 1);
765 std::vector
<SwCellFrame
*> aCellArr
;
766 aCellArr
.reserve( 255 );
767 ::lcl_CollectCells( aCellArr
, pUnion
->GetUnion(), pTab
);
769 // All Cell Borders that match the UnionRect or extend it are
770 // Outer Borders. All others are Inner Borders.
772 // New: The Outer Borders can, depending on whether it's a
773 // Start/Middle/Follow Table (for Selection via FollowTabs),
774 // also not be Outer Borders.
775 // Outer Borders are set on the left, right, at the top and at the bottom.
776 // Inner Borders are only set at the top and on the left.
777 for ( auto pCell
: aCellArr
)
779 const bool bVert
= pTab
->IsVertical();
780 const bool bRTL
= pTab
->IsRightToLeft();
781 bool bTopOver
, bLeftOver
, bRightOver
, bBottomOver
;
784 bTopOver
= pCell
->getFrameArea().Right() >= rUnion
.Right();
785 bLeftOver
= pCell
->getFrameArea().Top() <= rUnion
.Top();
786 bRightOver
= pCell
->getFrameArea().Bottom() >= rUnion
.Bottom();
787 bBottomOver
= pCell
->getFrameArea().Left() <= rUnion
.Left();
791 bTopOver
= pCell
->getFrameArea().Top() <= rUnion
.Top();
792 bLeftOver
= pCell
->getFrameArea().Left() <= rUnion
.Left();
793 bRightOver
= pCell
->getFrameArea().Right() >= rUnion
.Right();
794 bBottomOver
= pCell
->getFrameArea().Bottom() >= rUnion
.Bottom();
798 std::swap( bLeftOver
, bRightOver
);
800 // Do not set anything by default in HeadlineRepeats
801 if ( pTab
->IsFollow() &&
802 ( pTab
->IsInHeadline( *pCell
) ||
803 // Same holds for follow flow rows
804 pCell
->IsInFollowFlowRow() ) )
807 SvxBoxItem
aBox( pCell
->GetFormat()->GetBox() );
814 if ( bFirst
&& bTopOver
)
816 aBox
.SetLine( pTop
, SvxBoxItemLine::TOP
);
819 else if ( bHoriValid
)
821 aBox
.SetLine( nullptr, SvxBoxItemLine::TOP
);
826 // Fix fdo#62470 correct the input for RTL table
829 if( bLeftOver
&& bRightOver
)
833 aBox
.SetLine( pLeft
, SvxBoxItemLine::RIGHT
);
838 aBox
.SetLine( pRight
, SvxBoxItemLine::LEFT
);
846 aBox
.SetLine( bRightOver
? pLeft
: nullptr, SvxBoxItemLine::RIGHT
);
856 aBox
.SetLine( pRight
, SvxBoxItemLine::LEFT
);
860 else if ( bVertValid
)
862 aBox
.SetLine( pVert
, SvxBoxItemLine::LEFT
);
874 aBox
.SetLine( pLeft
, SvxBoxItemLine::LEFT
);
878 else if( bVertValid
)
880 aBox
.SetLine( pVert
, SvxBoxItemLine::LEFT
);
889 aBox
.SetLine( pRight
, SvxBoxItemLine::RIGHT
);
892 else if ( bVertValid
)
894 aBox
.SetLine( nullptr, SvxBoxItemLine::RIGHT
);
901 if ( bLast
&& bBottomOver
)
905 aBox
.SetLine( pBottom
, SvxBoxItemLine::BOTTOM
);
909 else if( bHoriValid
)
911 aBox
.SetLine( pHori
, SvxBoxItemLine::BOTTOM
);
917 for( SvxBoxItemLine k
: o3tl::enumrange
<SvxBoxItemLine
>() )
918 aBox
.SetDistance( pSetBox
->GetDistance( k
), k
);
921 SwTableBox
*pBox
= const_cast<SwTableBox
*>(pCell
->GetTabBox());
922 SwFrameFormat
*pNewFormat
= SwTableFormatCmp::FindNewFormat( aFormatCmp
, pBox
->GetFrameFormat(), nType
);
923 if ( nullptr != pNewFormat
)
924 pBox
->ChgFrameFormat( static_cast<SwTableBoxFormat
*>(pNewFormat
) );
927 SwFrameFormat
*pOld
= pBox
->GetFrameFormat();
928 SwFrameFormat
*pNew
= pBox
->ClaimFrameFormat();
929 pNew
->SetFormatAttr( aBox
);
930 aFormatCmp
.push_back(std::make_unique
<SwTableFormatCmp
>(pOld
, pNew
, nType
));
937 SwHTMLTableLayout
*pTableLayout
= rTable
.GetHTMLTableLayout();
940 SwContentFrame
* pFrame
= rCursor
.GetPointContentNode()->getLayoutFrame( rCursor
.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
941 SwTabFrame
* pTabFrame
= pFrame
->ImplFindTabFrame();
943 pTableLayout
->BordersChanged(
944 pTableLayout
->GetBrowseWidthByTabFrame( *pTabFrame
) );
946 ::ClearFEShellTabCols(*this, nullptr);
947 getIDocumentState().SetModified();
950 static void lcl_SetLineStyle( SvxBorderLine
*pToSet
,
951 const Color
*pColor
, const SvxBorderLine
*pBorderLine
)
957 Color
aTmp( pToSet
->GetColor() );
958 *pToSet
= *pBorderLine
;
959 pToSet
->SetColor( aTmp
);
962 *pToSet
= *pBorderLine
;
965 pToSet
->SetColor( *pColor
);
968 void SwDoc::SetTabLineStyle( const SwCursor
& rCursor
,
969 const Color
* pColor
, bool bSetLine
,
970 const SvxBorderLine
* pBorderLine
)
972 SwContentNode
* pCntNd
= rCursor
.GetPoint()->GetNode().GetContentNode();
973 SwTableNode
* pTableNd
= pCntNd
? pCntNd
->FindTableNode() : nullptr;
977 SwLayoutFrame
*pStart
, *pEnd
;
978 ::lcl_GetStartEndCell( rCursor
, pStart
, pEnd
);
981 ::MakeSelUnions( aUnions
, pStart
, pEnd
);
983 if( aUnions
.empty() )
986 SwTable
& rTable
= pTableNd
->GetTable();
987 if (GetIDocumentUndoRedo().DoesUndo())
989 GetIDocumentUndoRedo().AppendUndo(std::make_unique
<SwUndoAttrTable
>(*pTableNd
));
992 SvxBorderLine
aDefaultBorder(pBorderLine
? *pBorderLine
993 : SvxBorderLine(pColor
, SvxBorderLineWidth::VeryThin
));
994 if (pColor
&& pBorderLine
)
995 aDefaultBorder
.SetColor(*pColor
);
997 for( auto &rU
: aUnions
)
999 SwSelUnion
*pUnion
= &rU
;
1000 SwTabFrame
*pTab
= pUnion
->GetTable();
1001 std::vector
<SwCellFrame
*> aCellArr
;
1002 aCellArr
.reserve( 255 );
1003 ::lcl_CollectCells( aCellArr
, pUnion
->GetUnion(), pTab
);
1005 for ( auto pCell
: aCellArr
)
1007 // Do not set anything by default in HeadlineRepeats
1008 if ( pTab
->IsFollow() && pTab
->IsInHeadline( *pCell
) )
1011 const_cast<SwTableBox
*>(pCell
->GetTabBox())->ClaimFrameFormat();
1012 SwFrameFormat
*pFormat
= pCell
->GetFormat();
1013 std::unique_ptr
<SvxBoxItem
> aBox(pFormat
->GetBox().Clone());
1015 SvxBorderLine
* pTop
= aBox
->GetTop();
1016 SvxBorderLine
* pBot
= aBox
->GetBottom();
1017 SvxBorderLine
* pLeft
= aBox
->GetLeft();
1018 SvxBorderLine
* pRight
= aBox
->GetRight();
1020 if ( !pBorderLine
&& bSetLine
)
1022 aBox
.reset(::GetDfltAttr(RES_BOX
)->Clone());
1024 else if ((pColor
|| pBorderLine
) && !pTop
&& !pBot
&& !pLeft
&& !pRight
)
1026 aBox
->SetLine(&aDefaultBorder
, SvxBoxItemLine::TOP
);
1027 aBox
->SetLine(&aDefaultBorder
, SvxBoxItemLine::BOTTOM
);
1028 aBox
->SetLine(&aDefaultBorder
, SvxBoxItemLine::LEFT
);
1029 aBox
->SetLine(&aDefaultBorder
, SvxBoxItemLine::RIGHT
);
1034 ::lcl_SetLineStyle(pTop
, pColor
, pBorderLine
);
1036 ::lcl_SetLineStyle(pBot
, pColor
, pBorderLine
);
1038 ::lcl_SetLineStyle(pLeft
, pColor
, pBorderLine
);
1040 ::lcl_SetLineStyle(pRight
, pColor
, pBorderLine
);
1042 pFormat
->SetFormatAttr( *aBox
);
1046 SwHTMLTableLayout
*pTableLayout
= rTable
.GetHTMLTableLayout();
1049 SwContentFrame
* pFrame
= rCursor
.GetPointContentNode()->getLayoutFrame( rCursor
.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
1050 SwTabFrame
* pTabFrame
= pFrame
->ImplFindTabFrame();
1052 pTableLayout
->BordersChanged(
1053 pTableLayout
->GetBrowseWidthByTabFrame( *pTabFrame
) );
1055 ::ClearFEShellTabCols(*this, nullptr);
1056 getIDocumentState().SetModified();
1059 void SwDoc::GetTabBorders( const SwCursor
& rCursor
, SfxItemSet
& rSet
)
1061 SwContentNode
* pCntNd
= rCursor
.GetPoint()->GetNode().GetContentNode();
1062 SwTableNode
* pTableNd
= pCntNd
? pCntNd
->FindTableNode() : nullptr;
1066 SwLayoutFrame
*pStart
, *pEnd
;
1067 ::lcl_GetStartEndCell( rCursor
, pStart
, pEnd
);
1069 SwSelUnions aUnions
;
1070 ::MakeSelUnions( aUnions
, pStart
, pEnd
);
1072 if( aUnions
.empty() )
1075 SvxBoxItem
aSetBox ( rSet
.Get(RES_BOX
) );
1076 SvxBoxInfoItem
aSetBoxInfo( rSet
.Get(SID_ATTR_BORDER_INNER
) );
1078 bool bTopSet
= false,
1084 bDistanceSet
= false,
1087 aSetBoxInfo
.ResetFlags();
1089 for ( SwSelUnions::size_type i
= 0; i
< aUnions
.size(); ++i
)
1091 SwSelUnion
*pUnion
= &aUnions
[i
];
1092 const SwTabFrame
*pTab
= pUnion
->GetTable();
1093 const SwRect
&rUnion
= pUnion
->GetUnion();
1094 const bool bFirst
= i
== 0;
1095 const bool bLast
= (i
== aUnions
.size() - 1);
1097 std::vector
<SwCellFrame
*> aCellArr
;
1098 aCellArr
.reserve(255);
1099 ::lcl_CollectCells( aCellArr
, rUnion
, const_cast<SwTabFrame
*>(pTab
) );
1101 for ( auto pCell
: aCellArr
)
1103 const bool bVert
= pTab
->IsVertical();
1104 const bool bRTL
= bRTLTab
= pTab
->IsRightToLeft();
1105 bool bTopOver
, bLeftOver
, bRightOver
, bBottomOver
;
1108 bTopOver
= pCell
->getFrameArea().Right() >= rUnion
.Right();
1109 bLeftOver
= pCell
->getFrameArea().Top() <= rUnion
.Top();
1110 bRightOver
= pCell
->getFrameArea().Bottom() >= rUnion
.Bottom();
1111 bBottomOver
= pCell
->getFrameArea().Left() <= rUnion
.Left();
1115 bTopOver
= pCell
->getFrameArea().Top() <= rUnion
.Top();
1116 bLeftOver
= pCell
->getFrameArea().Left() <= rUnion
.Left();
1117 bRightOver
= pCell
->getFrameArea().Right() >= rUnion
.Right();
1118 bBottomOver
= pCell
->getFrameArea().Bottom() >= rUnion
.Bottom();
1122 std::swap( bLeftOver
, bRightOver
);
1124 const SwFrameFormat
*pFormat
= pCell
->GetFormat();
1125 const SvxBoxItem
&rBox
= pFormat
->GetBox();
1128 if ( bFirst
&& bTopOver
)
1130 if (aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::TOP
))
1134 aSetBox
.SetLine( rBox
.GetTop(), SvxBoxItemLine::TOP
);
1136 else if ((aSetBox
.GetTop() && rBox
.GetTop() &&
1137 (*aSetBox
.GetTop() != *rBox
.GetTop())) ||
1138 ((!aSetBox
.GetTop()) != (!rBox
.GetTop()))) // != expression is true, if one and only one of the two pointers is !0
1140 aSetBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::TOP
, false );
1141 aSetBox
.SetLine( nullptr, SvxBoxItemLine::TOP
);
1149 if (aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::LEFT
))
1153 aSetBox
.SetLine( rBox
.GetLeft(), SvxBoxItemLine::LEFT
);
1155 else if ((aSetBox
.GetLeft() && rBox
.GetLeft() &&
1156 (*aSetBox
.GetLeft() != *rBox
.GetLeft())) ||
1157 ((!aSetBox
.GetLeft()) != (!rBox
.GetLeft())))
1159 aSetBoxInfo
.SetValid(SvxBoxInfoItemValidFlags::LEFT
, false );
1160 aSetBox
.SetLine( nullptr, SvxBoxItemLine::LEFT
);
1166 if (aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::VERT
))
1170 aSetBoxInfo
.SetLine( rBox
.GetLeft(), SvxBoxInfoItemLine::VERT
);
1172 else if ((aSetBoxInfo
.GetVert() && rBox
.GetLeft() &&
1173 (*aSetBoxInfo
.GetVert() != *rBox
.GetLeft())) ||
1174 ((!aSetBoxInfo
.GetVert()) != (!rBox
.GetLeft())))
1175 { aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::VERT
, false );
1176 aSetBoxInfo
.SetLine( nullptr, SvxBoxInfoItemLine::VERT
);
1182 if ( aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::RIGHT
) && bRightOver
)
1186 aSetBox
.SetLine( rBox
.GetRight(), SvxBoxItemLine::RIGHT
);
1188 else if ((aSetBox
.GetRight() && rBox
.GetRight() &&
1189 (*aSetBox
.GetRight() != *rBox
.GetRight())) ||
1190 (!aSetBox
.GetRight() != !rBox
.GetRight()))
1191 { aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::RIGHT
, false );
1192 aSetBox
.SetLine( nullptr, SvxBoxItemLine::RIGHT
);
1197 if ( bLast
&& bBottomOver
)
1199 if ( aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::BOTTOM
) )
1202 { bBottomSet
= true;
1203 aSetBox
.SetLine( rBox
.GetBottom(), SvxBoxItemLine::BOTTOM
);
1205 else if ((aSetBox
.GetBottom() && rBox
.GetBottom() &&
1206 (*aSetBox
.GetBottom() != *rBox
.GetBottom())) ||
1207 (!aSetBox
.GetBottom() != !rBox
.GetBottom()))
1208 { aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::BOTTOM
, false );
1209 aSetBox
.SetLine( nullptr, SvxBoxItemLine::BOTTOM
);
1213 // In all Lines, except for the last one, the horizontal Line
1214 // is taken from the Bottom Line.
1217 if (aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::HORI
))
1221 aSetBoxInfo
.SetLine( rBox
.GetBottom(), SvxBoxInfoItemLine::HORI
);
1223 else if ((aSetBoxInfo
.GetHori() && rBox
.GetBottom() &&
1224 (*aSetBoxInfo
.GetHori() != *rBox
.GetBottom())) ||
1225 ((!aSetBoxInfo
.GetHori()) != (!rBox
.GetBottom())))
1227 aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::HORI
, false );
1228 aSetBoxInfo
.SetLine( nullptr, SvxBoxInfoItemLine::HORI
);
1234 if (aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::DISTANCE
))
1236 if( !bDistanceSet
) // Set on first iteration
1238 bDistanceSet
= true;
1239 for( SvxBoxItemLine k
: o3tl::enumrange
<SvxBoxItemLine
>() )
1240 aSetBox
.SetDistance( rBox
.GetDistance( k
), k
);
1244 for( SvxBoxItemLine k
: o3tl::enumrange
<SvxBoxItemLine
>() )
1245 if( aSetBox
.GetDistance( k
) !=
1246 rBox
.GetDistance( k
) )
1248 aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::DISTANCE
, false );
1249 aSetBox
.SetAllDistances(0);
1257 // fdo#62470 fix the reading for table format.
1260 SvxBoxItem
aTempBox ( rSet
.Get(RES_BOX
) );
1261 SvxBoxInfoItem
aTempBoxInfo( rSet
.Get(SID_ATTR_BORDER_INNER
) );
1263 aTempBox
.SetLine( aSetBox
.GetRight(), SvxBoxItemLine::RIGHT
);
1264 aSetBox
.SetLine( aSetBox
.GetLeft(), SvxBoxItemLine::RIGHT
);
1265 aSetBox
.SetLine( aTempBox
.GetRight(), SvxBoxItemLine::LEFT
);
1267 aTempBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::LEFT
, aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::LEFT
) );
1268 aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::LEFT
, aSetBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::RIGHT
) );
1269 aSetBoxInfo
.SetValid( SvxBoxInfoItemValidFlags::RIGHT
, aTempBoxInfo
.IsValid(SvxBoxInfoItemValidFlags::LEFT
) );
1272 rSet
.Put( aSetBox
);
1273 rSet
.Put( aSetBoxInfo
);
1276 void SwDoc::SetBoxAttr( const SwCursor
& rCursor
, const SfxPoolItem
&rNew
)
1278 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
1280 if( !(pTableNd
&& ::lcl_GetBoxSel( rCursor
, aBoxes
, true )) )
1283 SwTable
& rTable
= pTableNd
->GetTable();
1284 if (GetIDocumentUndoRedo().DoesUndo())
1286 GetIDocumentUndoRedo().AppendUndo( std::make_unique
<SwUndoAttrTable
>(*pTableNd
) );
1289 std::vector
<std::unique_ptr
<SwTableFormatCmp
>> aFormatCmp
;
1290 aFormatCmp
.reserve(std::max
<size_t>(255, aBoxes
.size()));
1291 for (size_t i
= 0; i
< aBoxes
.size(); ++i
)
1293 SwTableBox
*pBox
= aBoxes
[i
];
1295 SwFrameFormat
*pNewFormat
= SwTableFormatCmp::FindNewFormat( aFormatCmp
, pBox
->GetFrameFormat(), 0 );
1296 if ( nullptr != pNewFormat
)
1297 pBox
->ChgFrameFormat( static_cast<SwTableBoxFormat
*>(pNewFormat
) );
1300 SwFrameFormat
*pOld
= pBox
->GetFrameFormat();
1301 SwFrameFormat
*pNew
= pBox
->ClaimFrameFormat();
1302 pNew
->SetFormatAttr( rNew
);
1303 aFormatCmp
.push_back(std::make_unique
<SwTableFormatCmp
>(pOld
, pNew
, 0));
1306 pBox
->SetDirectFormatting(true);
1309 SwHTMLTableLayout
*pTableLayout
= rTable
.GetHTMLTableLayout();
1312 SwContentFrame
* pFrame
= rCursor
.GetPointContentNode()->getLayoutFrame( rCursor
.GetPointContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
1313 SwTabFrame
* pTabFrame
= pFrame
->ImplFindTabFrame();
1315 pTableLayout
->Resize(
1316 pTableLayout
->GetBrowseWidthByTabFrame( *pTabFrame
), true );
1318 getIDocumentState().SetModified();
1321 bool SwDoc::GetBoxAttr( const SwCursor
& rCursor
, std::unique_ptr
<SfxPoolItem
>& rToFill
)
1323 // tdf#144843 calling GetBoxAttr *requires* object
1324 assert(rToFill
&& "requires object here");
1326 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
1328 if( pTableNd
&& lcl_GetBoxSel( rCursor
, aBoxes
))
1331 bool bOneFound
= false;
1332 const sal_uInt16 nWhich
= rToFill
->Which();
1333 for (size_t i
= 0; i
< aBoxes
.size(); ++i
)
1337 case RES_BACKGROUND
:
1339 std::unique_ptr
<SvxBrushItem
> xBack
=
1340 aBoxes
[i
]->GetFrameFormat()->makeBackgroundBrushItem();
1343 rToFill
= std::move(xBack
);
1346 else if( *rToFill
!= *xBack
)
1353 const SvxFrameDirectionItem
& rDir
=
1354 aBoxes
[i
]->GetFrameFormat()->GetFrameDir();
1357 rToFill
.reset(rDir
.Clone());
1360 else if( rToFill
&& *rToFill
!= rDir
)
1364 case RES_VERT_ORIENT
:
1366 const SwFormatVertOrient
& rOrient
=
1367 aBoxes
[i
]->GetFrameFormat()->GetVertOrient();
1370 rToFill
.reset(rOrient
.Clone());
1373 else if( rToFill
&& *rToFill
!= rOrient
)
1386 void SwDoc::SetBoxAlign( const SwCursor
& rCursor
, sal_uInt16 nAlign
)
1388 OSL_ENSURE( nAlign
== text::VertOrientation::NONE
||
1389 nAlign
== text::VertOrientation::CENTER
||
1390 nAlign
== text::VertOrientation::BOTTOM
, "Wrong alignment" );
1391 SwFormatVertOrient
aVertOri( 0, nAlign
);
1392 SetBoxAttr( rCursor
, aVertOri
);
1395 sal_uInt16
SwDoc::GetBoxAlign( const SwCursor
& rCursor
)
1397 sal_uInt16 nAlign
= USHRT_MAX
;
1398 SwTableNode
* pTableNd
= rCursor
.GetPoint()->GetNode().FindTableNode();
1400 if( pTableNd
&& ::lcl_GetBoxSel( rCursor
, aBoxes
))
1402 for (size_t i
= 0; i
< aBoxes
.size(); ++i
)
1404 const SwFormatVertOrient
&rOri
=
1405 aBoxes
[i
]->GetFrameFormat()->GetVertOrient();
1406 if( USHRT_MAX
== nAlign
)
1407 nAlign
= o3tl::narrowing
<sal_uInt16
>(rOri
.GetVertOrient());
1408 else if( rOri
.GetVertOrient() != nAlign
)
1418 static sal_uInt16
lcl_CalcCellFit( const SwLayoutFrame
*pCell
)
1421 const SwFrame
*pFrame
= pCell
->Lower(); // The whole Line
1422 SwRectFnSet
aRectFnSet(pCell
);
1425 const SwTwips nAdd
= aRectFnSet
.GetWidth(pFrame
->getFrameArea()) -
1426 aRectFnSet
.GetWidth(pFrame
->getFramePrintArea());
1428 // pFrame does not necessarily have to be a SwTextFrame!
1429 const SwTwips nCalcFitToContent
= pFrame
->IsTextFrame() ?
1430 const_cast<SwTextFrame
*>(static_cast<const SwTextFrame
*>(pFrame
))->CalcFitToContent() :
1431 aRectFnSet
.GetWidth(pFrame
->getFramePrintArea());
1433 nRet
= std::max( nRet
, nCalcFitToContent
+ nAdd
);
1434 pFrame
= pFrame
->GetNext();
1436 // Surrounding border as well as left and Right Border also need to be respected
1437 nRet
+= aRectFnSet
.GetWidth(pCell
->getFrameArea()) -
1438 aRectFnSet
.GetWidth(pCell
->getFramePrintArea());
1440 // To compensate for the accuracy of calculation later on in SwTable::SetTabCols
1441 // we keep adding up a little.
1443 return o3tl::narrowing
<sal_uInt16
>(std::max( SwTwips(MINLAY
), nRet
));
1446 /* The Line is within the Selection but not outlined by the TabCols.
1448 * That means that the Line has been "split" by other Cells due to the
1449 * two-dimensional representation used. Thus, we have to distribute the cell's
1450 * default or minimum value amongst the Cell it has been split by.
1452 * First, we collect the Columns (not the Column separators) which overlap
1453 * with the Cell. We then distribute the desired value according to the
1454 * amount of overlapping amongst the Cells.
1456 * A Cell's default value stays the same if it already has a larger value than
1457 * the desired one. It's overwritten if it's smaller.
1459 static void lcl_CalcSubColValues( std::vector
<sal_uInt16
> &rToFill
, const SwTabCols
&rCols
,
1460 const SwLayoutFrame
*pCell
, const SwLayoutFrame
*pTab
,
1463 const sal_uInt16 nWish
= bWishValues
?
1464 ::lcl_CalcCellFit( pCell
) :
1465 MINLAY
+ sal_uInt16(pCell
->getFrameArea().Width() - pCell
->getFramePrintArea().Width());
1467 SwRectFnSet
aRectFnSet(pTab
);
1469 for ( size_t i
= 0 ; i
<= rCols
.Count(); ++i
)
1471 tools::Long nColLeft
= i
== 0 ? rCols
.GetLeft() : rCols
[i
-1];
1472 tools::Long nColRight
= i
== rCols
.Count() ? rCols
.GetRight() : rCols
[i
];
1473 nColLeft
+= rCols
.GetLeftMin();
1474 nColRight
+= rCols
.GetLeftMin();
1476 // Adapt values to the proportions of the Table (Follows)
1477 if ( rCols
.GetLeftMin() != aRectFnSet
.GetLeft(pTab
->getFrameArea()) )
1479 const tools::Long nDiff
= aRectFnSet
.GetLeft(pTab
->getFrameArea()) - rCols
.GetLeftMin();
1483 const tools::Long nCellLeft
= aRectFnSet
.GetLeft(pCell
->getFrameArea());
1484 const tools::Long nCellRight
= aRectFnSet
.GetRight(pCell
->getFrameArea());
1486 // Calculate overlapping value
1487 tools::Long nWidth
= 0;
1488 if ( nColLeft
<= nCellLeft
&& nColRight
>= (nCellLeft
+COLFUZZY
) )
1489 nWidth
= nColRight
- nCellLeft
;
1490 else if ( nColLeft
<= (nCellRight
-COLFUZZY
) && nColRight
>= nCellRight
)
1491 nWidth
= nCellRight
- nColLeft
;
1492 else if ( nColLeft
>= nCellLeft
&& nColRight
<= nCellRight
)
1493 nWidth
= nColRight
- nColLeft
;
1494 if ( nWidth
&& pCell
->getFrameArea().Width() )
1496 tools::Long nTmp
= nWidth
* nWish
/ pCell
->getFrameArea().Width();
1497 if ( o3tl::make_unsigned(nTmp
) > rToFill
[i
] )
1498 rToFill
[i
] = sal_uInt16(nTmp
);
1504 * Retrieves new values to set the TabCols.
1506 * We do not iterate over the TabCols' entries, but over the gaps that describe Cells.
1507 * We set TabCol entries for which we did not calculate Cells to 0.
1509 * @param bWishValues == true: We calculate the desired value of all affected
1510 * Cells for the current Selection/current Cell.
1511 * If more Cells are within a Column, the highest
1512 * desired value is returned.
1513 * We set TabCol entries for which we did not calculate
1516 * @param bWishValues == false: The Selection is expanded vertically.
1517 * We calculate the minimum value for every
1518 * Column in the TabCols that intersects with the
1521 static void lcl_CalcColValues( std::vector
<sal_uInt16
> &rToFill
, const SwTabCols
&rCols
,
1522 const SwLayoutFrame
*pStart
, const SwLayoutFrame
*pEnd
,
1525 SwSelUnions aUnions
;
1526 ::MakeSelUnions( aUnions
, pStart
, pEnd
,
1527 bWishValues
? SwTableSearchType::NONE
: SwTableSearchType::Col
);
1529 for ( auto &rU
: aUnions
)
1531 SwSelUnion
*pSelUnion
= &rU
;
1532 const SwTabFrame
*pTab
= pSelUnion
->GetTable();
1533 const SwRect
&rUnion
= pSelUnion
->GetUnion();
1535 SwRectFnSet
aRectFnSet(pTab
);
1536 bool bRTL
= pTab
->IsRightToLeft();
1538 const SwLayoutFrame
*pCell
= pTab
->FirstCell();
1543 if ( pCell
->IsCellFrame() && pCell
->FindTabFrame() == pTab
&& ::IsFrameInTableSel( rUnion
, pCell
) )
1545 const tools::Long nCLeft
= aRectFnSet
.GetLeft(pCell
->getFrameArea());
1546 const tools::Long nCRight
= aRectFnSet
.GetRight(pCell
->getFrameArea());
1548 bool bNotInCols
= true;
1550 for ( size_t i
= 0; i
<= rCols
.Count(); ++i
)
1552 sal_uInt16 nFit
= rToFill
[i
];
1553 tools::Long nColLeft
= i
== 0 ? rCols
.GetLeft() : rCols
[i
-1];
1554 tools::Long nColRight
= i
== rCols
.Count() ? rCols
.GetRight() : rCols
[i
];
1558 tools::Long nTmpRight
= nColRight
;
1559 nColRight
= rCols
.GetRight() - nColLeft
;
1560 nColLeft
= rCols
.GetRight() - nTmpRight
;
1563 nColLeft
+= rCols
.GetLeftMin();
1564 nColRight
+= rCols
.GetLeftMin();
1566 // Adapt values to the proportions of the Table (Follows)
1567 tools::Long nLeftA
= nColLeft
;
1568 tools::Long nRightA
= nColRight
;
1569 if ( rCols
.GetLeftMin() != sal_uInt16(aRectFnSet
.GetLeft(pTab
->getFrameArea())) )
1571 const tools::Long nDiff
= aRectFnSet
.GetLeft(pTab
->getFrameArea()) - rCols
.GetLeftMin();
1576 // We don't want to take a too close look
1577 if ( ::IsSame(nCLeft
, nLeftA
) && ::IsSame(nCRight
, nRightA
))
1582 const sal_uInt16 nWish
= ::lcl_CalcCellFit( pCell
);
1587 { const sal_uInt16 nMin
= MINLAY
+ sal_uInt16(pCell
->getFrameArea().Width() -
1588 pCell
->getFramePrintArea().Width());
1589 if ( !nFit
|| nMin
< nFit
)
1592 if ( rToFill
[i
] < nFit
)
1597 ::lcl_CalcSubColValues( rToFill
, rCols
, pCell
, pTab
, bWishValues
);
1600 pCell
= pCell
->GetNextLayoutLeaf();
1601 } while( pCell
&& pCell
->getFrameArea().Width() == 0 );
1602 } while ( pCell
&& pTab
->IsAnLower( pCell
) );
1606 void SwDoc::AdjustCellWidth( const SwCursor
& rCursor
,
1607 const bool bBalance
,
1608 const bool bNoShrink
)
1610 // Check whether the current Cursor has it's Point/Mark in a Table
1611 SwContentNode
* pCntNd
= rCursor
.GetPoint()->GetNode().GetContentNode();
1612 SwTableNode
* pTableNd
= pCntNd
? pCntNd
->FindTableNode() : nullptr;
1616 SwLayoutFrame
*pStart
, *pEnd
;
1617 ::lcl_GetStartEndCell( rCursor
, pStart
, pEnd
);
1619 // Collect TabCols; we reset the Table with them
1620 SwFrame
* pBoxFrame
= pStart
;
1621 while( pBoxFrame
&& !pBoxFrame
->IsCellFrame() )
1622 pBoxFrame
= pBoxFrame
->GetUpper();
1628 GetTabCols( aTabCols
, static_cast<SwCellFrame
*>(pBoxFrame
) );
1630 if ( ! aTabCols
.Count() )
1633 std::vector
<sal_uInt16
> aWish(aTabCols
.Count() + 1);
1634 std::vector
<sal_uInt16
> aMins(aTabCols
.Count() + 1);
1636 ::lcl_CalcColValues( aWish
, aTabCols
, pStart
, pEnd
, /*bWishValues=*/true );
1638 // It's more robust if we calculate the minimum values for the whole Table
1639 const SwTabFrame
*pTab
= pStart
->ImplFindTabFrame();
1640 pStart
= const_cast<SwLayoutFrame
*>(static_cast<SwLayoutFrame
const *>(pTab
->FirstCell()));
1641 pEnd
= const_cast<SwLayoutFrame
*>(pTab
->FindLastContentOrTable()->GetUpper());
1642 while( !pEnd
->IsCellFrame() )
1643 pEnd
= pEnd
->GetUpper();
1644 ::lcl_CalcColValues( aMins
, aTabCols
, pStart
, pEnd
, /*bWishValues=*/false );
1646 sal_uInt16 nSelectedWidth
= 0, nCols
= 0;
1647 float fTotalWish
= 0;
1648 if ( bBalance
|| bNoShrink
)
1650 // Find the combined size of the selected columns
1651 for ( size_t i
= 0; i
<= aTabCols
.Count(); ++i
)
1656 nSelectedWidth
+= aTabCols
[i
] - aTabCols
.GetLeft();
1657 else if ( i
== aTabCols
.Count() )
1658 nSelectedWidth
+= aTabCols
.GetRight() - aTabCols
[i
-1];
1660 nSelectedWidth
+= aTabCols
[i
] - aTabCols
[i
-1];
1663 fTotalWish
+= aWish
[i
];
1665 // bBalance: Distribute the width evenly
1669 const sal_uInt16 nEqualWidth
= nCols
? nSelectedWidth
/ nCols
: 0;
1670 for (sal_uInt16
& rn
: aWish
)
1676 const tools::Long nOldRight
= aTabCols
.GetRight();
1678 // In order to make the implementation easier, but still use the available
1679 // space properly, we do this twice.
1681 // The problem: The first column is getting wider, the others get slimmer
1683 // The first column's desired width would be discarded as it would cause
1684 // the Table's width to exceed the maximum width.
1685 const tools::Long nMaxRight
= std::max(aTabCols
.GetRightMax(), nOldRight
);
1686 const sal_uInt16 nEqualWidth
= (nMaxRight
- aTabCols
.GetLeft()) / (aTabCols
.Count() + 1);
1687 const sal_Int16 nTablePadding
= nSelectedWidth
- fTotalWish
;
1688 for ( int k
= 0; k
< 2; ++k
)
1690 for ( size_t i
= 0; i
<= aTabCols
.Count(); ++i
)
1692 // bNoShrink: distribute excess space proportionately on pass 2.
1693 if ( bNoShrink
&& k
&& nTablePadding
> 0 && fTotalWish
> 0 )
1694 aWish
[i
] += round( aWish
[i
] / fTotalWish
* nTablePadding
);
1696 // First pass is primarily a shrink pass. Give all columns a chance
1697 // to grow by requesting the maximum width as "balanced".
1698 // Second pass is a first-come, first-served chance to max out.
1699 int nDiff
= k
? aWish
[i
] : std::min(aWish
[i
], nEqualWidth
);
1702 int nMin
= aMins
[i
];
1708 if( aTabCols
.Count() )
1709 nDiff
-= aTabCols
[0] - aTabCols
.GetLeft();
1711 nDiff
-= aTabCols
.GetRight() - aTabCols
.GetLeft();
1713 else if ( i
== aTabCols
.Count() )
1714 nDiff
-= aTabCols
.GetRight() - aTabCols
[i
-1];
1716 nDiff
-= aTabCols
[i
] - aTabCols
[i
-1];
1718 tools::Long nTabRight
= aTabCols
.GetRight() + nDiff
;
1720 // If the Table would become (or is already) too wide,
1721 // restrict the column growth to the allowed maximum.
1722 if (!bBalance
&& nTabRight
> nMaxRight
)
1724 const tools::Long nTmpD
= nTabRight
- nMaxRight
;
1729 // all the remaining columns need to be shifted by the same amount
1730 for ( size_t i2
= i
; i2
< aTabCols
.Count(); ++i2
)
1731 aTabCols
[i2
] += nDiff
;
1732 aTabCols
.SetRight( nTabRight
);
1737 const tools::Long nNewRight
= aTabCols
.GetRight();
1739 SwFrameFormat
*pFormat
= pTableNd
->GetTable().GetFrameFormat();
1740 const sal_Int16 nOriHori
= pFormat
->GetHoriOrient().GetHoriOrient();
1742 // We can leave the "real" work to the SwTable now
1743 SetTabCols( aTabCols
, false, static_cast<SwCellFrame
*>(pBoxFrame
) );
1745 // Alignment might have been changed in SetTabCols; restore old value
1746 const SwFormatHoriOrient
&rHori
= pFormat
->GetHoriOrient();
1747 SwFormatHoriOrient
aHori( rHori
);
1748 if ( aHori
.GetHoriOrient() != nOriHori
)
1750 aHori
.SetHoriOrient( nOriHori
);
1751 pFormat
->SetFormatAttr( aHori
);
1754 // We switch to left-adjusted for automatic width
1755 // We adjust the right border for Border attributes
1756 if( !bBalance
&& nNewRight
< nOldRight
)
1758 if( aHori
.GetHoriOrient() == text::HoriOrientation::FULL
)
1760 aHori
.SetHoriOrient( text::HoriOrientation::LEFT
);
1761 pFormat
->SetFormatAttr( aHori
);
1765 getIDocumentState().SetModified();
1768 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */