1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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 <IDocumentLayoutAccess.hxx>
39 #include <pam.hxx>
40 #include <swcrsr.hxx>
41 #include <viscrs.hxx>
42 #include <swtable.hxx>
43 #include <htmltbl.hxx>
44 #include <tblsel.hxx>
45 #include <swtblfmt.hxx>
46 #include <ndindex.hxx>
47 #include <undobj.hxx>
48 #include <calbck.hxx>
49 #include <UndoTable.hxx>
50 #include <o3tl/enumrange.hxx>
51 
52 using ::editeng::SvxBorderLine;
53 using namespace ::com::sun::star;
54 
55 // See swtable.cxx too
56 #define COLFUZZY 20L
57 
IsSame(long nA,long nB)58 static bool IsSame( long nA, long nB ) { return  std::abs(nA-nB) <= COLFUZZY; }
59 
60 // SwTableLine::ChgFrameFormat may delete old format which doesn't have writer listeners anymore.
61 // This may invalidate my pointers, and lead to use-after-free. For this reason, I register myself
62 // as a writer listener for the old format here, and take care to delete formats without listeners
63 // in my own dtor.
64 class SwTableFormatCmp : public SwClient
65 {
66 public:
67     SwTableFormatCmp( SwFrameFormat *pOld, SwFrameFormat *pNew, sal_Int16 nType );
68     ~SwTableFormatCmp() override;
69 
70     static SwFrameFormat* FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
71                                         SwFrameFormat const* pOld, sal_Int16 nType);
72 
73 private:
74     SwFrameFormat *pOld, *pNew;
75     sal_Int16 const nType;
76 };
77 
SwTableFormatCmp(SwFrameFormat * pO,SwFrameFormat * pN,sal_Int16 nT)78 SwTableFormatCmp::SwTableFormatCmp( SwFrameFormat *pO, SwFrameFormat *pN, sal_Int16 nT )
79     : pOld ( pO ), pNew ( pN ), nType( nT )
80 {
81     if (pOld)
82         pOld->Add(this);
83 }
84 
~SwTableFormatCmp()85 SwTableFormatCmp::~SwTableFormatCmp()
86 {
87     if (pOld)
88     {
89         pOld->Remove(this);
90         if (!pOld->HasWriterListeners())
91             delete pOld;
92     }
93 }
94 
95 // static
FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>> & rArr,SwFrameFormat const * pOld,sal_Int16 nType)96 SwFrameFormat* SwTableFormatCmp::FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
97                                                SwFrameFormat const* pOld, sal_Int16 nType)
98 {
99     for (const auto& pCmp : rArr)
100     {
101         if ( pCmp->pOld == pOld && pCmp->nType == nType )
102             return pCmp->pNew;
103     }
104     return nullptr;
105 }
106 
lcl_GetStartEndCell(const SwCursor & rCursor,SwLayoutFrame * & prStart,SwLayoutFrame * & prEnd)107 static void lcl_GetStartEndCell( const SwCursor& rCursor,
108                         SwLayoutFrame *&prStart, SwLayoutFrame *&prEnd )
109 {
110     OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ),
111             "Tab selection not at ContentNode" );
112 
113     Point aPtPos, aMkPos;
114     const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
115     if( pShCursor )
116     {
117         aPtPos = pShCursor->GetPtPos();
118         aMkPos = pShCursor->GetMkPos();
119     }
120 
121     // Robust:
122     SwContentNode* pPointNd = rCursor.GetContentNode();
123     SwContentNode* pMarkNd  = rCursor.GetContentNode(false);
124 
125     std::pair<Point, bool> tmp(aPtPos, true);
126     SwFrame *const pPointFrame = pPointNd ? pPointNd->getLayoutFrame(pPointNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
127     tmp.first = aMkPos;
128     SwFrame *const pMarkFrame = pMarkNd ? pMarkNd->getLayoutFrame(pMarkNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
129 
130     prStart = pPointFrame ? pPointFrame->GetUpper() : nullptr;
131     prEnd   = pMarkFrame  ? pMarkFrame->GetUpper() : nullptr;
132 }
133 
lcl_GetBoxSel(const SwCursor & rCursor,SwSelBoxes & rBoxes,bool bAllCursor=false)134 static bool lcl_GetBoxSel( const SwCursor& rCursor, SwSelBoxes& rBoxes,
135                     bool bAllCursor = false )
136 {
137     const SwTableCursor* pTableCursor =
138         dynamic_cast<const SwTableCursor*>(&rCursor);
139     if( pTableCursor )
140         ::GetTableSelCrs( *pTableCursor, rBoxes );
141     else
142     {
143         const SwPaM *pCurPam = &rCursor, *pSttPam = pCurPam;
144         do {
145             const SwNode* pNd = pCurPam->GetNode().FindTableBoxStartNode();
146             if( pNd )
147             {
148                 SwTableBox* pBox = const_cast<SwTableBox*>(pNd->FindTableNode()->GetTable().
149                                             GetTableBox( pNd->GetIndex() ));
150                 rBoxes.insert( pBox );
151             }
152         } while( bAllCursor &&
153                 pSttPam != ( pCurPam = pCurPam->GetNext()) );
154     }
155     return !rBoxes.empty();
156 }
157 
InsertLine(std::vector<SwTableLine * > & rLineArr,SwTableLine * pLine)158 static void InsertLine( std::vector<SwTableLine*>& rLineArr, SwTableLine* pLine )
159 {
160     if( rLineArr.end() == std::find( rLineArr.begin(), rLineArr.end(), pLine ) )
161         rLineArr.push_back( pLine );
162 }
163 
lcl_IsAnLower(const SwTableLine * pLine,const SwTableLine * pAssumed)164 static bool lcl_IsAnLower( const SwTableLine *pLine, const SwTableLine *pAssumed )
165 {
166     const SwTableLine *pTmp = pAssumed->GetUpper() ?
167                                     pAssumed->GetUpper()->GetUpper() : nullptr;
168     while ( pTmp )
169     {
170         if ( pTmp == pLine )
171             return true;
172         pTmp = pTmp->GetUpper() ? pTmp->GetUpper()->GetUpper() : nullptr;
173     }
174     return false;
175 }
176 
177 struct LinesAndTable
178 {
179     std::vector<SwTableLine*> &m_rLines;
180     const SwTable             &m_rTable;
181     bool                      m_bInsertLines;
182 
LinesAndTableLinesAndTable183     LinesAndTable(std::vector<SwTableLine*> &rL, const SwTable &rTable) :
184           m_rLines(rL), m_rTable(rTable), m_bInsertLines(true) {}
185 };
186 
187 static bool FindLine_( FndLine_ & rLine, LinesAndTable* pPara );
188 
FindBox_(FndBox_ & rBox,LinesAndTable * pPara)189 static bool FindBox_( FndBox_ & rBox, LinesAndTable* pPara )
190 {
191     if (!rBox.GetLines().empty())
192     {
193         pPara->m_bInsertLines = true;
194         for (auto const& rpFndLine : rBox.GetLines())
195         {
196             FindLine_(*rpFndLine, pPara);
197         }
198 
199         if (pPara->m_bInsertLines)
200         {
201             const SwTableLines &rLines = (rBox.GetBox())
202                                     ? rBox.GetBox()->GetTabLines()
203                                     : pPara->m_rTable.GetTabLines();
204             if (rBox.GetLines().size() == rLines.size())
205             {
206                 for ( auto pLine : rLines )
207                     ::InsertLine(pPara->m_rLines, pLine);
208             }
209             else
210                 pPara->m_bInsertLines = false;
211         }
212     }
213     else if (rBox.GetBox())
214     {
215         ::InsertLine(pPara->m_rLines, rBox.GetBox()->GetUpper());
216     }
217     return true;
218 }
219 
FindLine_(FndLine_ & rLine,LinesAndTable * pPara)220 bool FindLine_( FndLine_& rLine, LinesAndTable* pPara )
221 {
222     for (auto const& it : rLine.GetBoxes())
223     {
224         FindBox_(*it, pPara);
225     }
226     return true;
227 }
228 
lcl_CollectLines(std::vector<SwTableLine * > & rArr,const SwCursor & rCursor,bool bRemoveLines)229 static void lcl_CollectLines( std::vector<SwTableLine*> &rArr, const SwCursor& rCursor, bool bRemoveLines )
230 {
231     // Collect the selected Boxes first
232     SwSelBoxes aBoxes;
233     if( !::lcl_GetBoxSel( rCursor, aBoxes ))
234         return ;
235 
236     // Copy the selected structure
237     const SwTable &rTable = aBoxes[0]->GetSttNd()->FindTableNode()->GetTable();
238     LinesAndTable aPara( rArr, rTable );
239     FndBox_ aFndBox( nullptr, nullptr );
240     {
241         FndPara aTmpPara( aBoxes, &aFndBox );
242         ForEach_FndLineCopyCol( const_cast<SwTableLines&>(rTable.GetTabLines()), &aTmpPara );
243     }
244 
245     // Collect the Lines which only contain selected Boxes
246     ::FindBox_(aFndBox, &aPara);
247 
248     // Remove lines, that have a common superordinate row.
249     // (Not for row split)
250     if ( bRemoveLines )
251     {
252         for ( std::vector<SwTableLine*>::size_type i = 0; i < rArr.size(); ++i )
253         {
254             SwTableLine *pUpLine = rArr[i];
255             for ( std::vector<SwTableLine*>::size_type k = 0; k < rArr.size(); ++k )
256             {
257                 if ( k != i && ::lcl_IsAnLower( pUpLine, rArr[k] ) )
258                 {
259                     rArr.erase( rArr.begin() + k );
260                     if ( k <= i )
261                         --i;
262                     --k;
263                 }
264             }
265         }
266     }
267 }
268 
lcl_ProcessRowAttr(std::vector<std::unique_ptr<SwTableFormatCmp>> & rFormatCmp,SwTableLine * pLine,const SfxPoolItem & rNew)269 static void lcl_ProcessRowAttr(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
270                                SwTableLine* pLine, const SfxPoolItem& rNew)
271 {
272     SwFrameFormat *pNewFormat;
273     if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( rFormatCmp, pLine->GetFrameFormat(), 0 )))
274         pLine->ChgFrameFormat( static_cast<SwTableLineFormat*>(pNewFormat) );
275     else
276     {
277         SwFrameFormat *pOld = pLine->GetFrameFormat();
278         SwFrameFormat *pNew = pLine->ClaimFrameFormat();
279         pNew->SetFormatAttr( rNew );
280         rFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
281     }
282 }
283 
284 static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
285                                SwTableBox* pBox, const SwFormatFrameSize& rNew);
286 
lcl_ProcessRowSize(std::vector<std::unique_ptr<SwTableFormatCmp>> & rFormatCmp,SwTableLine * pLine,const SwFormatFrameSize & rNew)287 static void lcl_ProcessRowSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
288                                SwTableLine* pLine, const SwFormatFrameSize& rNew)
289 {
290     lcl_ProcessRowAttr( rFormatCmp, pLine, rNew );
291     SwTableBoxes &rBoxes = pLine->GetTabBoxes();
292     for ( auto pBox : rBoxes )
293         ::lcl_ProcessBoxSize( rFormatCmp, pBox, rNew );
294 }
295 
lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>> & rFormatCmp,SwTableBox * pBox,const SwFormatFrameSize & rNew)296 static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
297                                SwTableBox* pBox, const SwFormatFrameSize& rNew)
298 {
299     SwTableLines &rLines = pBox->GetTabLines();
300     if ( !rLines.empty() )
301     {
302         SwFormatFrameSize aSz( rNew );
303         aSz.SetHeight( rNew.GetHeight() ? rNew.GetHeight() / rLines.size() : 0 );
304         for ( auto pLine : rLines )
305             ::lcl_ProcessRowSize( rFormatCmp, pLine, aSz );
306     }
307 }
308 
SetRowSplit(const SwCursor & rCursor,const SwFormatRowSplit & rNew)309 void SwDoc::SetRowSplit( const SwCursor& rCursor, const SwFormatRowSplit &rNew )
310 {
311     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
312     if( pTableNd )
313     {
314         std::vector<SwTableLine*> aRowArr; // For Lines collecting
315         ::lcl_CollectLines( aRowArr, rCursor, false );
316 
317         if( !aRowArr.empty() )
318         {
319             if (GetIDocumentUndoRedo().DoesUndo())
320             {
321                 GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
322             }
323 
324             std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
325             aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
326 
327             for( auto pLn : aRowArr )
328                 ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
329 
330             getIDocumentState().SetModified();
331         }
332     }
333 }
334 
GetRowSplit(const SwCursor & rCursor)335 std::unique_ptr<SwFormatRowSplit> SwDoc::GetRowSplit( const SwCursor& rCursor )
336 {
337     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
338     if( !pTableNd )
339         return nullptr;
340 
341     std::vector<SwTableLine*> aRowArr; // For Lines collecting
342     ::lcl_CollectLines( aRowArr, rCursor, false );
343 
344     if( aRowArr.empty() )
345         return nullptr;
346 
347     SwFormatRowSplit* pSz = &const_cast<SwFormatRowSplit&>(aRowArr[0]->GetFrameFormat()->GetRowSplit());
348 
349     for ( auto pLn : aRowArr )
350     {
351         if ( pSz->GetValue() != pLn->GetFrameFormat()->GetRowSplit().GetValue() )
352         {
353             return nullptr;
354         }
355     }
356     return std::make_unique<SwFormatRowSplit>( *pSz );
357 }
358 
359 /* Class:  SwDoc
360  * Methods:  SetRowHeight(), GetRowHeight()
361  *
362  * The line height is calculated from the Selection.
363  * Starting with every Cell within the Selection, all Cells are iterated
364  * through in an upwards fashion.
365  *
366  * The topmost Line gets the requested value, all Lines below it get
367  * a respective value that is calculated from the relation of the old and
368  * new size of the topmost Line in the lower line's own size.
369  *
370  * All changed Lines may get an own FrameFormat.
371  * Of course we can only touch every Line once.
372  */
373 
SetRowHeight(const SwCursor & rCursor,const SwFormatFrameSize & rNew)374 void SwDoc::SetRowHeight( const SwCursor& rCursor, const SwFormatFrameSize &rNew )
375 {
376     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
377     if( pTableNd )
378     {
379         std::vector<SwTableLine*> aRowArr; // For Lines collecting
380         ::lcl_CollectLines( aRowArr, rCursor, true );
381 
382         if( !aRowArr.empty() )
383         {
384             if (GetIDocumentUndoRedo().DoesUndo())
385             {
386                 GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
387             }
388 
389             std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
390             aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
391             for ( auto pLn : aRowArr )
392                 ::lcl_ProcessRowSize( aFormatCmp, pLn, rNew );
393 
394             getIDocumentState().SetModified();
395         }
396     }
397 }
398 
GetRowHeight(const SwCursor & rCursor)399 std::unique_ptr<SwFormatFrameSize> SwDoc::GetRowHeight( const SwCursor& rCursor )
400 {
401     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
402     if( !pTableNd )
403         return nullptr;
404 
405     std::vector<SwTableLine*> aRowArr; // For Lines collecting
406     ::lcl_CollectLines( aRowArr, rCursor, true );
407 
408     if( aRowArr.empty() )
409         return nullptr;
410 
411     SwFormatFrameSize* pSz = &const_cast<SwFormatFrameSize&>(aRowArr[0]->GetFrameFormat()->GetFrameSize());
412 
413     for ( auto pLn : aRowArr )
414     {
415         if ( *pSz != pLn->GetFrameFormat()->GetFrameSize() )
416             return nullptr;
417     }
418     return std::make_unique<SwFormatFrameSize>( *pSz );
419 }
420 
BalanceRowHeight(const SwCursor & rCursor,bool bTstOnly,const bool bOptimize)421 bool SwDoc::BalanceRowHeight( const SwCursor& rCursor, bool bTstOnly, const bool bOptimize )
422 {
423     bool bRet = false;
424     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
425     if( pTableNd )
426     {
427         std::vector<SwTableLine*> aRowArr; // For Lines collecting
428         ::lcl_CollectLines( aRowArr, rCursor, true );
429 
430         if( 1 < aRowArr.size() )
431         {
432             if( !bTstOnly )
433             {
434                 long nHeight = 0;
435                 sal_Int32 nTotalHeight = 0;
436                 for ( auto pLn : aRowArr )
437                 {
438                     SwIterator<SwFrame,SwFormat> aIter( *pLn->GetFrameFormat() );
439                     SwFrame* pFrame = aIter.First();
440                     while ( pFrame )
441                     {
442                         nHeight = std::max( nHeight, pFrame->getFrameArea().Height() );
443                         pFrame = aIter.Next();
444                     }
445                     nTotalHeight += nHeight;
446                 }
447 
448                 if ( bOptimize )
449                     nHeight = nTotalHeight / aRowArr.size();
450 
451                 SwFormatFrameSize aNew( ATT_MIN_SIZE, 0, nHeight );
452 
453                 if (GetIDocumentUndoRedo().DoesUndo())
454                 {
455                     GetIDocumentUndoRedo().AppendUndo(
456                             std::make_unique<SwUndoAttrTable>(*pTableNd));
457                 }
458 
459                 std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
460                 aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
461                 for( auto pLn : aRowArr )
462                     ::lcl_ProcessRowSize( aFormatCmp, pLn, aNew );
463 
464                 getIDocumentState().SetModified();
465             }
466             bRet = true;
467         }
468     }
469     return bRet;
470 }
471 
SetRowBackground(const SwCursor & rCursor,const SvxBrushItem & rNew)472 void SwDoc::SetRowBackground( const SwCursor& rCursor, const SvxBrushItem &rNew )
473 {
474     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
475     if( pTableNd )
476     {
477         std::vector<SwTableLine*> aRowArr; // For Lines collecting
478         ::lcl_CollectLines( aRowArr, rCursor, true );
479 
480         if( !aRowArr.empty() )
481         {
482             if (GetIDocumentUndoRedo().DoesUndo())
483             {
484                 GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
485             }
486 
487             std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
488             aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
489 
490             for( auto pLn : aRowArr )
491                 ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
492 
493             getIDocumentState().SetModified();
494         }
495     }
496 }
497 
GetRowBackground(const SwCursor & rCursor,std::shared_ptr<SvxBrushItem> & rToFill)498 bool SwDoc::GetRowBackground( const SwCursor& rCursor, std::shared_ptr<SvxBrushItem>& rToFill )
499 {
500     bool bRet = false;
501     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
502     if( pTableNd )
503     {
504         std::vector<SwTableLine*> aRowArr; // For Lines collecting
505         ::lcl_CollectLines( aRowArr, rCursor, true );
506 
507         if( !aRowArr.empty() )
508         {
509             rToFill = aRowArr[0]->GetFrameFormat()->makeBackgroundBrushItem();
510 
511             bRet = true;
512             for ( std::vector<SwTableLine*>::size_type i = 1; i < aRowArr.size(); ++i )
513             {
514                 std::shared_ptr<SvxBrushItem> aAlternative(aRowArr[i]->GetFrameFormat()->makeBackgroundBrushItem());
515 
516                 if ( rToFill != aAlternative && rToFill && aAlternative && *rToFill != *aAlternative )
517                 {
518                     bRet = false;
519                     break;
520                 }
521             }
522         }
523     }
524     return bRet;
525 }
526 
InsertCell(std::vector<SwCellFrame * > & rCellArr,SwCellFrame * pCellFrame)527 static void InsertCell( std::vector<SwCellFrame*>& rCellArr, SwCellFrame* pCellFrame )
528 {
529     if( rCellArr.end() == std::find( rCellArr.begin(), rCellArr.end(), pCellFrame ) )
530         rCellArr.push_back( pCellFrame );
531 }
532 
lcl_CollectCells(std::vector<SwCellFrame * > & rArr,const SwRect & rUnion,SwTabFrame * pTab)533 static void lcl_CollectCells( std::vector<SwCellFrame*> &rArr, const SwRect &rUnion,
534                           SwTabFrame *pTab )
535 {
536     SwLayoutFrame *pCell = pTab->FirstCell();
537     do
538     {
539         // If the Cell contains a CellFrame, we need to use it
540         // in order to get to the Cell
541         while ( !pCell->IsCellFrame() )
542             pCell = pCell->GetUpper();
543         OSL_ENSURE( pCell, "Frame is not a Cell" );
544         if ( rUnion.IsOver( pCell->getFrameArea() ) )
545             ::InsertCell( rArr, static_cast<SwCellFrame*>(pCell) );
546 
547         // Make sure the Cell is left (Areas)
548         SwLayoutFrame *pTmp = pCell;
549         do
550         {   pTmp = pTmp->GetNextLayoutLeaf();
551         } while ( pCell->IsAnLower( pTmp ) );
552         pCell = pTmp;
553     } while( pCell && pTab->IsAnLower( pCell ) );
554 }
555 
SetTabBorders(const SwCursor & rCursor,const SfxItemSet & rSet)556 void SwDoc::SetTabBorders( const SwCursor& rCursor, const SfxItemSet& rSet )
557 {
558     SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
559     SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
560     if( !pTableNd )
561         return ;
562 
563     SwLayoutFrame *pStart, *pEnd;
564     ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
565 
566     SwSelUnions aUnions;
567     ::MakeSelUnions( aUnions, pStart, pEnd );
568 
569     if( aUnions.empty() )
570         return;
571 
572     SwTable& rTable = pTableNd->GetTable();
573     if (GetIDocumentUndoRedo().DoesUndo())
574     {
575         GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
576     }
577 
578     std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
579     aFormatCmp.reserve( 255 );
580     const SvxBoxItem* pSetBox;
581     const SvxBoxInfoItem *pSetBoxInfo;
582 
583     const SvxBorderLine* pLeft = nullptr;
584     const SvxBorderLine* pRight = nullptr;
585     const SvxBorderLine* pTop = nullptr;
586     const SvxBorderLine* pBottom = nullptr;
587     const SvxBorderLine* pHori = nullptr;
588     const SvxBorderLine* pVert = nullptr;
589     bool bHoriValid = true, bVertValid = true,
590          bTopValid = true, bBottomValid = true,
591          bLeftValid = true, bRightValid = true;
592 
593     // The Flags in the BoxInfo Item decide whether a BorderLine is valid!
594     if( SfxItemState::SET == rSet.GetItemState( SID_ATTR_BORDER_INNER, false,
595         reinterpret_cast<const SfxPoolItem**>(&pSetBoxInfo)) )
596     {
597         pHori = pSetBoxInfo->GetHori();
598         pVert = pSetBoxInfo->GetVert();
599 
600         bHoriValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::HORI);
601         bVertValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::VERT);
602 
603         // Do we want to evaluate these?
604         bTopValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::TOP);
605         bBottomValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
606         bLeftValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT);
607         bRightValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT);
608     }
609 
610     if( SfxItemState::SET == rSet.GetItemState( RES_BOX, false,
611         reinterpret_cast<const SfxPoolItem**>(&pSetBox)) )
612     {
613         pLeft = pSetBox->GetLeft();
614         pRight = pSetBox->GetRight();
615         pTop = pSetBox->GetTop();
616         pBottom = pSetBox->GetBottom();
617     }
618     else
619     {
620         // Not set, thus not valid values
621         bTopValid = bBottomValid = bLeftValid = bRightValid = false;
622         pSetBox = nullptr;
623     }
624 
625     bool bFirst = true;
626     for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
627     {
628         SwSelUnion *pUnion = &aUnions[i];
629         SwTabFrame *pTab = pUnion->GetTable();
630         const SwRect &rUnion = pUnion->GetUnion();
631         const bool bLast  = (i == aUnions.size() - 1);
632 
633         std::vector<SwCellFrame*> aCellArr;
634         aCellArr.reserve( 255 );
635         ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
636 
637         // All Cell Borders that match the UnionRect or extend it are
638         // Outer Borders. All others are Inner Borders.
639 
640         // New: The Outer Borders can, depending on whether it's a
641         // Start/Middle/Follow Table (for Selection via FollowTabs),
642         // also not be Outer Borders.
643         // Outer Borders are set on the left, right, at the top and at the bottom.
644         // Inner Borders are only set at the top and on the left.
645         for ( auto pCell : aCellArr )
646         {
647             const bool bVert = pTab->IsVertical();
648             const bool bRTL = pTab->IsRightToLeft();
649             bool bTopOver, bLeftOver, bRightOver, bBottomOver;
650             if ( bVert )
651             {
652                 bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
653                 bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
654                 bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
655                 bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
656             }
657             else
658             {
659                 bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
660                 bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
661                 bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
662                 bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
663             }
664 
665             if ( bRTL )
666             {
667                 bool bTmp = bRightOver;
668                 bRightOver = bLeftOver;
669                 bLeftOver = bTmp;
670             }
671 
672             // Do not set anything by default in HeadlineRepeats
673             if ( pTab->IsFollow() &&
674                  ( pTab->IsInHeadline( *pCell ) ||
675                    // Same holds for follow flow rows
676                    pCell->IsInFollowFlowRow() ) )
677                 continue;
678 
679             SvxBoxItem aBox( pCell->GetFormat()->GetBox() );
680 
681             sal_Int16 nType = 0;
682 
683             // Top Border
684             if( bTopValid )
685             {
686                 if ( bFirst && bTopOver )
687                 {
688                     aBox.SetLine( pTop, SvxBoxItemLine::TOP );
689                     nType |= 0x0001;
690                 }
691                 else if ( bHoriValid )
692                 {
693                     aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
694                     nType |= 0x0002;
695                 }
696             }
697 
698             // Fix fdo#62470 correct the input for RTL table
699             if (bRTL)
700             {
701                     if( bLeftOver && bRightOver)
702                     {
703                         if ( bLeftValid )
704                         {
705                             aBox.SetLine( pLeft, SvxBoxItemLine::RIGHT );
706                             nType |= 0x0010;
707                         }
708                         if ( bRightValid )
709                         {
710                             aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
711                             nType |= 0x0004;
712                         }
713                     }
714                     else
715                     {
716                         if ( bLeftValid )
717                         {
718                             aBox.SetLine( bRightOver ? pLeft : nullptr, SvxBoxItemLine::RIGHT );
719                             if (bVertValid)
720                                 nType |= 0x0020;
721                             else
722                                 nType |= 0x0010;
723                         }
724                         if ( bLeftOver )
725                         {
726                             if ( bRightValid )
727                             {
728                                 aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
729                                 nType |= 0x0004;
730                             }
731                         }
732                         else if ( bVertValid )
733                         {
734                             aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
735                             nType |= 0x0008;
736                         }
737                     }
738             }
739             else
740             {
741                 // Left Border
742                 if ( bLeftOver )
743                 {
744                     if( bLeftValid )
745                     {
746                         aBox.SetLine( pLeft, SvxBoxItemLine::LEFT );
747                         nType |= 0x0004;
748                     }
749                 }
750                 else if( bVertValid )
751                 {
752                     aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
753                     nType |= 0x0008;
754                 }
755 
756                 // Right Border
757                 if( bRightValid )
758                 {
759                     if ( bRightOver )
760                     {
761                         aBox.SetLine( pRight, SvxBoxItemLine::RIGHT );
762                         nType |= 0x0010;
763                     }
764                     else if ( bVertValid )
765                     {
766                         aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
767                         nType |= 0x0020;
768                     }
769                 }
770             }
771 
772             // Bottom Border
773             if ( bLast && bBottomOver )
774             {
775                 if( bBottomValid )
776                 {
777                     aBox.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
778                     nType |= 0x0040;
779                 }
780             }
781             else if( bHoriValid )
782             {
783                 aBox.SetLine( pHori, SvxBoxItemLine::BOTTOM );
784                 nType |= 0x0080;
785             }
786 
787             if( pSetBox )
788             {
789                for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
790                     aBox.SetDistance( pSetBox->GetDistance( k ), k );
791             }
792 
793             SwTableBox *pBox = const_cast<SwTableBox*>(pCell->GetTabBox());
794             SwFrameFormat *pNewFormat;
795             if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), nType )))
796                 pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
797             else
798             {
799                 SwFrameFormat *pOld = pBox->GetFrameFormat();
800                 SwFrameFormat *pNew = pBox->ClaimFrameFormat();
801                 pNew->SetFormatAttr( aBox );
802                 aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, nType));
803             }
804         }
805 
806         bFirst = false;
807     }
808 
809     SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
810     if( pTableLayout )
811     {
812         SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
813         SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
814 
815         pTableLayout->BordersChanged(
816             pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
817     }
818     ::ClearFEShellTabCols(*this, nullptr);
819     getIDocumentState().SetModified();
820 }
821 
lcl_SetLineStyle(SvxBorderLine * pToSet,const Color * pColor,const SvxBorderLine * pBorderLine)822 static void lcl_SetLineStyle( SvxBorderLine *pToSet,
823                           const Color *pColor, const SvxBorderLine *pBorderLine)
824 {
825     if ( pBorderLine )
826     {
827         if ( !pColor )
828         {
829             Color aTmp( pToSet->GetColor() );
830             *pToSet = *pBorderLine;
831             pToSet->SetColor( aTmp );
832         }
833         else
834             *pToSet = *pBorderLine;
835     }
836     if ( pColor )
837         pToSet->SetColor( *pColor );
838 }
839 
SetTabLineStyle(const SwCursor & rCursor,const Color * pColor,bool bSetLine,const SvxBorderLine * pBorderLine)840 void SwDoc::SetTabLineStyle( const SwCursor& rCursor,
841                              const Color* pColor, bool bSetLine,
842                              const SvxBorderLine* pBorderLine )
843 {
844     SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
845     SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
846     if( !pTableNd )
847         return ;
848 
849     SwLayoutFrame *pStart, *pEnd;
850     ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
851 
852     SwSelUnions aUnions;
853     ::MakeSelUnions( aUnions, pStart, pEnd );
854 
855     if( !aUnions.empty() )
856     {
857         SwTable& rTable = pTableNd->GetTable();
858         if (GetIDocumentUndoRedo().DoesUndo())
859         {
860             GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
861         }
862 
863         for( auto &rU : aUnions )
864         {
865             SwSelUnion *pUnion = &rU;
866             SwTabFrame *pTab = pUnion->GetTable();
867             std::vector<SwCellFrame*> aCellArr;
868             aCellArr.reserve( 255 );
869             ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
870 
871             for ( auto pCell : aCellArr )
872             {
873                 // Do not set anything by default in HeadlineRepeats
874                 if ( pTab->IsFollow() && pTab->IsInHeadline( *pCell ) )
875                     continue;
876 
877                 const_cast<SwTableBox*>(pCell->GetTabBox())->ClaimFrameFormat();
878                 SwFrameFormat *pFormat = pCell->GetFormat();
879                 std::shared_ptr<SvxBoxItem> aBox(static_cast<SvxBoxItem*>(pFormat->GetBox().Clone()));
880 
881                 if ( !pBorderLine && bSetLine )
882                 {
883                     aBox.reset(static_cast<SvxBoxItem*>(::GetDfltAttr(RES_BOX)->Clone()));
884                 }
885                 else
886                 {
887                     if ( aBox->GetTop() )
888                         ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetTop()),
889                                         pColor, pBorderLine );
890                     if ( aBox->GetBottom() )
891                         ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetBottom()),
892                                         pColor, pBorderLine );
893                     if ( aBox->GetLeft() )
894                         ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetLeft()),
895                                         pColor, pBorderLine );
896                     if ( aBox->GetRight() )
897                         ::lcl_SetLineStyle( const_cast<SvxBorderLine*>(aBox->GetRight()),
898                                         pColor, pBorderLine );
899                 }
900                 pFormat->SetFormatAttr( *aBox );
901             }
902         }
903 
904         SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
905         if( pTableLayout )
906         {
907             SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
908             SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
909 
910             pTableLayout->BordersChanged(
911                 pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
912         }
913         ::ClearFEShellTabCols(*this, nullptr);
914         getIDocumentState().SetModified();
915     }
916 }
917 
GetTabBorders(const SwCursor & rCursor,SfxItemSet & rSet)918 void SwDoc::GetTabBorders( const SwCursor& rCursor, SfxItemSet& rSet )
919 {
920     SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
921     SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
922     if( !pTableNd )
923         return ;
924 
925     SwLayoutFrame *pStart, *pEnd;
926     ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
927 
928     SwSelUnions aUnions;
929     ::MakeSelUnions( aUnions, pStart, pEnd );
930 
931     if( !aUnions.empty() )
932     {
933         SvxBoxItem     aSetBox    ( rSet.Get(RES_BOX    ) );
934         SvxBoxInfoItem aSetBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
935 
936         bool bTopSet      = false,
937              bBottomSet   = false,
938              bLeftSet     = false,
939              bRightSet    = false,
940              bHoriSet     = false,
941              bVertSet     = false,
942              bDistanceSet = false,
943              bRTLTab      = false;
944 
945         aSetBoxInfo.ResetFlags();
946 
947         for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
948         {
949             SwSelUnion *pUnion = &aUnions[i];
950             const SwTabFrame *pTab = pUnion->GetTable();
951             const SwRect &rUnion = pUnion->GetUnion();
952             const bool bFirst = i == 0;
953             const bool bLast  = (i == aUnions.size() - 1);
954 
955             std::vector<SwCellFrame*> aCellArr;
956             aCellArr.reserve(255);
957             ::lcl_CollectCells( aCellArr, rUnion, const_cast<SwTabFrame*>(pTab) );
958 
959             for ( auto pCell : aCellArr )
960             {
961                 const bool bVert = pTab->IsVertical();
962                 const bool bRTL = bRTLTab = pTab->IsRightToLeft();
963                 bool bTopOver, bLeftOver, bRightOver, bBottomOver;
964                 if ( bVert )
965                 {
966                     bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
967                     bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
968                     bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
969                     bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
970                 }
971                 else
972                 {
973                     bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
974                     bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
975                     bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
976                     bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
977                 }
978 
979                 if ( bRTL )
980                 {
981                     bool bTmp = bRightOver;
982                     bRightOver = bLeftOver;
983                     bLeftOver = bTmp;
984                 }
985 
986                 const SwFrameFormat  *pFormat  = pCell->GetFormat();
987                 const SvxBoxItem  &rBox  = pFormat->GetBox();
988 
989                 // Top Border
990                 if ( bFirst && bTopOver )
991                 {
992                     if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
993                     {
994                         if ( !bTopSet )
995                         {   bTopSet = true;
996                             aSetBox.SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
997                         }
998                         else if ((aSetBox.GetTop() && rBox.GetTop() &&
999                                  (*aSetBox.GetTop() != *rBox.GetTop())) ||
1000                                  ((!aSetBox.GetTop()) != (!rBox.GetTop()))) // != expression is true, if one and only one of the two pointers is !0
1001                         {
1002                             aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, false );
1003                             aSetBox.SetLine( nullptr, SvxBoxItemLine::TOP );
1004                         }
1005                     }
1006                 }
1007 
1008                 // Left Border
1009                 if ( bLeftOver )
1010                 {
1011                     if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT))
1012                     {
1013                         if ( !bLeftSet )
1014                         {   bLeftSet = true;
1015                             aSetBox.SetLine( rBox.GetLeft(), SvxBoxItemLine::LEFT );
1016                         }
1017                         else if ((aSetBox.GetLeft() && rBox.GetLeft() &&
1018                                  (*aSetBox.GetLeft() != *rBox.GetLeft())) ||
1019                                  ((!aSetBox.GetLeft()) != (!rBox.GetLeft())))
1020                         {
1021                             aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, false );
1022                             aSetBox.SetLine( nullptr, SvxBoxItemLine::LEFT );
1023                         }
1024                     }
1025                 }
1026                 else
1027                 {
1028                     if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::VERT))
1029                     {
1030                         if ( !bVertSet )
1031                         {   bVertSet = true;
1032                             aSetBoxInfo.SetLine( rBox.GetLeft(), SvxBoxInfoItemLine::VERT );
1033                         }
1034                         else if ((aSetBoxInfo.GetVert() && rBox.GetLeft() &&
1035                                  (*aSetBoxInfo.GetVert() != *rBox.GetLeft())) ||
1036                                  ((!aSetBoxInfo.GetVert()) != (!rBox.GetLeft())))
1037                         {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::VERT, false );
1038                             aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
1039                         }
1040                     }
1041                 }
1042 
1043                 // Right Border
1044                 if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) && bRightOver )
1045                 {
1046                     if ( !bRightSet )
1047                     {   bRightSet = true;
1048                         aSetBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
1049                     }
1050                     else if ((aSetBox.GetRight() && rBox.GetRight() &&
1051                              (*aSetBox.GetRight() != *rBox.GetRight())) ||
1052                              (!aSetBox.GetRight() != !rBox.GetRight()))
1053                     {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, false );
1054                         aSetBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
1055                     }
1056                 }
1057 
1058                 // Bottom Border
1059                 if ( bLast && bBottomOver )
1060                 {
1061                     if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
1062                     {
1063                         if ( !bBottomSet )
1064                         {   bBottomSet = true;
1065                             aSetBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
1066                         }
1067                         else if ((aSetBox.GetBottom() && rBox.GetBottom() &&
1068                                  (*aSetBox.GetBottom() != *rBox.GetBottom())) ||
1069                                  (!aSetBox.GetBottom() != !rBox.GetBottom()))
1070                         {   aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, false );
1071                             aSetBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
1072                         }
1073                     }
1074                 }
1075                 // In all Lines, except for the last one, the horizontal Line
1076                 // is taken from the Bottom Line.
1077                 else
1078                 {
1079                     if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::HORI))
1080                     {
1081                         if ( !bHoriSet )
1082                         {   bHoriSet = true;
1083                             aSetBoxInfo.SetLine( rBox.GetBottom(), SvxBoxInfoItemLine::HORI );
1084                         }
1085                         else if ((aSetBoxInfo.GetHori() && rBox.GetBottom() &&
1086                                  (*aSetBoxInfo.GetHori() != *rBox.GetBottom())) ||
1087                                  ((!aSetBoxInfo.GetHori()) != (!rBox.GetBottom())))
1088                         {
1089                             aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::HORI, false );
1090                             aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
1091                         }
1092                     }
1093                 }
1094 
1095                 // Distance to text
1096                 if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::DISTANCE))
1097                 {
1098                     if( !bDistanceSet ) // Set on first iteration
1099                     {
1100                         bDistanceSet = true;
1101                         for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
1102                             aSetBox.SetDistance( rBox.GetDistance( k ), k );
1103                     }
1104                     else
1105                     {
1106                         for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
1107                             if( aSetBox.GetDistance( k ) !=
1108                                 rBox.GetDistance( k ) )
1109                             {
1110                                 aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, false );
1111                                 aSetBox.SetAllDistances(0);
1112                                 break;
1113                             }
1114                     }
1115                 }
1116             }
1117         }
1118 
1119         // fdo#62470 fix the reading for table format.
1120         if ( bRTLTab )
1121         {
1122             SvxBoxItem     aTempBox    ( rSet.Get(RES_BOX    ) );
1123             SvxBoxInfoItem aTempBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
1124 
1125             aTempBox.SetLine( aSetBox.GetRight(), SvxBoxItemLine::RIGHT);
1126             aSetBox.SetLine( aSetBox.GetLeft(), SvxBoxItemLine::RIGHT);
1127             aSetBox.SetLine( aTempBox.GetRight(), SvxBoxItemLine::LEFT);
1128 
1129             aTempBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
1130             aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) );
1131             aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
1132         }
1133 
1134         rSet.Put( aSetBox );
1135         rSet.Put( aSetBoxInfo );
1136     }
1137 }
1138 
SetBoxAttr(const SwCursor & rCursor,const SfxPoolItem & rNew)1139 void SwDoc::SetBoxAttr( const SwCursor& rCursor, const SfxPoolItem &rNew )
1140 {
1141     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
1142     SwSelBoxes aBoxes;
1143     if( pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes, true ) )
1144     {
1145         SwTable& rTable = pTableNd->GetTable();
1146         if (GetIDocumentUndoRedo().DoesUndo())
1147         {
1148             GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
1149         }
1150 
1151         std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
1152         aFormatCmp.reserve(std::max<size_t>(255, aBoxes.size()));
1153         for (size_t i = 0; i < aBoxes.size(); ++i)
1154         {
1155             SwTableBox *pBox = aBoxes[i];
1156 
1157             SwFrameFormat *pNewFormat;
1158             if ( nullptr != (pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), 0 )))
1159                 pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
1160             else
1161             {
1162                 SwFrameFormat *pOld = pBox->GetFrameFormat();
1163                 SwFrameFormat *pNew = pBox->ClaimFrameFormat();
1164                 pNew->SetFormatAttr( rNew );
1165                 aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
1166             }
1167 
1168             pBox->SetDirectFormatting(true);
1169         }
1170 
1171         SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
1172         if( pTableLayout )
1173         {
1174             SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout() );
1175             SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
1176 
1177             pTableLayout->Resize(
1178                 pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ), true );
1179         }
1180         getIDocumentState().SetModified();
1181     }
1182 }
1183 
GetBoxAttr(const SwCursor & rCursor,std::shared_ptr<SfxPoolItem> & rToFill)1184 bool SwDoc::GetBoxAttr( const SwCursor& rCursor, std::shared_ptr<SfxPoolItem>& rToFill )
1185 {
1186     bool bRet = false;
1187     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
1188     SwSelBoxes aBoxes;
1189     if( pTableNd && lcl_GetBoxSel( rCursor, aBoxes ))
1190     {
1191         bRet = true;
1192         bool bOneFound = false;
1193         const sal_uInt16 nWhich = rToFill->Which();
1194         for (size_t i = 0; i < aBoxes.size(); ++i)
1195         {
1196             switch ( nWhich )
1197             {
1198                 case RES_BACKGROUND:
1199                 {
1200                     std::shared_ptr<SvxBrushItem> aBack =
1201                         aBoxes[i]->GetFrameFormat()->makeBackgroundBrushItem();
1202                     if( !bOneFound )
1203                     {
1204                         rToFill.reset(aBack->Clone());
1205                         bOneFound = true;
1206                     }
1207                     else if( rToFill != aBack )
1208                         bRet = false;
1209                 }
1210                 break;
1211 
1212                 case RES_FRAMEDIR:
1213                 {
1214                     const SvxFrameDirectionItem& rDir =
1215                                     aBoxes[i]->GetFrameFormat()->GetFrameDir();
1216                     if( !bOneFound )
1217                     {
1218                         rToFill.reset(rDir.Clone());
1219                         bOneFound = true;
1220                     }
1221                     else if( rToFill && *rToFill != rDir )
1222                         bRet = false;
1223                 }
1224                 break;
1225                 case RES_VERT_ORIENT:
1226                 {
1227                     const SwFormatVertOrient& rOrient =
1228                                     aBoxes[i]->GetFrameFormat()->GetVertOrient();
1229                     if( !bOneFound )
1230                     {
1231                         rToFill.reset(rOrient.Clone());
1232                         bOneFound = true;
1233                     }
1234                     else if( rToFill && *rToFill != rOrient )
1235                         bRet = false;
1236                 }
1237                 break;
1238             }
1239 
1240             if ( !bRet )
1241                 break;
1242         }
1243     }
1244     return bRet;
1245 }
1246 
SetBoxAlign(const SwCursor & rCursor,sal_uInt16 nAlign)1247 void SwDoc::SetBoxAlign( const SwCursor& rCursor, sal_uInt16 nAlign )
1248 {
1249     OSL_ENSURE( nAlign == text::VertOrientation::NONE   ||
1250             nAlign == text::VertOrientation::CENTER ||
1251             nAlign == text::VertOrientation::BOTTOM, "Wrong alignment" );
1252     SwFormatVertOrient aVertOri( 0, nAlign );
1253     SetBoxAttr( rCursor, aVertOri );
1254 }
1255 
GetBoxAlign(const SwCursor & rCursor)1256 sal_uInt16 SwDoc::GetBoxAlign( const SwCursor& rCursor )
1257 {
1258     sal_uInt16 nAlign = USHRT_MAX;
1259     SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
1260     SwSelBoxes aBoxes;
1261     if( pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes ))
1262     {
1263         for (size_t i = 0; i < aBoxes.size(); ++i)
1264         {
1265             const SwFormatVertOrient &rOri =
1266                             aBoxes[i]->GetFrameFormat()->GetVertOrient();
1267             if( USHRT_MAX == nAlign )
1268                 nAlign = static_cast<sal_uInt16>(rOri.GetVertOrient());
1269             else if( rOri.GetVertOrient() != nAlign )
1270             {
1271                 nAlign = USHRT_MAX;
1272                 break;
1273             }
1274         }
1275     }
1276     return nAlign;
1277 }
1278 
lcl_CalcCellFit(const SwLayoutFrame * pCell)1279 static sal_uInt16 lcl_CalcCellFit( const SwLayoutFrame *pCell )
1280 {
1281     SwTwips nRet = 0;
1282     const SwFrame *pFrame = pCell->Lower(); // The whole Line
1283     SwRectFnSet aRectFnSet(pCell);
1284     while ( pFrame )
1285     {
1286         const SwTwips nAdd = aRectFnSet.GetWidth(pFrame->getFrameArea()) -
1287                              aRectFnSet.GetWidth(pFrame->getFramePrintArea());
1288 
1289         // pFrame does not necessarily have to be a SwTextFrame!
1290         const SwTwips nCalcFitToContent = pFrame->IsTextFrame() ?
1291                                           const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent() :
1292                                           aRectFnSet.GetWidth(pFrame->getFramePrintArea());
1293 
1294         nRet = std::max( nRet, nCalcFitToContent + nAdd );
1295         pFrame = pFrame->GetNext();
1296     }
1297     // Surrounding border as well as left and Right Border also need to be respected
1298     nRet += aRectFnSet.GetWidth(pCell->getFrameArea()) -
1299             aRectFnSet.GetWidth(pCell->getFramePrintArea());
1300 
1301     // To compensate for the accuracy of calculation later on in SwTable::SetTabCols
1302     // we keep adding up a little.
1303     nRet += COLFUZZY;
1304     return static_cast<sal_uInt16>(std::max( long(MINLAY), nRet ));
1305 }
1306 
1307 /* The Line is within the Selection but not outlined by the TabCols.
1308  *
1309  * That means that the Line has been "split" by other Cells due to the
1310  * two-dimensional representation used. Thus, we have to distribute the cell's
1311  * default or minimum value amongst the Cell it has been split by.
1312  *
1313  * First, we collect the Columns (not the Column separators) which overlap
1314  * with the Cell. We then distribute the desired value according to the
1315  * amount of overlapping amongst the Cells.
1316  *
1317  * A Cell's default value stays the same if it already has a larger value than
1318  * the desired one. It's overwritten if it's smaller.
1319  */
lcl_CalcSubColValues(std::vector<sal_uInt16> & rToFill,const SwTabCols & rCols,const SwLayoutFrame * pCell,const SwLayoutFrame * pTab,bool bWishValues)1320 static void lcl_CalcSubColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
1321                               const SwLayoutFrame *pCell, const SwLayoutFrame *pTab,
1322                               bool bWishValues )
1323 {
1324     const sal_uInt16 nWish = bWishValues ?
1325                     ::lcl_CalcCellFit( pCell ) :
1326                     MINLAY + sal_uInt16(pCell->getFrameArea().Width() - pCell->getFramePrintArea().Width());
1327 
1328     SwRectFnSet aRectFnSet(pTab);
1329 
1330     for ( size_t i = 0 ; i <= rCols.Count(); ++i )
1331     {
1332         long nColLeft  = i == 0             ? rCols.GetLeft()  : rCols[i-1];
1333         long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
1334         nColLeft  += rCols.GetLeftMin();
1335         nColRight += rCols.GetLeftMin();
1336 
1337         // Adapt values to the proportions of the Table (Follows)
1338         if ( rCols.GetLeftMin() != aRectFnSet.GetLeft(pTab->getFrameArea()) )
1339         {
1340             const long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
1341             nColLeft  += nDiff;
1342             nColRight += nDiff;
1343         }
1344         const long nCellLeft  = aRectFnSet.GetLeft(pCell->getFrameArea());
1345         const long nCellRight = aRectFnSet.GetRight(pCell->getFrameArea());
1346 
1347         // Calculate overlapping value
1348         long nWidth = 0;
1349         if ( nColLeft <= nCellLeft && nColRight >= (nCellLeft+COLFUZZY) )
1350             nWidth = nColRight - nCellLeft;
1351         else if ( nColLeft <= (nCellRight-COLFUZZY) && nColRight >= nCellRight )
1352             nWidth = nCellRight - nColLeft;
1353         else if ( nColLeft >= nCellLeft && nColRight <= nCellRight )
1354             nWidth = nColRight - nColLeft;
1355         if ( nWidth && pCell->getFrameArea().Width() )
1356         {
1357             long nTmp = nWidth * nWish / pCell->getFrameArea().Width();
1358             if ( sal_uInt16(nTmp) > rToFill[i] )
1359                 rToFill[i] = sal_uInt16(nTmp);
1360         }
1361     }
1362 }
1363 
1364 /**
1365  * Retrieves new values to set the TabCols.
1366  *
1367  * We do not iterate over the TabCols' entries, but over the gaps that describe Cells.
1368  * We set TabCol entries for which we did not calculate Cells to 0.
1369  *
1370  * @param bWishValues == true:      We calculate the desired value of all affected
1371  *                                  Cells for the current Selection/current Cell.
1372  *                                  If more Cells are within a Column, the highest
1373  *                                  desired value is returned.
1374  *                                  We set TabCol entries for which we did not calculate
1375  *                                  Cells to 0.
1376  *
1377  * @param bWishValues == false:     The Selection is expanded vertically.
1378  *                                  We calculate the minimum value for every
1379  *                                  Column in the TabCols that intersects with the
1380  *                                  Selection.
1381  */
lcl_CalcColValues(std::vector<sal_uInt16> & rToFill,const SwTabCols & rCols,const SwLayoutFrame * pStart,const SwLayoutFrame * pEnd,bool bWishValues)1382 static void lcl_CalcColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
1383                            const SwLayoutFrame *pStart, const SwLayoutFrame *pEnd,
1384                            bool bWishValues )
1385 {
1386     SwSelUnions aUnions;
1387     ::MakeSelUnions( aUnions, pStart, pEnd,
1388                     bWishValues ? SwTableSearchType::NONE : SwTableSearchType::Col );
1389 
1390     for ( auto &rU : aUnions )
1391     {
1392         SwSelUnion *pSelUnion = &rU;
1393         const SwTabFrame *pTab = pSelUnion->GetTable();
1394         const SwRect &rUnion = pSelUnion->GetUnion();
1395 
1396         SwRectFnSet aRectFnSet(pTab);
1397         bool bRTL = pTab->IsRightToLeft();
1398 
1399         const SwLayoutFrame *pCell = pTab->FirstCell();
1400         if (!pCell)
1401             continue;
1402         do
1403         {
1404             if ( pCell->IsCellFrame() && pCell->FindTabFrame() == pTab && ::IsFrameInTableSel( rUnion, pCell ) )
1405             {
1406                 const long nCLeft  = aRectFnSet.GetLeft(pCell->getFrameArea());
1407                 const long nCRight = aRectFnSet.GetRight(pCell->getFrameArea());
1408 
1409                 bool bNotInCols = true;
1410 
1411                 for ( size_t i = 0; i <= rCols.Count(); ++i )
1412                 {
1413                     sal_uInt16 nFit = rToFill[i];
1414                     long nColLeft  = i == 0             ? rCols.GetLeft()  : rCols[i-1];
1415                     long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
1416 
1417                     if ( bRTL )
1418                     {
1419                         long nTmpRight = nColRight;
1420                         nColRight = rCols.GetRight() - nColLeft;
1421                         nColLeft = rCols.GetRight() - nTmpRight;
1422                     }
1423 
1424                     nColLeft  += rCols.GetLeftMin();
1425                     nColRight += rCols.GetLeftMin();
1426 
1427                     // Adapt values to the proportions of the Table (Follows)
1428                     long nLeftA  = nColLeft;
1429                     long nRightA = nColRight;
1430                     if ( rCols.GetLeftMin() !=  sal_uInt16(aRectFnSet.GetLeft(pTab->getFrameArea())) )
1431                     {
1432                         const long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
1433                         nLeftA  += nDiff;
1434                         nRightA += nDiff;
1435                     }
1436 
1437                     // We don't want to take a too close look
1438                     if ( ::IsSame(nCLeft, nLeftA) && ::IsSame(nCRight, nRightA))
1439                     {
1440                         bNotInCols = false;
1441                         if ( bWishValues )
1442                         {
1443                             const sal_uInt16 nWish = ::lcl_CalcCellFit( pCell );
1444                             if ( nWish > nFit )
1445                                 nFit = nWish;
1446                         }
1447                         else
1448                         {   const sal_uInt16 nMin = MINLAY + sal_uInt16(pCell->getFrameArea().Width() -
1449                                                                 pCell->getFramePrintArea().Width());
1450                             if ( !nFit || nMin < nFit )
1451                                 nFit = nMin;
1452                         }
1453                         if ( rToFill[i] < nFit )
1454                             rToFill[i] = nFit;
1455                     }
1456                 }
1457                 if ( bNotInCols )
1458                     ::lcl_CalcSubColValues( rToFill, rCols, pCell, pTab, bWishValues );
1459             }
1460             do {
1461                 pCell = pCell->GetNextLayoutLeaf();
1462             } while( pCell && pCell->getFrameArea().Width() == 0 );
1463         } while ( pCell && pTab->IsAnLower( pCell ) );
1464     }
1465 }
1466 
AdjustCellWidth(const SwCursor & rCursor,const bool bBalance,const bool bNoShrink)1467 void SwDoc::AdjustCellWidth( const SwCursor& rCursor,
1468                              const bool bBalance,
1469                              const bool bNoShrink )
1470 {
1471     // Check whether the current Cursor has it's Point/Mark in a Table
1472     SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
1473     SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
1474     if( !pTableNd )
1475         return ;
1476 
1477     SwLayoutFrame *pStart, *pEnd;
1478     ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
1479 
1480     // Collect TabCols; we reset the Table with them
1481     SwFrame* pBoxFrame = pStart;
1482     while( pBoxFrame && !pBoxFrame->IsCellFrame() )
1483         pBoxFrame = pBoxFrame->GetUpper();
1484 
1485     if ( !pBoxFrame )
1486         return; // Robust
1487 
1488     SwTabCols aTabCols;
1489     GetTabCols( aTabCols, static_cast<SwCellFrame*>(pBoxFrame) );
1490 
1491     if ( ! aTabCols.Count() )
1492         return;
1493 
1494     std::vector<sal_uInt16> aWish(aTabCols.Count() + 1);
1495     std::vector<sal_uInt16> aMins(aTabCols.Count() + 1);
1496 
1497     ::lcl_CalcColValues( aWish, aTabCols, pStart, pEnd, /*bWishValues=*/true );
1498 
1499     // It's more robust if we calculate the minimum values for the whole Table
1500     const SwTabFrame *pTab = pStart->ImplFindTabFrame();
1501     pStart = const_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame const *>(pTab->FirstCell()));
1502     pEnd   = const_cast<SwLayoutFrame*>(pTab->FindLastContentOrTable()->GetUpper());
1503     while( !pEnd->IsCellFrame() )
1504         pEnd = pEnd->GetUpper();
1505     ::lcl_CalcColValues( aMins, aTabCols, pStart, pEnd, /*bWishValues=*/false );
1506 
1507     sal_uInt16 nSelectedWidth = 0, nCols = 0;
1508     float fTotalWish = 0;
1509     if ( bBalance || bNoShrink )
1510     {
1511         // Find the combined size of the selected columns
1512         for ( size_t i = 0; i <= aTabCols.Count(); ++i )
1513         {
1514             if ( aWish[i] )
1515             {
1516                 if ( i == 0 )
1517                     nSelectedWidth += aTabCols[i] - aTabCols.GetLeft();
1518                 else if ( i == aTabCols.Count() )
1519                     nSelectedWidth += aTabCols.GetRight() - aTabCols[i-1];
1520                 else
1521                     nSelectedWidth += aTabCols[i] - aTabCols[i-1];
1522                 ++nCols;
1523             }
1524             fTotalWish += aWish[i];
1525         }
1526         const sal_uInt16 nEqualWidth = nSelectedWidth / nCols;
1527         // bBalance: Distribute the width evenly
1528         for (sal_uInt16 & rn : aWish)
1529             if ( rn && bBalance )
1530                 rn = nEqualWidth;
1531     }
1532 
1533     const long nOldRight = aTabCols.GetRight();
1534 
1535     // In order to make the implementation easier, but still use the available
1536     // space properly, we do this twice.
1537 
1538     // The problem: The first column is getting wider, the others get slimmer
1539     // only afterwards.
1540     // The first column's desired width would be discarded as it would cause
1541     // the Table's width to exceed the maximum width.
1542     const sal_uInt16 nEqualWidth = (aTabCols.GetRight() - aTabCols.GetLeft()) / (aTabCols.Count() + 1);
1543     const sal_Int16 nTablePadding = nSelectedWidth - fTotalWish;
1544     for ( int k = 0; k < 2; ++k )
1545     {
1546         for ( size_t i = 0; i <= aTabCols.Count(); ++i )
1547         {
1548             // bNoShrink: distribute excess space proportionately on pass 2.
1549             if ( bNoShrink && k && nTablePadding > 0 && fTotalWish > 0 )
1550                 aWish[i] += round( aWish[i] / fTotalWish * nTablePadding );
1551 
1552             // First pass is primarily a shrink pass. Give all columns a chance
1553             //    to grow by requesting the maximum width as "balanced".
1554             // Second pass is a first-come, first-served chance to max out.
1555             int nDiff = k ? aWish[i] : std::min(aWish[i], nEqualWidth);
1556             if ( nDiff )
1557             {
1558                 int nMin = aMins[i];
1559                 if ( nMin > nDiff )
1560                     nDiff = nMin;
1561 
1562                 if ( i == 0 )
1563                 {
1564                     if( aTabCols.Count() )
1565                         nDiff -= aTabCols[0] - aTabCols.GetLeft();
1566                     else
1567                         nDiff -= aTabCols.GetRight() - aTabCols.GetLeft();
1568                 }
1569                 else if ( i == aTabCols.Count() )
1570                     nDiff -= aTabCols.GetRight() - aTabCols[i-1];
1571                 else
1572                     nDiff -= aTabCols[i] - aTabCols[i-1];
1573 
1574                 long nTabRight = aTabCols.GetRight() + nDiff;
1575 
1576                 // If the Table would become too wide, we restrict the
1577                 // adjusted amount to the allowed maximum.
1578                 if ( !bBalance && nTabRight > aTabCols.GetRightMax() )
1579                 {
1580                     const long nTmpD = nTabRight - aTabCols.GetRightMax();
1581                     nDiff     -= nTmpD;
1582                     nTabRight -= nTmpD;
1583                 }
1584                 for ( size_t i2 = i; i2 < aTabCols.Count(); ++i2 )
1585                     aTabCols[i2] += nDiff;
1586                 aTabCols.SetRight( nTabRight );
1587             }
1588         }
1589     }
1590 
1591     const long nNewRight = aTabCols.GetRight();
1592 
1593     SwFrameFormat *pFormat = pTableNd->GetTable().GetFrameFormat();
1594     const sal_Int16 nOriHori = pFormat->GetHoriOrient().GetHoriOrient();
1595 
1596     // We can leave the "real" work to the SwTable now
1597     SetTabCols( aTabCols, false, static_cast<SwCellFrame*>(pBoxFrame) );
1598 
1599     // Alignment might have been changed in SetTabCols; restore old value
1600     const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
1601     SwFormatHoriOrient aHori( rHori );
1602     if ( aHori.GetHoriOrient() != nOriHori )
1603     {
1604         aHori.SetHoriOrient( nOriHori );
1605         pFormat->SetFormatAttr( aHori );
1606     }
1607 
1608     // We switch to left-adjusted for automatic width
1609     // We adjust the right border for Border attributes
1610     if( !bBalance && nNewRight < nOldRight )
1611     {
1612         if( aHori.GetHoriOrient() == text::HoriOrientation::FULL )
1613         {
1614             aHori.SetHoriOrient( text::HoriOrientation::LEFT );
1615             pFormat->SetFormatAttr( aHori );
1616         }
1617     }
1618 
1619     getIDocumentState().SetModified();
1620 }
1621 
1622 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1623