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