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