/src/libreoffice/svx/source/table/tablelayouter.cxx
Line | Count | Source |
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 | | |
21 | | #include <com/sun/star/table/XMergeableCell.hpp> |
22 | | |
23 | | #include <tools/debug.hxx> |
24 | | #include <comphelper/diagnose_ex.hxx> |
25 | | #include <tools/gen.hxx> |
26 | | #include <libxml/xmlwriter.h> |
27 | | |
28 | | #include <cell.hxx> |
29 | | #include <o3tl/safeint.hxx> |
30 | | #include <tablemodel.hxx> |
31 | | #include "tablelayouter.hxx" |
32 | | #include <svx/svdotable.hxx> |
33 | | #include <editeng/borderline.hxx> |
34 | | #include <editeng/boxitem.hxx> |
35 | | #include <utility> |
36 | | |
37 | | using ::editeng::SvxBorderLine; |
38 | | using namespace ::com::sun::star::uno; |
39 | | using namespace ::com::sun::star::container; |
40 | | using namespace ::com::sun::star::beans; |
41 | | using namespace ::com::sun::star::table; |
42 | | using namespace ::com::sun::star::text; |
43 | | |
44 | | |
45 | | namespace sdr::table { |
46 | | |
47 | | |
48 | | static SvxBorderLine gEmptyBorder; |
49 | | |
50 | | constexpr OUString gsSize( u"Size"_ustr ); |
51 | | |
52 | | TableLayouter::TableLayouter( TableModelRef xTableModel ) |
53 | 2.34k | : mxTable(std::move( xTableModel )) |
54 | 2.34k | { |
55 | 2.34k | } |
56 | | |
57 | | |
58 | | TableLayouter::~TableLayouter() |
59 | 2.34k | { |
60 | 2.34k | ClearBorderLayout(); |
61 | 2.34k | } |
62 | | |
63 | | |
64 | | basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const |
65 | 0 | { |
66 | 0 | sal_Int32 width = 0; |
67 | 0 | sal_Int32 height = 0; |
68 | |
|
69 | 0 | try |
70 | 0 | { |
71 | 0 | if( xCell.is() && !xCell->isMerged() ) |
72 | 0 | { |
73 | 0 | CellPos aPos( rPos ); |
74 | |
|
75 | 0 | sal_Int32 nRowCount = getRowCount(); |
76 | 0 | sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), sal_Int32(1) ); |
77 | 0 | while( nRowSpan && (aPos.mnRow < nRowCount) ) |
78 | 0 | { |
79 | 0 | if( static_cast<sal_Int32>(maRows.size()) <= aPos.mnRow ) |
80 | 0 | break; |
81 | | |
82 | 0 | height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize); |
83 | 0 | nRowSpan--; |
84 | 0 | } |
85 | |
|
86 | 0 | sal_Int32 nColCount = getColumnCount(); |
87 | 0 | sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), sal_Int32(1) ); |
88 | 0 | while( nColSpan && (aPos.mnCol < nColCount ) ) |
89 | 0 | { |
90 | 0 | if( static_cast<sal_Int32>(maColumns.size()) <= aPos.mnCol ) |
91 | 0 | break; |
92 | | |
93 | 0 | width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize); |
94 | 0 | nColSpan--; |
95 | 0 | } |
96 | 0 | } |
97 | 0 | } |
98 | 0 | catch( Exception& ) |
99 | 0 | { |
100 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
101 | 0 | } |
102 | | |
103 | 0 | return basegfx::B2ITuple( width, height ); |
104 | 0 | } |
105 | | |
106 | | |
107 | | bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const |
108 | 0 | { |
109 | 0 | try |
110 | 0 | { |
111 | 0 | if( xCell.is() && !xCell->isMerged() && isValid(rPos) ) |
112 | 0 | { |
113 | 0 | const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) ); |
114 | 0 | const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB); |
115 | |
|
116 | 0 | if( (rPos.mnCol < static_cast<sal_Int32>(maColumns.size())) && (rPos.mnRow < static_cast<sal_Int32>(maRows.size()) ) ) |
117 | 0 | { |
118 | 0 | const sal_Int32 y = maRows[rPos.mnRow].mnPos; |
119 | |
|
120 | 0 | sal_Int32 endy; |
121 | 0 | if (o3tl::checked_add(y, aCellSize.getY(), endy)) |
122 | 0 | return false; |
123 | | |
124 | 0 | if(bRTL) |
125 | 0 | { |
126 | | ///For RTL Table Calculate the Right End of cell instead of Left |
127 | 0 | const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize; |
128 | 0 | sal_Int32 startx; |
129 | 0 | if (o3tl::checked_sub(x, aCellSize.getX(), startx)) |
130 | 0 | return false; |
131 | 0 | rArea = basegfx::B2IRectangle(startx, y, x, endy); |
132 | 0 | } |
133 | 0 | else |
134 | 0 | { |
135 | 0 | const sal_Int32 x = maColumns[rPos.mnCol].mnPos; |
136 | 0 | sal_Int32 endx; |
137 | 0 | if (o3tl::checked_add(x, aCellSize.getX(), endx)) |
138 | 0 | return false; |
139 | 0 | rArea = basegfx::B2IRectangle(x, y, endx, endy); |
140 | 0 | } |
141 | 0 | return true; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | 0 | catch( Exception& ) |
146 | 0 | { |
147 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
148 | 0 | } |
149 | 0 | return false; |
150 | 0 | } |
151 | | |
152 | | |
153 | | sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const |
154 | 0 | { |
155 | 0 | if( isValidRow(nRow) ) |
156 | 0 | return maRows[nRow].mnSize; |
157 | 0 | else |
158 | 0 | return 0; |
159 | 0 | } |
160 | | |
161 | | |
162 | | sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const |
163 | 0 | { |
164 | 0 | if( isValidColumn(nColumn) ) |
165 | 0 | return maColumns[nColumn].mnSize; |
166 | 0 | else |
167 | 0 | return 0; |
168 | 0 | } |
169 | | |
170 | | sal_Int32 TableLayouter::calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const |
171 | 0 | { |
172 | 0 | sal_Int32 nRet = 0; |
173 | 0 | for ( sal_uInt32 nRow = 0; nRow < static_cast<sal_uInt32>(maRows.size()); ++nRow ) |
174 | 0 | { |
175 | | // Account for the space desired by spanned columns. |
176 | | // Only the last spanned cell will try to ensure sufficient space, |
177 | | // by looping through the previous columns, subtracting their portion. |
178 | 0 | sal_Int32 nWish = 0; |
179 | 0 | sal_Int32 nSpannedColumn = nColumn; |
180 | 0 | bool bFindSpan = true; |
181 | 0 | while ( bFindSpan && isValidColumn(nSpannedColumn) ) |
182 | 0 | { |
183 | | // recursive function call gets earlier portion of spanned column. |
184 | 0 | if ( nSpannedColumn < nColumn ) |
185 | 0 | nWish -= calcPreferredColumnWidth( nSpannedColumn, aSize ); |
186 | |
|
187 | 0 | CellRef xCell( getCell( CellPos( nSpannedColumn, nRow ) ) ); |
188 | 0 | if ( xCell.is() && !xCell->isMerged() |
189 | 0 | && (xCell->getColumnSpan() == 1 || nSpannedColumn < nColumn) ) |
190 | 0 | { |
191 | 0 | nWish += xCell->calcPreferredWidth(aSize); |
192 | 0 | bFindSpan = false; |
193 | 0 | } |
194 | 0 | else if ( xCell.is() && xCell->isMerged() |
195 | 0 | && nColumn == nSpannedColumn |
196 | 0 | && isValidColumn(nColumn + 1) ) |
197 | 0 | { |
198 | 0 | xCell = getCell( CellPos( nColumn + 1, nRow ) ); |
199 | 0 | bFindSpan = xCell.is() && !xCell->isMerged(); |
200 | 0 | } |
201 | 0 | nSpannedColumn--; |
202 | 0 | } |
203 | 0 | nRet = std::max( nRet, nWish ); |
204 | 0 | } |
205 | 0 | return nRet; |
206 | 0 | } |
207 | | |
208 | | |
209 | | bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const |
210 | 0 | { |
211 | 0 | const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders; |
212 | |
|
213 | 0 | if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) && |
214 | 0 | (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) ) |
215 | 0 | { |
216 | 0 | return rMap[nEdgeX][nEdgeY] != nullptr; |
217 | 0 | } |
218 | 0 | else |
219 | 0 | { |
220 | 0 | OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" ); |
221 | 0 | } |
222 | | |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | | |
227 | | /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */ |
228 | | SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const |
229 | 0 | { |
230 | 0 | SvxBorderLine* pLine = nullptr; |
231 | |
|
232 | 0 | const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders; |
233 | |
|
234 | 0 | if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) && |
235 | 0 | (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) ) |
236 | 0 | { |
237 | 0 | pLine = rMap[nEdgeX][nEdgeY]; |
238 | 0 | if( pLine == &gEmptyBorder ) |
239 | 0 | pLine = nullptr; |
240 | 0 | } |
241 | 0 | else |
242 | 0 | { |
243 | 0 | OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" ); |
244 | 0 | } |
245 | |
|
246 | 0 | return pLine; |
247 | 0 | } |
248 | | |
249 | | std::vector<EdgeInfo> TableLayouter::getHorizontalEdges() |
250 | 0 | { |
251 | 0 | std::vector<EdgeInfo> aReturn; |
252 | 0 | sal_Int32 nRowSize = sal_Int32(maRows.size()); |
253 | 0 | for (sal_Int32 i = 0; i <= nRowSize; i++) |
254 | 0 | { |
255 | 0 | sal_Int32 nEdgeMin = 0; |
256 | 0 | sal_Int32 nEdgeMax = 0; |
257 | 0 | sal_Int32 nEdge = getHorizontalEdge(i, &nEdgeMin, &nEdgeMax); |
258 | 0 | nEdgeMin -= nEdge; |
259 | 0 | nEdgeMax -= nEdge; |
260 | 0 | aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax); |
261 | 0 | } |
262 | 0 | return aReturn; |
263 | 0 | } |
264 | | |
265 | | std::vector<EdgeInfo> TableLayouter::getVerticalEdges() |
266 | 0 | { |
267 | 0 | std::vector<EdgeInfo> aReturn; |
268 | 0 | sal_Int32 nColumnSize = sal_Int32(maColumns.size()); |
269 | 0 | for (sal_Int32 i = 0; i <= nColumnSize; i++) |
270 | 0 | { |
271 | 0 | sal_Int32 nEdgeMin = 0; |
272 | 0 | sal_Int32 nEdgeMax = 0; |
273 | 0 | sal_Int32 nEdge = getVerticalEdge(i, &nEdgeMin, &nEdgeMax); |
274 | 0 | nEdgeMin -= nEdge; |
275 | 0 | nEdgeMax -= nEdge; |
276 | 0 | aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax); |
277 | 0 | } |
278 | 0 | return aReturn; |
279 | 0 | } |
280 | | |
281 | | sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ ) |
282 | 0 | { |
283 | 0 | sal_Int32 nRet = 0; |
284 | 0 | const sal_Int32 nRowCount = getRowCount(); |
285 | 0 | if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) ) |
286 | 0 | nRet = maRows[std::min(static_cast<sal_Int32>(nEdgeY),nRowCount-1)].mnPos; |
287 | |
|
288 | 0 | if( nEdgeY == nRowCount ) |
289 | 0 | nRet += maRows[nEdgeY - 1].mnSize; |
290 | |
|
291 | 0 | if( pnMin ) |
292 | 0 | { |
293 | 0 | if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) ) |
294 | 0 | { |
295 | 0 | *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo |
296 | 0 | } |
297 | 0 | else |
298 | 0 | { |
299 | 0 | *pnMin = nRet; |
300 | 0 | } |
301 | 0 | } |
302 | |
|
303 | 0 | if( pnMax ) |
304 | 0 | { |
305 | 0 | *pnMax = 0x0fffffff; |
306 | 0 | } |
307 | 0 | return nRet; |
308 | 0 | } |
309 | | |
310 | | |
311 | | sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ ) |
312 | 0 | { |
313 | 0 | sal_Int32 nRet = 0; |
314 | |
|
315 | 0 | const sal_Int32 nColCount = getColumnCount(); |
316 | 0 | if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) ) |
317 | 0 | nRet = maColumns[std::min(static_cast<sal_Int32>(nEdgeX),nColCount-1)].mnPos; |
318 | |
|
319 | 0 | const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB); |
320 | 0 | if( bRTL ) |
321 | 0 | { |
322 | 0 | if( (nEdgeX >= 0) && (nEdgeX < nColCount) ) |
323 | 0 | nRet += maColumns[nEdgeX].mnSize; |
324 | 0 | } |
325 | 0 | else |
326 | 0 | { |
327 | 0 | if( nEdgeX == nColCount ) |
328 | 0 | nRet += maColumns[nEdgeX - 1].mnSize; |
329 | 0 | } |
330 | |
|
331 | 0 | if( pnMin ) |
332 | 0 | { |
333 | 0 | *pnMin = nRet; |
334 | 0 | if( bRTL ) |
335 | 0 | { |
336 | 0 | if( nEdgeX < nColCount ) |
337 | 0 | *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX); |
338 | 0 | } |
339 | 0 | else |
340 | 0 | { |
341 | 0 | if( (nEdgeX > 0) && (nEdgeX <= nColCount ) ) |
342 | 0 | *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 ); |
343 | 0 | } |
344 | 0 | } |
345 | |
|
346 | 0 | if( pnMax ) |
347 | 0 | { |
348 | 0 | *pnMax = 0x0fffffff; // todo |
349 | 0 | if( bRTL ) |
350 | 0 | { |
351 | 0 | if( nEdgeX > 0 ) |
352 | 0 | *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 ); |
353 | 0 | } |
354 | 0 | else |
355 | 0 | { |
356 | 0 | if( (nEdgeX >= 0) && (nEdgeX < nColCount ) ) |
357 | 0 | *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX ); |
358 | 0 | } |
359 | 0 | } |
360 | |
|
361 | 0 | return nRet; |
362 | 0 | } |
363 | | |
364 | | |
365 | | static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning ) |
366 | 364k | { |
367 | 364k | Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY ); |
368 | 364k | if( xCell.is() && !xCell->isMerged() ) |
369 | 51.5k | { |
370 | 51.5k | const sal_Int32 nRight = xCell->getColumnSpan() + nCellX; |
371 | 51.5k | const sal_Int32 nBottom = xCell->getRowSpan() + nCellY; |
372 | 51.5k | if( (nMergedX < nRight) && (nMergedY < nBottom) ) |
373 | 26.7k | return true; |
374 | | |
375 | 24.8k | bRunning = false; |
376 | 24.8k | } |
377 | 337k | return false; |
378 | 364k | } |
379 | | |
380 | | /** returns true if the cell(nMergedX,nMergedY) is merged with other cells. |
381 | | the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge. |
382 | | */ |
383 | | bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY ) |
384 | 26.7k | { |
385 | 26.7k | rOriginX = nMergedX; |
386 | 26.7k | rOriginY = nMergedY; |
387 | | |
388 | 26.7k | if( xTable.is() ) try |
389 | 26.7k | { |
390 | | // check if this cell already the origin or not merged at all |
391 | 26.7k | Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW ); |
392 | 26.7k | if( !xCell->isMerged() ) |
393 | 0 | return true; |
394 | | |
395 | 26.7k | bool bCheckVert = true; |
396 | 26.7k | bool bCheckHorz = true; |
397 | | |
398 | 26.7k | sal_Int32 nMinCol = 0; |
399 | 26.7k | sal_Int32 nMinRow = 0; |
400 | | |
401 | 26.7k | sal_Int32 nStep = 1, i; |
402 | | |
403 | 26.7k | sal_Int32 nRow, nCol; |
404 | 26.7k | do |
405 | 102k | { |
406 | 102k | if( bCheckVert ) |
407 | 85.4k | { |
408 | 85.4k | nRow = nMergedY - nStep; |
409 | 85.4k | if( nRow >= nMinRow ) |
410 | 84.5k | { |
411 | 84.5k | nCol = nMergedX; |
412 | 276k | for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- ) |
413 | 224k | { |
414 | 224k | if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) ) |
415 | 15.3k | { |
416 | 15.3k | rOriginX = nCol; rOriginY = nRow; |
417 | 15.3k | return true; |
418 | 15.3k | } |
419 | | |
420 | 209k | if( !bCheckVert ) |
421 | 17.7k | { |
422 | 17.7k | if( nCol == nMergedX ) |
423 | 9.40k | { |
424 | 9.40k | nMinRow = nRow+1; |
425 | 9.40k | } |
426 | 8.30k | else |
427 | 8.30k | { |
428 | 8.30k | bCheckVert = true; |
429 | 8.30k | } |
430 | 17.7k | break; |
431 | 17.7k | } |
432 | 209k | } |
433 | 84.5k | } |
434 | 927 | else |
435 | 927 | { |
436 | 927 | bCheckVert = false; |
437 | 927 | } |
438 | 85.4k | } |
439 | | |
440 | 87.5k | if( bCheckHorz ) |
441 | 67.0k | { |
442 | 67.0k | nCol = nMergedX - nStep; |
443 | 67.0k | if( nCol >= nMinCol ) |
444 | 66.5k | { |
445 | 66.5k | nRow = nMergedY; |
446 | 187k | for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- ) |
447 | 139k | { |
448 | 139k | if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) ) |
449 | 11.3k | { |
450 | 11.3k | rOriginX = nCol; rOriginY = nRow; |
451 | 11.3k | return true; |
452 | 11.3k | } |
453 | | |
454 | 128k | if( !bCheckHorz ) |
455 | 7.15k | { |
456 | 7.15k | if( nRow == nMergedY ) |
457 | 6.81k | { |
458 | 6.81k | nMinCol = nCol+1; |
459 | 6.81k | } |
460 | 345 | else |
461 | 345 | { |
462 | 345 | bCheckHorz = true; |
463 | 345 | } |
464 | 7.15k | break; |
465 | 7.15k | } |
466 | 128k | } |
467 | 66.5k | } |
468 | 545 | else |
469 | 545 | { |
470 | 545 | bCheckHorz = false; |
471 | 545 | } |
472 | 67.0k | } |
473 | 76.2k | nStep++; |
474 | 76.2k | } |
475 | 76.2k | while( bCheckVert || bCheckHorz ); |
476 | 26.7k | } |
477 | 26.7k | catch( Exception& ) |
478 | 26.7k | { |
479 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
480 | 0 | } |
481 | 0 | return false; |
482 | 26.7k | } |
483 | | |
484 | | |
485 | | sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn ) |
486 | 0 | { |
487 | 0 | if( isValidColumn( nColumn ) ) |
488 | 0 | { |
489 | 0 | return maColumns[nColumn].mnMinSize; |
490 | 0 | } |
491 | 0 | else |
492 | 0 | { |
493 | 0 | OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" ); |
494 | 0 | return 0; |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | |
499 | | sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute ) |
500 | 0 | { |
501 | | // break loops after 100 runs to avoid freezing office due to developer error |
502 | 0 | sal_Int32 nSafe = 100; |
503 | |
|
504 | 0 | const std::size_t nCount = rLayouts.size(); |
505 | 0 | std::size_t nIndex; |
506 | |
|
507 | 0 | bool bConstrainsBroken = false; |
508 | |
|
509 | 0 | do |
510 | 0 | { |
511 | 0 | bConstrainsBroken = false; |
512 | | |
513 | | // first enforce minimum size constrains on all entities |
514 | 0 | for( nIndex = 0; nIndex < nCount; ++nIndex ) |
515 | 0 | { |
516 | 0 | Layout& rLayout = rLayouts[nIndex]; |
517 | 0 | if( rLayout.mnSize < rLayout.mnMinSize ) |
518 | 0 | { |
519 | 0 | sal_Int32 nDiff(0); |
520 | 0 | bConstrainsBroken |= o3tl::checked_sub(rLayout.mnMinSize, rLayout.mnSize, nDiff); |
521 | 0 | bConstrainsBroken |= o3tl::checked_sub(nDistribute, nDiff, nDistribute); |
522 | 0 | rLayout.mnSize = rLayout.mnMinSize; |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | | // calculate current width |
527 | | // if nDistribute is < 0 (shrinking), entities that are already |
528 | | // at minimum width are not counted |
529 | 0 | sal_Int32 nCurrentWidth = 0; |
530 | 0 | for( nIndex = 0; nIndex < nCount; ++nIndex ) |
531 | 0 | { |
532 | 0 | Layout& rLayout = rLayouts[nIndex]; |
533 | 0 | if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) ) |
534 | 0 | nCurrentWidth = o3tl::saturating_add(nCurrentWidth, rLayout.mnSize); |
535 | 0 | } |
536 | | |
537 | | // now distribute over entities |
538 | 0 | if( (nCurrentWidth != 0) && (nDistribute != 0) ) |
539 | 0 | { |
540 | 0 | sal_Int32 nDistributed = nDistribute; |
541 | 0 | for( nIndex = 0; nIndex < nCount; ++nIndex ) |
542 | 0 | { |
543 | 0 | Layout& rLayout = rLayouts[nIndex]; |
544 | 0 | if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) ) |
545 | 0 | { |
546 | 0 | sal_Int32 n(nDistributed); // for last entity use up rest |
547 | 0 | if (nIndex != (nCount-1)) |
548 | 0 | { |
549 | 0 | bConstrainsBroken |= o3tl::checked_multiply(nDistribute, rLayout.mnSize, n); |
550 | 0 | n /= nCurrentWidth; |
551 | 0 | } |
552 | |
|
553 | 0 | bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize); |
554 | 0 | nDistributed -= n; |
555 | |
|
556 | 0 | if( rLayout.mnSize < rLayout.mnMinSize ) |
557 | 0 | bConstrainsBroken = true; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | } |
561 | 0 | } while( bConstrainsBroken && --nSafe ); |
562 | |
|
563 | 0 | sal_Int32 nSize = 0; |
564 | 0 | for( nIndex = 0; nIndex < nCount; ++nIndex ) |
565 | 0 | nSize = o3tl::saturating_add(nSize, rLayouts[nIndex].mnSize); |
566 | |
|
567 | 0 | return nSize; |
568 | 0 | } |
569 | | |
570 | | |
571 | | typedef std::vector< CellRef > MergeableCellVector; |
572 | | typedef std::vector< MergeableCellVector > MergeVector; |
573 | | |
574 | | |
575 | | void TableLayouter::LayoutTableWidth( tools::Rectangle& rArea, bool bFit ) |
576 | 0 | { |
577 | 0 | const sal_Int32 nColCount = getColumnCount(); |
578 | 0 | const sal_Int32 nRowCount = getRowCount(); |
579 | 0 | if( nColCount == 0 ) |
580 | 0 | return; |
581 | | |
582 | 0 | MergeVector aMergedCells( nColCount ); |
583 | 0 | std::vector<sal_Int32> aOptimalColumns; |
584 | |
|
585 | 0 | static constexpr OUStringLiteral sOptimalSize(u"OptimalSize"); |
586 | |
|
587 | 0 | if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount ) |
588 | 0 | maColumns.resize( nColCount ); |
589 | |
|
590 | 0 | Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW ); |
591 | | |
592 | | // first calculate current width and initial minimum width per column, |
593 | | // merged cells will be counted later |
594 | 0 | sal_Int32 nCurrentWidth = 0; |
595 | 0 | sal_Int32 nCol = 0, nRow = 0; |
596 | 0 | for( nCol = 0; nCol < nColCount; nCol++ ) |
597 | 0 | { |
598 | 0 | sal_Int32 nMinWidth = 0; |
599 | |
|
600 | 0 | bool bIsEmpty = true; // check if all cells in this column are merged |
601 | |
|
602 | 0 | for( nRow = 0; nRow < nRowCount; ++nRow ) |
603 | 0 | { |
604 | 0 | CellRef xCell( getCell( CellPos( nCol, nRow ) ) ); |
605 | 0 | if( xCell.is() && !xCell->isMerged() ) |
606 | 0 | { |
607 | 0 | bIsEmpty = false; |
608 | |
|
609 | 0 | sal_Int32 nColSpan = xCell->getColumnSpan(); |
610 | 0 | if( nColSpan > 1 ) |
611 | 0 | { |
612 | | // merged cells will be evaluated later |
613 | 0 | aMergedCells[nCol+nColSpan-1].push_back( xCell ); |
614 | 0 | } |
615 | 0 | else |
616 | 0 | { |
617 | 0 | nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() ); |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } |
621 | |
|
622 | 0 | maColumns[nCol].mnMinSize = nMinWidth; |
623 | |
|
624 | 0 | if( bIsEmpty ) |
625 | 0 | { |
626 | 0 | maColumns[nCol].mnSize = 0; |
627 | 0 | } |
628 | 0 | else |
629 | 0 | { |
630 | 0 | sal_Int32 nColWidth = 0; |
631 | 0 | Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW ); |
632 | 0 | bool bOptimal = false; |
633 | 0 | xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal; |
634 | 0 | if( bOptimal ) |
635 | 0 | { |
636 | 0 | aOptimalColumns.push_back(nCol); |
637 | 0 | } |
638 | 0 | else |
639 | 0 | { |
640 | 0 | xColSet->getPropertyValue( gsSize ) >>= nColWidth; |
641 | 0 | } |
642 | |
|
643 | 0 | maColumns[nCol].mnSize = std::max( nColWidth, nMinWidth); |
644 | |
|
645 | 0 | nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize); |
646 | 0 | } |
647 | 0 | } |
648 | | |
649 | | // if we have optimal sized rows, distribute what is given (left) |
650 | 0 | if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getOpenWidth()) ) |
651 | 0 | { |
652 | 0 | sal_Int32 nLeft = rArea.getOpenWidth() - nCurrentWidth; |
653 | 0 | sal_Int32 nDistribute = nLeft / aOptimalColumns.size(); |
654 | |
|
655 | 0 | auto iter( aOptimalColumns.begin() ); |
656 | 0 | while( iter != aOptimalColumns.end() ) |
657 | 0 | { |
658 | 0 | sal_Int32 nOptCol = *iter++; |
659 | 0 | if( iter == aOptimalColumns.end() ) |
660 | 0 | nDistribute = nLeft; |
661 | |
|
662 | 0 | maColumns[nOptCol].mnSize += nDistribute; |
663 | 0 | nLeft -= nDistribute; |
664 | 0 | } |
665 | |
|
666 | 0 | DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" ); |
667 | 0 | } |
668 | | |
669 | | // now check if merged cells fit |
670 | 0 | for( nCol = 1; nCol < nColCount; ++nCol ) |
671 | 0 | { |
672 | 0 | bool bChanges = false; |
673 | |
|
674 | 0 | const sal_Int32 nOldSize = maColumns[nCol].mnSize; |
675 | |
|
676 | 0 | for( const CellRef& xCell : aMergedCells[nCol] ) |
677 | 0 | { |
678 | 0 | sal_Int32 nMinWidth = xCell->getMinimumWidth(); |
679 | |
|
680 | 0 | for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol ) |
681 | 0 | nMinWidth -= maColumns[nMCol].mnSize; |
682 | |
|
683 | 0 | if( nMinWidth > maColumns[nCol].mnMinSize ) |
684 | 0 | maColumns[nCol].mnMinSize = nMinWidth; |
685 | |
|
686 | 0 | if( nMinWidth > maColumns[nCol].mnSize ) |
687 | 0 | { |
688 | 0 | maColumns[nCol].mnSize = nMinWidth; |
689 | 0 | bChanges = true; |
690 | 0 | } |
691 | 0 | } |
692 | |
|
693 | 0 | if( bChanges ) |
694 | 0 | { |
695 | 0 | nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize - nOldSize); |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | // now scale if wanted and needed |
700 | 0 | if( bFit && (nCurrentWidth != rArea.getOpenWidth()) ) |
701 | 0 | distribute( maColumns, rArea.getOpenWidth() - nCurrentWidth ); |
702 | | |
703 | | // last step, update left edges |
704 | 0 | sal_Int32 nNewWidth = 0; |
705 | |
|
706 | 0 | const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB); |
707 | 0 | RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL ); |
708 | 0 | while( coliter.next(nCol ) ) |
709 | 0 | { |
710 | 0 | maColumns[nCol].mnPos = nNewWidth; |
711 | 0 | nNewWidth = o3tl::saturating_add(nNewWidth, maColumns[nCol].mnSize); |
712 | 0 | if( bFit ) |
713 | 0 | { |
714 | 0 | Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW ); |
715 | 0 | xColSet->setPropertyValue( gsSize, Any( maColumns[nCol].mnSize ) ); |
716 | 0 | } |
717 | 0 | } |
718 | |
|
719 | 0 | rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) ); |
720 | 0 | updateCells( rArea ); |
721 | 0 | } |
722 | | |
723 | | |
724 | | void TableLayouter::LayoutTableHeight( tools::Rectangle& rArea, bool bFit ) |
725 | 159 | { |
726 | 159 | const sal_Int32 nColCount = getColumnCount(); |
727 | 159 | const sal_Int32 nRowCount = getRowCount(); |
728 | | |
729 | 159 | if( nRowCount == 0 ) |
730 | 159 | return; |
731 | | |
732 | 0 | Reference< XTableRows > xRows( mxTable->getRows() ); |
733 | |
|
734 | 0 | MergeVector aMergedCells( nRowCount ); |
735 | 0 | std::vector<sal_Int32> aOptimalRows; |
736 | |
|
737 | 0 | static constexpr OUStringLiteral sOptimalSize(u"OptimalSize"); |
738 | | |
739 | | // first calculate current height and initial minimum size per column, |
740 | | // merged cells will be counted later |
741 | 0 | sal_Int32 nCurrentHeight = 0; |
742 | 0 | sal_Int32 nCol, nRow; |
743 | 0 | for( nRow = 0; nRow < nRowCount; ++nRow ) |
744 | 0 | { |
745 | 0 | sal_Int32 nMinHeight = 0; |
746 | |
|
747 | 0 | bool bIsEmpty = true; // check if all cells in this row are merged |
748 | |
|
749 | 0 | for( nCol = 0; nCol < nColCount; ++nCol ) |
750 | 0 | { |
751 | 0 | CellRef xCell( getCell( CellPos( nCol, nRow ) ) ); |
752 | 0 | if( xCell.is() && !xCell->isMerged() ) |
753 | 0 | { |
754 | 0 | bIsEmpty = false; |
755 | |
|
756 | 0 | sal_Int32 nRowSpan = xCell->getRowSpan(); |
757 | 0 | if( nRowSpan > 1 ) |
758 | 0 | { |
759 | | // merged cells will be evaluated later |
760 | 0 | aMergedCells[nRow+nRowSpan-1].push_back( xCell ); |
761 | 0 | } |
762 | 0 | else |
763 | 0 | { |
764 | 0 | nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() ); |
765 | 0 | } |
766 | 0 | } |
767 | 0 | } |
768 | |
|
769 | 0 | maRows[nRow].mnMinSize = nMinHeight; |
770 | |
|
771 | 0 | if( bIsEmpty ) |
772 | 0 | { |
773 | 0 | maRows[nRow].mnSize = 0; |
774 | 0 | } |
775 | 0 | else |
776 | 0 | { |
777 | 0 | sal_Int32 nRowHeight = 0; |
778 | 0 | Reference<XPropertySet> xRowSet(xRows->getByIndex(nRow), UNO_QUERY_THROW); |
779 | |
|
780 | 0 | bool bOptimal = false; |
781 | 0 | xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal; |
782 | 0 | if( bOptimal ) |
783 | 0 | { |
784 | 0 | aOptimalRows.push_back( nRow ); |
785 | 0 | } |
786 | 0 | else |
787 | 0 | { |
788 | 0 | xRowSet->getPropertyValue( gsSize ) >>= nRowHeight; |
789 | 0 | } |
790 | |
|
791 | 0 | maRows[nRow].mnSize = nRowHeight; |
792 | |
|
793 | 0 | if( maRows[nRow].mnSize < nMinHeight ) |
794 | 0 | maRows[nRow].mnSize = nMinHeight; |
795 | |
|
796 | 0 | nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize); |
797 | 0 | } |
798 | 0 | } |
799 | | |
800 | | // if we have optimal sized rows, distribute what is given (left) |
801 | 0 | if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getOpenHeight()) ) |
802 | 0 | { |
803 | 0 | sal_Int32 nLeft = rArea.getOpenHeight() - nCurrentHeight; |
804 | 0 | sal_Int32 nDistribute = nLeft / aOptimalRows.size(); |
805 | |
|
806 | 0 | auto iter( aOptimalRows.begin() ); |
807 | 0 | while( iter != aOptimalRows.end() ) |
808 | 0 | { |
809 | 0 | sal_Int32 nOptRow = *iter++; |
810 | 0 | if( iter == aOptimalRows.end() ) |
811 | 0 | nDistribute = nLeft; |
812 | |
|
813 | 0 | maRows[nOptRow].mnSize += nDistribute; |
814 | 0 | nLeft -= nDistribute; |
815 | |
|
816 | 0 | } |
817 | |
|
818 | 0 | DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" ); |
819 | 0 | } |
820 | | |
821 | | // now check if merged cells fit |
822 | 0 | for( nRow = 1; nRow < nRowCount; ++nRow ) |
823 | 0 | { |
824 | 0 | bool bChanges = false; |
825 | 0 | sal_Int32 nOldSize = maRows[nRow].mnSize; |
826 | |
|
827 | 0 | for( const CellRef& xCell : aMergedCells[nRow] ) |
828 | 0 | { |
829 | 0 | sal_Int32 nMinHeight = xCell->getMinimumHeight(); |
830 | |
|
831 | 0 | for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow ) |
832 | 0 | nMinHeight -= maRows[nMRow].mnSize; |
833 | |
|
834 | 0 | if( nMinHeight > maRows[nRow].mnMinSize ) |
835 | 0 | maRows[nRow].mnMinSize = nMinHeight; |
836 | |
|
837 | 0 | if( nMinHeight > maRows[nRow].mnSize ) |
838 | 0 | { |
839 | 0 | maRows[nRow].mnSize = nMinHeight; |
840 | 0 | bChanges = true; |
841 | 0 | } |
842 | 0 | } |
843 | 0 | if( bChanges ) |
844 | 0 | nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize - nOldSize); |
845 | 0 | } |
846 | | |
847 | | // now scale if wanted and needed |
848 | 0 | if( bFit && nCurrentHeight != rArea.getOpenHeight() ) |
849 | 0 | distribute(maRows, o3tl::saturating_sub<sal_Int32>(rArea.getOpenHeight(), nCurrentHeight)); |
850 | | |
851 | | // last step, update left edges |
852 | 0 | sal_Int32 nNewHeight = 0; |
853 | 0 | for( nRow = 0; nRow < nRowCount; ++nRow ) |
854 | 0 | { |
855 | 0 | maRows[nRow].mnPos = nNewHeight; |
856 | 0 | nNewHeight = o3tl::saturating_add(nNewHeight, maRows[nRow].mnSize); |
857 | |
|
858 | 0 | if( bFit ) |
859 | 0 | { |
860 | 0 | Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW ); |
861 | 0 | xRowSet->setPropertyValue( gsSize, Any( maRows[nRow].mnSize ) ); |
862 | 0 | } |
863 | 0 | } |
864 | |
|
865 | 0 | rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) ); |
866 | 0 | updateCells( rArea ); |
867 | 0 | } |
868 | | |
869 | | |
870 | | /** try to fit the table into the given rectangle. |
871 | | If the rectangle is too small, it will be grown to fit the table. */ |
872 | | void TableLayouter::LayoutTable( tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight ) |
873 | 0 | { |
874 | 0 | if( !mxTable.is() ) |
875 | 0 | return; |
876 | | |
877 | 0 | const sal_Int32 nRowCount = mxTable->getRowCount(); |
878 | 0 | const sal_Int32 nColCount = mxTable->getColumnCount(); |
879 | |
|
880 | 0 | if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) ) |
881 | 0 | { |
882 | 0 | if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount ) |
883 | 0 | maRows.resize( nRowCount ); |
884 | |
|
885 | 0 | for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) |
886 | 0 | maRows[nRow].clear(); |
887 | |
|
888 | 0 | if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount ) |
889 | 0 | maColumns.resize( nColCount ); |
890 | |
|
891 | 0 | for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) |
892 | 0 | maColumns[nCol].clear(); |
893 | 0 | } |
894 | |
|
895 | 0 | LayoutTableWidth( rRectangle, bFitWidth ); |
896 | 0 | LayoutTableHeight( rRectangle, bFitHeight ); |
897 | 0 | UpdateBorderLayout(); |
898 | 0 | } |
899 | | |
900 | | |
901 | | void TableLayouter::updateCells( tools::Rectangle const & rRectangle ) |
902 | 191 | { |
903 | 191 | const sal_Int32 nColCount = getColumnCount(); |
904 | 191 | const sal_Int32 nRowCount = getRowCount(); |
905 | | |
906 | 191 | CellPos aPos; |
907 | 191 | for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ ) |
908 | 0 | { |
909 | 0 | for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ ) |
910 | 0 | { |
911 | 0 | CellRef xCell( getCell( aPos ) ); |
912 | 0 | if( xCell.is() ) |
913 | 0 | { |
914 | 0 | basegfx::B2IRectangle aCellArea; |
915 | 0 | if( getCellArea( xCell, aPos, aCellArea ) ) |
916 | 0 | { |
917 | 0 | tools::Rectangle aCellRect; |
918 | 0 | aCellRect.SetLeft( aCellArea.getMinX() ); |
919 | 0 | aCellRect.SetRight( aCellArea.getMaxX() ); |
920 | 0 | aCellRect.SetTop( aCellArea.getMinY() ); |
921 | 0 | aCellRect.SetBottom( aCellArea.getMaxY() ); |
922 | 0 | aCellRect.Move( rRectangle.Left(), rRectangle.Top() ); |
923 | 0 | xCell->setCellRect( aCellRect ); |
924 | 0 | } |
925 | 0 | } |
926 | 0 | } |
927 | 0 | } |
928 | 191 | } |
929 | | |
930 | | |
931 | | CellRef TableLayouter::getCell( const CellPos& rPos ) const |
932 | 0 | { |
933 | 0 | CellRef xCell; |
934 | 0 | if( mxTable.is() ) try |
935 | 0 | { |
936 | 0 | xCell = mxTable->getCell( rPos.mnCol, rPos.mnRow ); |
937 | 0 | } |
938 | 0 | catch( Exception& ) |
939 | 0 | { |
940 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
941 | 0 | } |
942 | 0 | return xCell; |
943 | 0 | } |
944 | | |
945 | | |
946 | | bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther ) |
947 | 0 | { |
948 | 0 | if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr))) |
949 | 0 | return false; |
950 | 0 | if (!pOther || (pOther == &gEmptyBorder)) |
951 | 0 | return true; |
952 | | |
953 | 0 | sal_uInt16 nThisSize = pThis->GetScaledWidth(); |
954 | 0 | sal_uInt16 nOtherSize = pOther->GetScaledWidth(); |
955 | |
|
956 | 0 | if (nThisSize > nOtherSize) |
957 | 0 | return true; |
958 | | |
959 | 0 | else if (nThisSize < nOtherSize) |
960 | 0 | { |
961 | 0 | return false; |
962 | 0 | } |
963 | 0 | else |
964 | 0 | { |
965 | 0 | if ( pOther->GetInWidth() && !pThis->GetInWidth() ) |
966 | 0 | { |
967 | 0 | return true; |
968 | 0 | } |
969 | 0 | else if ( pThis->GetInWidth() && !pOther->GetInWidth() ) |
970 | 0 | { |
971 | 0 | return false; |
972 | 0 | } |
973 | 0 | else |
974 | 0 | { |
975 | 0 | return true; //! ??? |
976 | 0 | } |
977 | 0 | } |
978 | 0 | } |
979 | | |
980 | | void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine ) |
981 | 0 | { |
982 | 0 | if (!pLine) |
983 | 0 | pLine = &gEmptyBorder; |
984 | |
|
985 | 0 | BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders; |
986 | |
|
987 | 0 | if( (nCol >= 0) && (nCol < sal::static_int_cast<sal_Int32>(rMap.size())) && |
988 | 0 | (nRow >= 0) && (nRow < sal::static_int_cast<sal_Int32>(rMap[nCol].size())) ) |
989 | 0 | { |
990 | 0 | SvxBorderLine *pOld = rMap[nCol][nRow]; |
991 | |
|
992 | 0 | if (HasPriority(pLine, pOld)) |
993 | 0 | { |
994 | 0 | if (pOld && pOld != &gEmptyBorder) |
995 | 0 | delete pOld; |
996 | |
|
997 | 0 | SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder; |
998 | |
|
999 | 0 | rMap[nCol][nRow] = pNew; |
1000 | 0 | } |
1001 | 0 | } |
1002 | 0 | else |
1003 | 0 | { |
1004 | 0 | OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" ); |
1005 | 0 | } |
1006 | 0 | } |
1007 | | |
1008 | | void TableLayouter::ClearBorderLayout() |
1009 | 2.34k | { |
1010 | 2.34k | ClearBorderLayout(maHorizontalBorders); |
1011 | 2.34k | ClearBorderLayout(maVerticalBorders); |
1012 | 2.34k | } |
1013 | | |
1014 | | void TableLayouter::ClearBorderLayout(BorderLineMap& rMap) |
1015 | 4.68k | { |
1016 | 4.68k | const sal_Int32 nColCount = rMap.size(); |
1017 | | |
1018 | 4.68k | for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) |
1019 | 0 | { |
1020 | 0 | const sal_Int32 nRowCount = rMap[nCol].size(); |
1021 | 0 | for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) |
1022 | 0 | { |
1023 | 0 | SvxBorderLine* pLine = rMap[nCol][nRow]; |
1024 | 0 | if( pLine ) |
1025 | 0 | { |
1026 | 0 | if( pLine != &gEmptyBorder ) |
1027 | 0 | delete pLine; |
1028 | |
|
1029 | 0 | rMap[nCol][nRow] = nullptr; |
1030 | 0 | } |
1031 | 0 | } |
1032 | 0 | } |
1033 | 4.68k | } |
1034 | | |
1035 | | void TableLayouter::ResizeBorderLayout() |
1036 | 0 | { |
1037 | 0 | ClearBorderLayout(); |
1038 | 0 | ResizeBorderLayout(maHorizontalBorders); |
1039 | 0 | ResizeBorderLayout(maVerticalBorders); |
1040 | 0 | } |
1041 | | |
1042 | | |
1043 | | void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap ) |
1044 | 0 | { |
1045 | 0 | const sal_Int32 nColCount = getColumnCount() + 1; |
1046 | 0 | const sal_Int32 nRowCount = getRowCount() + 1; |
1047 | |
|
1048 | 0 | if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount ) |
1049 | 0 | rMap.resize( nColCount ); |
1050 | |
|
1051 | 0 | for( auto& nCol : rMap ) |
1052 | 0 | { |
1053 | 0 | if( sal::static_int_cast<sal_Int32>(nCol.size()) != nRowCount ) |
1054 | 0 | nCol.resize( nRowCount ); |
1055 | 0 | } |
1056 | 0 | } |
1057 | | |
1058 | | |
1059 | | void TableLayouter::UpdateBorderLayout() |
1060 | 0 | { |
1061 | | // make sure old border layout is cleared and border maps have correct size |
1062 | 0 | ResizeBorderLayout(); |
1063 | |
|
1064 | 0 | const sal_Int32 nColCount = getColumnCount(); |
1065 | 0 | const sal_Int32 nRowCount = getRowCount(); |
1066 | |
|
1067 | 0 | CellPos aPos; |
1068 | 0 | for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ ) |
1069 | 0 | { |
1070 | 0 | for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ ) |
1071 | 0 | { |
1072 | 0 | CellRef xCell( getCell( aPos ) ); |
1073 | 0 | if( !xCell.is() ) |
1074 | 0 | continue; |
1075 | | |
1076 | 0 | const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER ); |
1077 | 0 | OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?"); |
1078 | |
|
1079 | 0 | if( !pThisAttr ) |
1080 | 0 | continue; |
1081 | | |
1082 | 0 | const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow; |
1083 | 0 | const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol; |
1084 | |
|
1085 | 0 | for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ ) |
1086 | 0 | { |
1087 | 0 | SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() ); |
1088 | 0 | SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() ); |
1089 | 0 | } |
1090 | |
|
1091 | 0 | for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ ) |
1092 | 0 | { |
1093 | 0 | SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() ); |
1094 | 0 | SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() ); |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 | } |
1098 | 0 | } |
1099 | | |
1100 | | |
1101 | | void TableLayouter::DistributeColumns( ::tools::Rectangle& rArea, |
1102 | | sal_Int32 nFirstCol, |
1103 | | sal_Int32 nLastCol, |
1104 | | const bool bOptimize, |
1105 | | const bool bMinimize ) |
1106 | 0 | { |
1107 | 0 | if( !mxTable.is() ) |
1108 | 0 | return; |
1109 | | |
1110 | 0 | try |
1111 | 0 | { |
1112 | 0 | const sal_Int32 nColCount = getColumnCount(); |
1113 | 0 | Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW ); |
1114 | 0 | const Size aSize(0xffffff, 0xffffff); |
1115 | | |
1116 | | //special case - optimize a single column |
1117 | 0 | if ( (bOptimize || bMinimize) && nFirstCol == nLastCol ) |
1118 | 0 | { |
1119 | 0 | const sal_Int32 nWish = calcPreferredColumnWidth(nFirstCol, aSize); |
1120 | 0 | if ( nWish < getColumnWidth(nFirstCol) ) |
1121 | 0 | { |
1122 | 0 | Reference< XPropertySet > xColSet( xCols->getByIndex(nFirstCol), UNO_QUERY_THROW ); |
1123 | 0 | xColSet->setPropertyValue( gsSize, Any( nWish ) ); |
1124 | | |
1125 | | //FitWidth automatically distributes the new excess space |
1126 | 0 | LayoutTable( rArea, /*bFitWidth=*/!bMinimize, /*bFitHeight=*/false ); |
1127 | 0 | } |
1128 | 0 | } |
1129 | |
|
1130 | 0 | if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) ) |
1131 | 0 | return; |
1132 | | |
1133 | 0 | sal_Int32 nAllWidth = 0; |
1134 | 0 | float fAllWish = 0; |
1135 | 0 | sal_Int32 nUnused = 0; |
1136 | 0 | std::vector<sal_Int32> aWish(nColCount); |
1137 | |
|
1138 | 0 | for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) |
1139 | 0 | nAllWidth += getColumnWidth(nCol); |
1140 | |
|
1141 | 0 | const sal_Int32 nEqualWidth = nAllWidth / (nLastCol-nFirstCol+1); |
1142 | | |
1143 | | //pass 1 - collect unneeded space (from an equal width perspective) |
1144 | 0 | if ( bMinimize || bOptimize ) |
1145 | 0 | { |
1146 | 0 | for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) |
1147 | 0 | { |
1148 | 0 | const sal_Int32 nIndex = nCol - nFirstCol; |
1149 | 0 | aWish[nIndex] = calcPreferredColumnWidth(nCol, aSize); |
1150 | 0 | fAllWish += aWish[nIndex]; |
1151 | 0 | if ( aWish[nIndex] < nEqualWidth ) |
1152 | 0 | nUnused += nEqualWidth - aWish[nIndex]; |
1153 | 0 | } |
1154 | 0 | } |
1155 | 0 | const sal_Int32 nDistributeExcess = nAllWidth - fAllWish; |
1156 | |
|
1157 | 0 | sal_Int32 nWidth = nEqualWidth; |
1158 | 0 | for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) |
1159 | 0 | { |
1160 | 0 | if ( !bMinimize && nCol == nLastCol ) |
1161 | 0 | nWidth = nAllWidth; // last column gets rounding/logic errors |
1162 | 0 | else if ( (bMinimize || bOptimize) && fAllWish ) |
1163 | 0 | { |
1164 | | //pass 2 - first come, first served when requesting from the |
1165 | | // unneeded pool, or proportionally allocate excess. |
1166 | 0 | const sal_Int32 nIndex = nCol - nFirstCol; |
1167 | 0 | if ( aWish[nIndex] > nEqualWidth + nUnused ) |
1168 | 0 | { |
1169 | 0 | nWidth = nEqualWidth + nUnused; |
1170 | 0 | nUnused = 0; |
1171 | 0 | } |
1172 | 0 | else |
1173 | 0 | { |
1174 | 0 | nWidth = aWish[nIndex]; |
1175 | 0 | if ( aWish[nIndex] > nEqualWidth ) |
1176 | 0 | nUnused -= aWish[nIndex] - nEqualWidth; |
1177 | |
|
1178 | 0 | if ( !bMinimize && nDistributeExcess > 0 ) |
1179 | 0 | nWidth += nWidth / fAllWish * nDistributeExcess; |
1180 | 0 | } |
1181 | 0 | } |
1182 | |
|
1183 | 0 | Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW ); |
1184 | 0 | xColSet->setPropertyValue( gsSize, Any( nWidth ) ); |
1185 | |
|
1186 | 0 | nAllWidth -= nWidth; |
1187 | 0 | } |
1188 | |
|
1189 | 0 | LayoutTable( rArea, !bMinimize, false ); |
1190 | 0 | } |
1191 | 0 | catch( Exception& ) |
1192 | 0 | { |
1193 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | | |
1198 | | void TableLayouter::DistributeRows( ::tools::Rectangle& rArea, |
1199 | | sal_Int32 nFirstRow, |
1200 | | sal_Int32 nLastRow, |
1201 | | const bool bOptimize, |
1202 | | const bool bMinimize ) |
1203 | 0 | { |
1204 | 0 | if( !mxTable.is() ) |
1205 | 0 | return; |
1206 | | |
1207 | 0 | try |
1208 | 0 | { |
1209 | 0 | const sal_Int32 nRowCount = mxTable->getRowCount(); |
1210 | 0 | Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW ); |
1211 | 0 | sal_Int32 nMinHeight = 0; |
1212 | | |
1213 | | //special case - minimize a single row |
1214 | 0 | if ( bMinimize && nFirstRow == nLastRow ) |
1215 | 0 | { |
1216 | 0 | const sal_Int32 nWish = std::max( maRows[nFirstRow].mnMinSize, nMinHeight ); |
1217 | 0 | if ( nWish < getRowHeight(nFirstRow) ) |
1218 | 0 | { |
1219 | 0 | Reference< XPropertySet > xRowSet( xRows->getByIndex( nFirstRow ), UNO_QUERY_THROW ); |
1220 | 0 | xRowSet->setPropertyValue( gsSize, Any( nWish ) ); |
1221 | |
|
1222 | 0 | LayoutTable( rArea, /*bFitWidth=*/false, /*bFitHeight=*/!bMinimize ); |
1223 | 0 | } |
1224 | 0 | } |
1225 | |
|
1226 | 0 | if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) ) |
1227 | 0 | return; |
1228 | | |
1229 | 0 | sal_Int32 nAllHeight = 0; |
1230 | 0 | sal_Int32 nMaxHeight = 0; |
1231 | |
|
1232 | 0 | for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) |
1233 | 0 | { |
1234 | 0 | nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight ); |
1235 | 0 | nMaxHeight = std::max( maRows[nRow].mnSize, nMaxHeight ); |
1236 | 0 | nAllHeight += maRows[nRow].mnSize; |
1237 | 0 | } |
1238 | |
|
1239 | 0 | const sal_Int32 nRows = nLastRow-nFirstRow+1; |
1240 | 0 | sal_Int32 nHeight = nAllHeight / nRows; |
1241 | |
|
1242 | 0 | if ( !bMinimize && nHeight < nMaxHeight ) |
1243 | 0 | { |
1244 | 0 | if ( !bOptimize ) |
1245 | 0 | { |
1246 | 0 | sal_Int32 nNeededHeight = nRows * nMaxHeight; |
1247 | 0 | rArea.AdjustBottom(nNeededHeight - nAllHeight ); |
1248 | 0 | nHeight = nMaxHeight; |
1249 | 0 | nAllHeight = nRows * nMaxHeight; |
1250 | 0 | } |
1251 | 0 | else if ( nHeight < nMinHeight ) |
1252 | 0 | { |
1253 | 0 | sal_Int32 nNeededHeight = nRows * nMinHeight; |
1254 | 0 | rArea.AdjustBottom(nNeededHeight - nAllHeight ); |
1255 | 0 | nHeight = nMinHeight; |
1256 | 0 | nAllHeight = nRows * nMinHeight; |
1257 | 0 | } |
1258 | 0 | } |
1259 | |
|
1260 | 0 | for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) |
1261 | 0 | { |
1262 | 0 | if ( bMinimize ) |
1263 | 0 | nHeight = maRows[nRow].mnMinSize; |
1264 | 0 | else if ( nRow == nLastRow ) |
1265 | 0 | nHeight = nAllHeight; // last row get round errors |
1266 | |
|
1267 | 0 | Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW ); |
1268 | 0 | xRowSet->setPropertyValue( gsSize, Any( nHeight ) ); |
1269 | |
|
1270 | 0 | nAllHeight -= nHeight; |
1271 | 0 | } |
1272 | |
|
1273 | 0 | LayoutTable( rArea, false, !bMinimize ); |
1274 | 0 | } |
1275 | 0 | catch( Exception& ) |
1276 | 0 | { |
1277 | 0 | TOOLS_WARN_EXCEPTION("svx", ""); |
1278 | 0 | } |
1279 | 0 | } |
1280 | | |
1281 | | void TableLayouter::dumpAsXml(xmlTextWriterPtr pWriter) const |
1282 | 0 | { |
1283 | 0 | (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter")); |
1284 | |
|
1285 | 0 | (void)xmlTextWriterStartElement(pWriter, BAD_CAST("columns")); |
1286 | 0 | for (const auto& rColumn : maColumns) |
1287 | 0 | rColumn.dumpAsXml(pWriter); |
1288 | 0 | (void)xmlTextWriterEndElement(pWriter); |
1289 | |
|
1290 | 0 | (void)xmlTextWriterStartElement(pWriter, BAD_CAST("rows")); |
1291 | 0 | for (const auto& rRow : maRows) |
1292 | 0 | rRow.dumpAsXml(pWriter); |
1293 | 0 | (void)xmlTextWriterEndElement(pWriter); |
1294 | |
|
1295 | 0 | (void)xmlTextWriterEndElement(pWriter); |
1296 | 0 | } |
1297 | | |
1298 | | void TableLayouter::Layout::dumpAsXml(xmlTextWriterPtr pWriter) const |
1299 | 0 | { |
1300 | 0 | (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter_Layout")); |
1301 | |
|
1302 | 0 | (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos).getStr())); |
1303 | 0 | (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(mnSize).getStr())); |
1304 | 0 | (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize).getStr())); |
1305 | |
|
1306 | 0 | (void)xmlTextWriterEndElement(pWriter); |
1307 | 0 | } |
1308 | | |
1309 | | } |
1310 | | |
1311 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |