android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / docnode / ndtbl1.cxx
blob49b93463a429f90423a1a3265c4f495b0b49ee08
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
21 #include <editeng/boxitem.hxx>
22 #include <editeng/brushitem.hxx>
23 #include <editeng/frmdiritem.hxx>
24 #include <fesh.hxx>
25 #include <fmtornt.hxx>
26 #include <fmtfsize.hxx>
27 #include <fmtrowsplt.hxx>
28 #include <tabcol.hxx>
29 #include <frmatr.hxx>
30 #include <cellfrm.hxx>
31 #include <tabfrm.hxx>
32 #include <cntfrm.hxx>
33 #include <txtfrm.hxx>
34 #include <svx/svxids.hrc>
35 #include <doc.hxx>
36 #include <IDocumentUndoRedo.hxx>
37 #include <IDocumentState.hxx>
38 #include <IDocumentContentOperations.hxx>
39 #include <IDocumentRedlineAccess.hxx>
40 #include <IDocumentLayoutAccess.hxx>
41 #include <pam.hxx>
42 #include <swcrsr.hxx>
43 #include <viscrs.hxx>
44 #include <swtable.hxx>
45 #include <htmltbl.hxx>
46 #include <tblsel.hxx>
47 #include <swtblfmt.hxx>
48 #include <ndindex.hxx>
49 #include <undobj.hxx>
50 #include <calbck.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
61 #define COLFUZZY 20L
63 static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; }
65 namespace {
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
70 // in my own dtor.
71 class SwTableFormatCmp : public SwClient
73 public:
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);
80 private:
81 SwFrameFormat *m_pOld, *m_pNew;
82 sal_Int16 m_nType;
87 SwTableFormatCmp::SwTableFormatCmp(SwFrameFormat* pO, SwFrameFormat* pN, sal_Int16 nT)
88 : m_pOld(pO)
89 , m_pNew(pN)
90 , m_nType(nT)
92 if (m_pOld)
93 m_pOld->Add(this);
96 SwTableFormatCmp::~SwTableFormatCmp()
98 if (m_pOld)
100 m_pOld->Remove(this);
101 if (!m_pOld->HasWriterListeners())
102 delete m_pOld;
106 // static
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)
113 return pCmp->m_pNew;
115 return nullptr;
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);
126 if( pShCursor )
128 aPtPos = pShCursor->GetPtPos();
129 aMkPos = pShCursor->GetMkPos();
132 // Robust:
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;
138 tmp.first = aMkPos;
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);
150 if( pTableCursor )
151 ::GetTableSelCrs( *pTableCursor, rBoxes );
152 else
154 const SwPaM *pCurPam = &rCursor, *pSttPam = pCurPam;
155 do {
156 const SwNode* pNd = pCurPam->GetPointNode().FindTableBoxStartNode();
157 if( pNd )
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;
179 while ( pTmp )
181 if ( pTmp == pLine )
182 return true;
183 pTmp = pTmp->GetUpper() ? pTmp->GetUpper()->GetUpper() : nullptr;
185 return false;
188 namespace {
190 struct LinesAndTable
192 std::vector<SwTableLine*> &m_rLines;
193 const SwTable &m_rTable;
194 bool m_bInsertLines;
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);
224 else
225 pPara->m_bInsertLines = false;
228 else if (rBox.GetBox())
230 ::InsertLine(pPara->m_rLines, rBox.GetBox()->GetUpper());
232 return true;
235 bool FindLine_( FndLine_& rLine, LinesAndTable* pPara )
237 for (auto const& it : rLine.GetBoxes())
239 FindBox_(*it, pPara);
241 return true;
244 static void lcl_CollectLines( std::vector<SwTableLine*> &rArr, const SwCursor& rCursor, bool bRemoveLines )
246 // Collect the selected Boxes first
247 SwSelBoxes aBoxes;
248 if( !::lcl_GetBoxSel( rCursor, aBoxes ))
249 return ;
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)
265 if ( !bRemoveLines )
266 return;
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 );
276 if ( k <= i )
277 --i;
278 --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) );
290 else
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();
327 if( !pTableNd )
328 return;
330 std::vector<SwTableLine*> aRowArr; // For Lines collecting
331 ::lcl_CollectLines( aRowArr, rCursor, false );
333 if( aRowArr.empty() )
334 return;
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();
353 if( !pTableNd )
354 return nullptr;
356 std::vector<SwTableLine*> aRowArr; // For Lines collecting
357 ::lcl_CollectLines( aRowArr, rCursor, false );
359 if( aRowArr.empty() )
360 return nullptr;
362 SwFormatRowSplit* pSz = &const_cast<SwFormatRowSplit&>(aRowArr[0]->GetFrameFormat()->GetRowSplit());
364 for ( auto pLn : aRowArr )
366 if ( pSz->GetValue() != pLn->GetFrameFormat()->GetRowSplit().GetValue() )
368 return nullptr;
371 return std::make_unique<SwFormatRowSplit>( *pSz );
374 /* Class: SwDoc
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();
392 if( !pTableNd )
393 return;
395 std::vector<SwTableLine*> aRowArr; // For Lines collecting
396 ::lcl_CollectLines( aRowArr, rCursor, true );
398 if( aRowArr.empty() )
399 return;
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();
417 if( !pTableNd )
418 return nullptr;
420 std::vector<SwTableLine*> aRowArr; // For Lines collecting
421 ::lcl_CollectLines( aRowArr, rCursor, true );
423 if( aRowArr.empty() )
424 return nullptr;
426 SwFormatFrameSize* pSz = &const_cast<SwFormatFrameSize&>(aRowArr[0]->GetFrameFormat()->GetFrameSize());
428 for ( auto pLn : aRowArr )
430 if ( *pSz != pLn->GetFrameFormat()->GetFrameSize() )
431 return nullptr;
433 return std::make_unique<SwFormatFrameSize>( *pSz );
436 bool SwDoc::BalanceRowHeight( const SwCursor& rCursor, bool bTstOnly, const bool bOptimize )
438 bool bRet = false;
439 SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
440 if( pTableNd )
442 std::vector<SwTableLine*> aRowArr; // For Lines collecting
443 ::lcl_CollectLines( aRowArr, rCursor, true );
445 if( 1 < aRowArr.size() )
447 if( !bTstOnly )
449 tools::Long nHeight = 0;
450 sal_Int32 nTotalHeight = 0;
451 for ( auto pLn : aRowArr )
453 if (bOptimize)
454 nHeight = 0;
455 SwIterator<SwFrame,SwFormat> aIter( *pLn->GetFrameFormat() );
456 SwFrame* pFrame = aIter.First();
457 while ( pFrame )
459 nHeight = std::max( nHeight, pFrame->getFrameArea().Height() );
460 pFrame = aIter.Next();
462 nTotalHeight += nHeight;
465 if ( bOptimize )
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();
483 bRet = true;
486 return bRet;
489 void SwDoc::SetRowBackground( const SwCursor& rCursor, const SvxBrushItem &rNew )
491 SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
492 if( !pTableNd )
493 return;
495 std::vector<SwTableLine*> aRowArr; // For Lines collecting
496 ::lcl_CollectLines( aRowArr, rCursor, true );
498 if( aRowArr.empty() )
499 return;
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 )
517 bool bRet = false;
518 SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
519 if( pTableNd )
521 std::vector<SwTableLine*> aRowArr; // For Lines collecting
522 ::lcl_CollectLines( aRowArr, rCursor, true );
524 if( !aRowArr.empty() )
526 rToFill = aRowArr[0]->GetFrameFormat()->makeBackgroundBrushItem();
528 bRet = true;
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 )
535 bRet = false;
536 break;
541 return bRet;
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();
548 if( !pTableNd )
549 return false;
551 std::vector<SwTableLine*> aRowArr; // For Lines collecting
552 ::lcl_CollectLines( aRowArr, rCursor, true );
554 if( aRowArr.empty() )
555 return false;
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
566 return true;
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() )
575 return true;
578 return false;
581 void SwDoc::SetRowNotTracked( const SwCursor& rCursor,
582 const SvxPrintItem &rNew, bool bAll, bool bIns )
584 SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
585 if( !pTableNd )
586 return;
588 std::vector<SwTableLine*> aRowArr; // For Lines collecting
589 if ( bAll )
591 const SwTableLines &rLines = pTableNd->GetTable().GetTabLines();
592 aRowArr.insert(aRowArr.end(), rLines.begin(), rLines.end());
594 else
595 ::lcl_CollectLines( aRowArr, rCursor, true );
597 if( aRowArr.empty() )
598 return;
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)
607 !rNew.GetValue();
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;
618 if ( bInsertDummy )
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);
647 SwPaM aPaM(aInsPos);
648 getIDocumentContentOperations().InsertString( aPaM,
649 OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
650 aPaM.SetMark();
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,
666 SwTabFrame *pTab )
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 ) );
684 pCell = 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;
692 if( !pTableNd )
693 return ;
695 SwLayoutFrame *pStart, *pEnd;
696 ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
698 SwSelUnions aUnions;
699 ::MakeSelUnions( aUnions, pStart, pEnd );
701 if( aUnions.empty() )
702 return;
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 );
727 if( pSetBoxInfo )
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 );
743 if( pSetBox )
745 pLeft = pSetBox->GetLeft();
746 pRight = pSetBox->GetRight();
747 pTop = pSetBox->GetTop();
748 pBottom = pSetBox->GetBottom();
750 else
752 // Not set, thus not valid values
753 bTopValid = bBottomValid = bLeftValid = bRightValid = false;
754 pSetBox = nullptr;
757 bool bFirst = true;
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;
782 if ( bVert )
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();
789 else
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();
797 if ( bRTL )
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() ) )
805 continue;
807 SvxBoxItem aBox( pCell->GetFormat()->GetBox() );
809 sal_Int16 nType = 0;
811 // Top Border
812 if( bTopValid )
814 if ( bFirst && bTopOver )
816 aBox.SetLine( pTop, SvxBoxItemLine::TOP );
817 nType |= 0x0001;
819 else if ( bHoriValid )
821 aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
822 nType |= 0x0002;
826 // Fix fdo#62470 correct the input for RTL table
827 if (bRTL)
829 if( bLeftOver && bRightOver)
831 if ( bLeftValid )
833 aBox.SetLine( pLeft, SvxBoxItemLine::RIGHT );
834 nType |= 0x0010;
836 if ( bRightValid )
838 aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
839 nType |= 0x0004;
842 else
844 if ( bLeftValid )
846 aBox.SetLine( bRightOver ? pLeft : nullptr, SvxBoxItemLine::RIGHT );
847 if (bVertValid)
848 nType |= 0x0020;
849 else
850 nType |= 0x0010;
852 if ( bLeftOver )
854 if ( bRightValid )
856 aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
857 nType |= 0x0004;
860 else if ( bVertValid )
862 aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
863 nType |= 0x0008;
867 else
869 // Left Border
870 if ( bLeftOver )
872 if( bLeftValid )
874 aBox.SetLine( pLeft, SvxBoxItemLine::LEFT );
875 nType |= 0x0004;
878 else if( bVertValid )
880 aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
881 nType |= 0x0008;
884 // Right Border
885 if( bRightValid )
887 if ( bRightOver )
889 aBox.SetLine( pRight, SvxBoxItemLine::RIGHT );
890 nType |= 0x0010;
892 else if ( bVertValid )
894 aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
895 nType |= 0x0020;
900 // Bottom Border
901 if ( bLast && bBottomOver )
903 if( bBottomValid )
905 aBox.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
906 nType |= 0x0040;
909 else if( bHoriValid )
911 aBox.SetLine( pHori, SvxBoxItemLine::BOTTOM );
912 nType |= 0x0080;
915 if( pSetBox )
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) );
925 else
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));
934 bFirst = false;
937 SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
938 if( pTableLayout )
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)
953 if ( pBorderLine )
955 if ( !pColor )
957 Color aTmp( pToSet->GetColor() );
958 *pToSet = *pBorderLine;
959 pToSet->SetColor( aTmp );
961 else
962 *pToSet = *pBorderLine;
964 if ( pColor )
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;
974 if( !pTableNd )
975 return ;
977 SwLayoutFrame *pStart, *pEnd;
978 ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
980 SwSelUnions aUnions;
981 ::MakeSelUnions( aUnions, pStart, pEnd );
983 if( aUnions.empty() )
984 return;
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 ) )
1009 continue;
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);
1031 else
1033 if (pTop)
1034 ::lcl_SetLineStyle(pTop, pColor, pBorderLine);
1035 if (pBot)
1036 ::lcl_SetLineStyle(pBot, pColor, pBorderLine);
1037 if (pLeft)
1038 ::lcl_SetLineStyle(pLeft, pColor, pBorderLine);
1039 if (pRight)
1040 ::lcl_SetLineStyle(pRight, pColor, pBorderLine);
1042 pFormat->SetFormatAttr( *aBox );
1046 SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
1047 if( pTableLayout )
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;
1063 if( !pTableNd )
1064 return ;
1066 SwLayoutFrame *pStart, *pEnd;
1067 ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
1069 SwSelUnions aUnions;
1070 ::MakeSelUnions( aUnions, pStart, pEnd );
1072 if( aUnions.empty() )
1073 return;
1075 SvxBoxItem aSetBox ( rSet.Get(RES_BOX ) );
1076 SvxBoxInfoItem aSetBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
1078 bool bTopSet = false,
1079 bBottomSet = false,
1080 bLeftSet = false,
1081 bRightSet = false,
1082 bHoriSet = false,
1083 bVertSet = false,
1084 bDistanceSet = false,
1085 bRTLTab = 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;
1106 if ( bVert )
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();
1113 else
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();
1121 if ( bRTL )
1122 std::swap( bLeftOver, bRightOver );
1124 const SwFrameFormat *pFormat = pCell->GetFormat();
1125 const SvxBoxItem &rBox = pFormat->GetBox();
1127 // Top Border
1128 if ( bFirst && bTopOver )
1130 if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
1132 if ( !bTopSet )
1133 { bTopSet = true;
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 );
1146 // Left Border
1147 if ( bLeftOver )
1149 if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT))
1151 if ( !bLeftSet )
1152 { bLeftSet = true;
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 );
1164 else
1166 if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::VERT))
1168 if ( !bVertSet )
1169 { bVertSet = true;
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 );
1181 // Right Border
1182 if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) && bRightOver )
1184 if ( !bRightSet )
1185 { bRightSet = true;
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 );
1196 // Bottom Border
1197 if ( bLast && bBottomOver )
1199 if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
1201 if ( !bBottomSet )
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.
1215 else
1217 if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::HORI))
1219 if ( !bHoriSet )
1220 { bHoriSet = true;
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 );
1233 // Distance to text
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 );
1242 else
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);
1250 break;
1257 // fdo#62470 fix the reading for table format.
1258 if ( bRTLTab )
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();
1279 SwSelBoxes aBoxes;
1280 if( !(pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes, true )) )
1281 return;
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) );
1298 else
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();
1310 if( pTableLayout )
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");
1325 bool bRet = false;
1326 SwTableNode* pTableNd = rCursor.GetPoint()->GetNode().FindTableNode();
1327 SwSelBoxes aBoxes;
1328 if( pTableNd && lcl_GetBoxSel( rCursor, aBoxes ))
1330 bRet = true;
1331 bool bOneFound = false;
1332 const sal_uInt16 nWhich = rToFill->Which();
1333 for (size_t i = 0; i < aBoxes.size(); ++i)
1335 switch ( nWhich )
1337 case RES_BACKGROUND:
1339 std::unique_ptr<SvxBrushItem> xBack =
1340 aBoxes[i]->GetFrameFormat()->makeBackgroundBrushItem();
1341 if( !bOneFound )
1343 rToFill = std::move(xBack);
1344 bOneFound = true;
1346 else if( *rToFill != *xBack )
1347 bRet = false;
1349 break;
1351 case RES_FRAMEDIR:
1353 const SvxFrameDirectionItem& rDir =
1354 aBoxes[i]->GetFrameFormat()->GetFrameDir();
1355 if( !bOneFound )
1357 rToFill.reset(rDir.Clone());
1358 bOneFound = true;
1360 else if( rToFill && *rToFill != rDir )
1361 bRet = false;
1363 break;
1364 case RES_VERT_ORIENT:
1366 const SwFormatVertOrient& rOrient =
1367 aBoxes[i]->GetFrameFormat()->GetVertOrient();
1368 if( !bOneFound )
1370 rToFill.reset(rOrient.Clone());
1371 bOneFound = true;
1373 else if( rToFill && *rToFill != rOrient )
1374 bRet = false;
1376 break;
1379 if ( !bRet )
1380 break;
1383 return bRet;
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();
1399 SwSelBoxes aBoxes;
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 )
1410 nAlign = USHRT_MAX;
1411 break;
1415 return nAlign;
1418 static sal_uInt16 lcl_CalcCellFit( const SwLayoutFrame *pCell )
1420 SwTwips nRet = 0;
1421 const SwFrame *pFrame = pCell->Lower(); // The whole Line
1422 SwRectFnSet aRectFnSet(pCell);
1423 while ( pFrame )
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.
1442 nRet += COLFUZZY;
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,
1461 bool bWishValues )
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();
1480 nColLeft += nDiff;
1481 nColRight += nDiff;
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
1514 * Cells to 0.
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
1519 * Selection.
1521 static void lcl_CalcColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
1522 const SwLayoutFrame *pStart, const SwLayoutFrame *pEnd,
1523 bool bWishValues )
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();
1539 if (!pCell)
1540 continue;
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];
1556 if ( bRTL )
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();
1572 nLeftA += nDiff;
1573 nRightA += nDiff;
1576 // We don't want to take a too close look
1577 if ( ::IsSame(nCLeft, nLeftA) && ::IsSame(nCRight, nRightA))
1579 bNotInCols = false;
1580 if ( bWishValues )
1582 const sal_uInt16 nWish = ::lcl_CalcCellFit( pCell );
1583 if ( nWish > nFit )
1584 nFit = nWish;
1586 else
1587 { const sal_uInt16 nMin = MINLAY + sal_uInt16(pCell->getFrameArea().Width() -
1588 pCell->getFramePrintArea().Width());
1589 if ( !nFit || nMin < nFit )
1590 nFit = nMin;
1592 if ( rToFill[i] < nFit )
1593 rToFill[i] = nFit;
1596 if ( bNotInCols )
1597 ::lcl_CalcSubColValues( rToFill, rCols, pCell, pTab, bWishValues );
1599 do {
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;
1613 if( !pTableNd )
1614 return ;
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();
1624 if ( !pBoxFrame )
1625 return; // Robust
1627 SwTabCols aTabCols;
1628 GetTabCols( aTabCols, static_cast<SwCellFrame*>(pBoxFrame) );
1630 if ( ! aTabCols.Count() )
1631 return;
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 )
1653 if ( aWish[i] )
1655 if ( i == 0 )
1656 nSelectedWidth += aTabCols[i] - aTabCols.GetLeft();
1657 else if ( i == aTabCols.Count() )
1658 nSelectedWidth += aTabCols.GetRight() - aTabCols[i-1];
1659 else
1660 nSelectedWidth += aTabCols[i] - aTabCols[i-1];
1661 ++nCols;
1663 fTotalWish += aWish[i];
1665 // bBalance: Distribute the width evenly
1666 if (bBalance)
1668 assert(nCols);
1669 const sal_uInt16 nEqualWidth = nCols ? nSelectedWidth / nCols : 0;
1670 for (sal_uInt16 & rn : aWish)
1671 if (rn)
1672 rn = nEqualWidth;
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
1682 // only afterwards.
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);
1700 if ( nDiff )
1702 int nMin = aMins[i];
1703 if ( nMin > nDiff )
1704 nDiff = nMin;
1706 if ( i == 0 )
1708 if( aTabCols.Count() )
1709 nDiff -= aTabCols[0] - aTabCols.GetLeft();
1710 else
1711 nDiff -= aTabCols.GetRight() - aTabCols.GetLeft();
1713 else if ( i == aTabCols.Count() )
1714 nDiff -= aTabCols.GetRight() - aTabCols[i-1];
1715 else
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;
1725 nDiff -= nTmpD;
1726 nTabRight -= nTmpD;
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: */