Line data Source code
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 : #include <com/sun/star/awt/XLayoutConstrains.hpp>
23 :
24 : #include <tools/gen.hxx>
25 :
26 : #include "cell.hxx"
27 : #include "cellrange.hxx"
28 : #include "tablemodel.hxx"
29 : #include "tablerow.hxx"
30 : #include "tablerows.hxx"
31 : #include "tablecolumn.hxx"
32 : #include "tablecolumns.hxx"
33 : #include "tablelayouter.hxx"
34 : #include "svx/svdotable.hxx"
35 : #include "editeng/borderline.hxx"
36 : #include "editeng/boxitem.hxx"
37 : #include "svx/svdmodel.hxx"
38 : #include "svx/svdstr.hrc"
39 : #include "svdglob.hxx"
40 :
41 : using ::editeng::SvxBorderLine;
42 : using ::com::sun::star::awt::XLayoutConstrains;
43 : using namespace ::com::sun::star::uno;
44 : using namespace ::com::sun::star::lang;
45 : using namespace ::com::sun::star::container;
46 : using namespace ::com::sun::star::beans;
47 : using namespace ::com::sun::star::table;
48 : using namespace ::com::sun::star::text;
49 :
50 :
51 :
52 : namespace sdr { namespace table {
53 :
54 :
55 :
56 217 : static SvxBorderLine gEmptyBorder;
57 :
58 :
59 :
60 78 : TableLayouter::TableLayouter( const TableModelRef& xTableModel )
61 : : mxTable( xTableModel )
62 78 : , msSize( "Size" )
63 : {
64 78 : }
65 :
66 :
67 :
68 234 : TableLayouter::~TableLayouter()
69 : {
70 78 : ClearBorderLayout();
71 156 : }
72 :
73 :
74 :
75 10288 : basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
76 : {
77 10288 : sal_Int32 width = 0;
78 10288 : sal_Int32 height = 0;
79 :
80 : try
81 : {
82 10288 : if( xCell.is() && !xCell->isMerged() )
83 : {
84 10288 : CellPos aPos( rPos );
85 :
86 10288 : sal_Int32 nRowCount = getRowCount();
87 10288 : sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), (sal_Int32)1 );
88 30996 : while( nRowSpan && (aPos.mnRow < nRowCount) )
89 : {
90 10420 : if( ((sal_Int32)maRows.size()) <= aPos.mnRow )
91 0 : break;
92 :
93 10420 : height += maRows[aPos.mnRow++].mnSize;
94 10420 : nRowSpan--;
95 : }
96 :
97 10288 : sal_Int32 nColCount = getColumnCount();
98 10288 : sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), (sal_Int32)1 );
99 30864 : while( nColSpan && (aPos.mnCol < nColCount ) )
100 : {
101 10288 : if( ((sal_Int32)maColumns.size()) <= aPos.mnCol )
102 0 : break;
103 :
104 10288 : width += maColumns[aPos.mnCol++].mnSize;
105 10288 : nColSpan--;
106 : }
107 : }
108 : }
109 0 : catch( Exception& )
110 : {
111 : OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
112 : }
113 :
114 10288 : return basegfx::B2ITuple( width, height );
115 : }
116 :
117 :
118 :
119 10420 : bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
120 : {
121 : try
122 : {
123 10420 : if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
124 : {
125 10288 : const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
126 10288 : const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
127 :
128 10288 : if( (rPos.mnCol < ((sal_Int32)maColumns.size()) && (rPos.mnRow < ((sal_Int32)maRows.size()) ) ) )
129 : {
130 10288 : const sal_Int32 y = maRows[rPos.mnRow].mnPos;
131 :
132 10288 : if(bRTL)
133 : {
134 : ///For RTL Table Calculate the Right End of cell instead of Left
135 0 : const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
136 0 : rArea = basegfx::B2IRectangle( x-aCellSize.getX(), y, x, y + aCellSize.getY() );
137 : }
138 : else
139 : {
140 10288 : const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
141 10288 : rArea = basegfx::B2IRectangle( x, y, x + aCellSize.getX(), y + aCellSize.getY() );
142 : }
143 10288 : return true;
144 0 : }
145 : }
146 : }
147 0 : catch( Exception& )
148 : {
149 : OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
150 : }
151 132 : return false;
152 : }
153 :
154 :
155 0 : sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
156 : {
157 0 : if( isValidRow(nRow) )
158 0 : return maRows[nRow].mnSize;
159 : else
160 0 : return 0;
161 : }
162 :
163 :
164 0 : sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
165 : {
166 0 : if( isValidColumn(nColumn) )
167 0 : return maColumns[nColumn].mnSize;
168 : else
169 0 : return 0;
170 : }
171 :
172 :
173 :
174 0 : bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
175 : {
176 0 : const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
177 :
178 0 : if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
179 0 : (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
180 : {
181 0 : return rMap[nEdgeX][nEdgeY] != 0;
182 : }
183 : else
184 : {
185 : OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
186 : }
187 :
188 0 : return false;
189 : }
190 :
191 :
192 :
193 : /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
194 0 : SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
195 : {
196 0 : SvxBorderLine* pLine = 0;
197 :
198 0 : const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
199 :
200 0 : if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
201 0 : (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
202 : {
203 0 : pLine = rMap[nEdgeX][nEdgeY];
204 0 : if( pLine == &gEmptyBorder )
205 0 : pLine = 0;
206 : }
207 : else
208 : {
209 : OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
210 : }
211 :
212 0 : return pLine;
213 : }
214 :
215 :
216 :
217 0 : sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
218 : {
219 0 : sal_Int32 nRet = 0;
220 0 : const sal_Int32 nRowCount = getRowCount();
221 0 : if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
222 0 : nRet = maRows[std::min((sal_Int32)nEdgeY,nRowCount-1)].mnPos;
223 :
224 0 : if( nEdgeY == nRowCount )
225 0 : nRet += maRows[nEdgeY - 1].mnSize;
226 :
227 0 : if( pnMin )
228 : {
229 0 : if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
230 : {
231 0 : *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
232 : }
233 : else
234 : {
235 0 : *pnMin = nRet;
236 : }
237 : }
238 :
239 0 : if( pnMax )
240 : {
241 0 : *pnMax = 0x0fffffff;
242 : }
243 0 : return nRet;
244 : }
245 :
246 :
247 :
248 0 : sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
249 : {
250 0 : sal_Int32 nRet = 0;
251 :
252 0 : const sal_Int32 nColCount = getColumnCount();
253 0 : if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
254 0 : nRet = maColumns[std::min((sal_Int32)nEdgeX,nColCount-1)].mnPos;
255 :
256 0 : const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
257 0 : if( bRTL )
258 : {
259 0 : if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
260 0 : nRet += maColumns[nEdgeX].mnSize;
261 : }
262 : else
263 : {
264 0 : if( nEdgeX == nColCount )
265 0 : nRet += maColumns[nEdgeX - 1].mnSize;
266 : }
267 :
268 0 : if( pnMin )
269 : {
270 0 : *pnMin = nRet;
271 0 : if( bRTL )
272 : {
273 0 : if( nEdgeX < nColCount )
274 0 : *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
275 : }
276 : else
277 : {
278 0 : if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
279 0 : *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
280 : }
281 : }
282 :
283 0 : if( pnMax )
284 : {
285 0 : *pnMax = 0x0fffffff; // todo
286 0 : if( bRTL )
287 : {
288 0 : if( nEdgeX > 0 )
289 0 : *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
290 : }
291 : else
292 : {
293 0 : if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
294 0 : *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
295 : }
296 : }
297 :
298 0 : return nRet;
299 : }
300 :
301 :
302 :
303 0 : static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
304 : {
305 0 : Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
306 0 : if( xCell.is() && !xCell->isMerged() )
307 : {
308 0 : const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
309 0 : const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
310 0 : if( (nMergedX < nRight) && (nMergedY < nBottom) )
311 0 : return true;
312 :
313 0 : bRunning = false;
314 : }
315 0 : return false;
316 : }
317 :
318 : /** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
319 : the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
320 : */
321 0 : bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
322 : {
323 0 : rOriginX = nMergedX;
324 0 : rOriginY = nMergedY;
325 :
326 0 : if( xTable.is() ) try
327 : {
328 : // check if this cell already the origin or not merged at all
329 0 : Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
330 0 : if( !xCell.is() || !xCell->isMerged() )
331 0 : return true;
332 :
333 0 : bool bCheckVert = true;
334 0 : bool bCheckHorz = true;
335 :
336 0 : sal_Int32 nMinCol = 0;
337 0 : sal_Int32 nMinRow = 0;
338 :
339 0 : sal_Int32 nStep = 1, i;
340 :
341 : sal_Int32 nRow, nCol;
342 0 : do
343 : {
344 0 : if( bCheckVert )
345 : {
346 0 : nRow = nMergedY - nStep;
347 0 : if( nRow >= nMinRow )
348 : {
349 0 : nCol = nMergedX;
350 0 : for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
351 : {
352 0 : if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
353 : {
354 0 : rOriginX = nCol; rOriginY = nRow;
355 0 : return true;
356 : }
357 :
358 0 : if( !bCheckVert )
359 : {
360 0 : if( nCol == nMergedX )
361 : {
362 0 : nMinRow = nRow+1;
363 : }
364 : else
365 : {
366 0 : bCheckVert = true;
367 : }
368 0 : break;
369 : }
370 : }
371 : }
372 : else
373 : {
374 0 : bCheckVert = false;
375 : }
376 : }
377 :
378 0 : if( bCheckHorz )
379 : {
380 0 : nCol = nMergedX - nStep;
381 0 : if( nCol >= nMinCol )
382 : {
383 0 : nRow = nMergedY;
384 0 : for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
385 : {
386 0 : if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
387 : {
388 0 : rOriginX = nCol; rOriginY = nRow;
389 0 : return true;
390 : }
391 :
392 0 : if( !bCheckHorz )
393 : {
394 0 : if( nRow == nMergedY )
395 : {
396 0 : nMinCol = nCol+1;
397 : }
398 : else
399 : {
400 0 : bCheckHorz = true;
401 : }
402 0 : break;
403 : }
404 : }
405 : }
406 : else
407 : {
408 0 : bCheckHorz = false;
409 : }
410 : }
411 0 : nStep++;
412 : }
413 0 : while( bCheckVert || bCheckHorz );
414 : }
415 0 : catch( Exception& )
416 : {
417 : OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
418 : }
419 0 : return false;
420 : }
421 :
422 :
423 :
424 0 : sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
425 : {
426 0 : if( isValidColumn( nColumn ) )
427 : {
428 0 : return maColumns[nColumn].mnMinSize;
429 : }
430 : else
431 : {
432 : OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
433 0 : return 0;
434 : }
435 : }
436 :
437 :
438 :
439 192 : sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
440 : {
441 : // break loops after 100 runs to avoid freezing office due to developer error
442 192 : sal_Int32 nSafe = 100;
443 :
444 192 : const sal_Size nCount = rLayouts.size();
445 : sal_Size nIndex;
446 :
447 192 : bool bConstrainsBroken = false;
448 :
449 202 : do
450 : {
451 : // first enforce minimum size constrains on all entities
452 442 : for( nIndex = 0; nIndex < nCount; ++nIndex )
453 : {
454 240 : Layout& rLayout = rLayouts[nIndex];
455 240 : if( rLayout.mnSize < rLayout.mnMinSize )
456 : {
457 10 : nDistribute -= rLayout.mnMinSize - rLayout.mnSize;
458 10 : rLayout.mnSize = rLayout.mnMinSize;
459 : }
460 : }
461 :
462 : // calculate current width
463 : // if nDistribute is < 0 (shrinking), entities that are already
464 : // at minimum width are not counted
465 202 : sal_Int32 nCurrentWidth = 0;
466 442 : for( nIndex = 0; nIndex < nCount; ++nIndex )
467 : {
468 240 : Layout& rLayout = rLayouts[nIndex];
469 240 : if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
470 30 : nCurrentWidth += rLayout.mnSize;
471 : }
472 :
473 202 : bConstrainsBroken = false;
474 :
475 : // now distribute over entities
476 202 : if( (nCurrentWidth != 0) && (nDistribute != 0) )
477 : {
478 20 : sal_Int32 nDistributed = nDistribute;
479 50 : for( nIndex = 0; nIndex < nCount; ++nIndex )
480 : {
481 30 : Layout& rLayout = rLayouts[nIndex];
482 30 : if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
483 : {
484 : sal_Int32 n;
485 30 : if( nIndex == (nCount-1) )
486 20 : n = nDistributed; // for last entitie, use up rest
487 : else
488 10 : n = (nDistribute * rLayout.mnSize) / nCurrentWidth;
489 :
490 30 : nDistributed -= n;
491 30 : rLayout.mnSize += n;
492 :
493 30 : if( rLayout.mnSize < rLayout.mnMinSize )
494 10 : bConstrainsBroken = true;
495 : }
496 : }
497 : }
498 202 : } while( bConstrainsBroken && --nSafe );
499 :
500 192 : sal_Int32 nSize = 0;
501 422 : for( nIndex = 0; nIndex < nCount; ++nIndex )
502 230 : nSize += rLayouts[nIndex].mnSize;
503 :
504 192 : return nSize;
505 : }
506 :
507 :
508 :
509 : typedef std::vector< CellRef > MergeableCellVector;
510 : typedef std::vector< MergeableCellVector > MergeVector;
511 : typedef std::vector< sal_Int32 > Int32Vector;
512 :
513 :
514 :
515 662 : void TableLayouter::LayoutTableWidth( Rectangle& rArea, bool bFit )
516 : {
517 662 : const sal_Int32 nColCount = getColumnCount();
518 662 : const sal_Int32 nRowCount = getRowCount();
519 662 : if( nColCount == 0 )
520 662 : return;
521 :
522 662 : MergeVector aMergedCells( nColCount );
523 1324 : Int32Vector aOptimalColumns;
524 :
525 1324 : const OUString sOptimalSize("OptimalSize");
526 :
527 662 : if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
528 0 : maColumns.resize( nColCount );
529 :
530 1324 : Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
531 :
532 : // first calculate current width and initial minimum width per column,
533 : // merged cells will be counted later
534 662 : sal_Int32 nCurrentWidth = 0;
535 662 : sal_Int32 nCol = 0, nRow = 0;
536 2890 : for( nCol = 0; nCol < nColCount; nCol++ )
537 : {
538 2228 : sal_Int32 nMinWidth = 0;
539 :
540 2228 : bool bIsEmpty = true; // check if all cells in this column are merged
541 :
542 7368 : for( nRow = 0; nRow < nRowCount; ++nRow )
543 : {
544 5140 : CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
545 5140 : if( xCell.is() && !xCell->isMerged() )
546 : {
547 5074 : bIsEmpty = false;
548 :
549 5074 : sal_Int32 nColSpan = xCell->getColumnSpan();
550 5074 : if( nColSpan > 1 )
551 : {
552 : // merged cells will be evaluated later
553 0 : aMergedCells[nCol+nColSpan-1].push_back( xCell );
554 : }
555 : else
556 : {
557 5074 : nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
558 : }
559 : }
560 5140 : }
561 :
562 2228 : maColumns[nCol].mnMinSize = nMinWidth;
563 :
564 2228 : if( bIsEmpty )
565 : {
566 0 : maColumns[nCol].mnSize = 0;
567 : }
568 : else
569 : {
570 2228 : sal_Int32 nColWidth = 0;
571 2228 : Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
572 2228 : bool bOptimal = false;
573 2228 : xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
574 2228 : if( bOptimal )
575 : {
576 78 : aOptimalColumns.push_back(nCol);
577 : }
578 : else
579 : {
580 2150 : xColSet->getPropertyValue( msSize ) >>= nColWidth;
581 : }
582 :
583 2228 : maColumns[nCol].mnSize = nColWidth;
584 :
585 2228 : if( maColumns[nCol].mnSize < nMinWidth )
586 82 : maColumns[nCol].mnSize = nMinWidth;
587 :
588 2228 : nCurrentWidth += maColumns[nCol].mnSize;
589 : }
590 : }
591 :
592 : // if we have optimal sized rows, distribute what is given (left)
593 662 : if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
594 : {
595 0 : sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
596 0 : sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
597 :
598 0 : Int32Vector::iterator iter( aOptimalColumns.begin() );
599 0 : while( iter != aOptimalColumns.end() )
600 : {
601 0 : sal_Int32 nOptCol = (*iter++);
602 0 : if( iter == aOptimalColumns.end() )
603 0 : nDistribute = nLeft;
604 :
605 0 : maColumns[nOptCol].mnSize += nDistribute;
606 0 : nLeft -= nDistribute;
607 : }
608 :
609 : DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
610 : }
611 :
612 : // now check if merged cells fit
613 2228 : for( nCol = 1; nCol < nColCount; ++nCol )
614 : {
615 1566 : bool bChanges = false;
616 1566 : MergeableCellVector::iterator iter( aMergedCells[nCol].begin() );
617 :
618 1566 : const sal_Int32 nOldSize = maColumns[nCol].mnSize;
619 :
620 3132 : while( iter != aMergedCells[nCol].end() )
621 : {
622 0 : CellRef xCell( (*iter++) );
623 0 : sal_Int32 nMinWidth = xCell->getMinimumWidth();
624 :
625 0 : for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
626 0 : nMinWidth -= maColumns[nMCol].mnSize;
627 :
628 0 : if( nMinWidth > maColumns[nCol].mnMinSize )
629 0 : maColumns[nCol].mnMinSize = nMinWidth;
630 :
631 0 : if( nMinWidth > maColumns[nCol].mnSize )
632 : {
633 0 : maColumns[nCol].mnSize = nMinWidth;
634 0 : bChanges = true;
635 : }
636 0 : }
637 :
638 1566 : if( bChanges )
639 0 : nCurrentWidth += maColumns[nCol].mnSize - nOldSize;
640 : }
641 :
642 : // now scale if wanted and needed
643 662 : if( bFit && (nCurrentWidth != rArea.getWidth()) )
644 86 : distribute( maColumns, rArea.getWidth() - nCurrentWidth );
645 :
646 : // last step, update left edges
647 662 : sal_Int32 nNewWidth = 0;
648 :
649 662 : const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
650 662 : RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
651 3552 : while( coliter.next(nCol ) )
652 : {
653 2228 : maColumns[nCol].mnPos = nNewWidth;
654 2228 : nNewWidth += maColumns[nCol].mnSize;
655 2228 : if( bFit )
656 : {
657 468 : Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
658 468 : xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) );
659 : }
660 : }
661 :
662 662 : rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
663 1324 : updateCells( rArea );
664 : }
665 :
666 :
667 :
668 662 : void TableLayouter::LayoutTableHeight( Rectangle& rArea, bool bFit )
669 : {
670 662 : const sal_Int32 nColCount = getColumnCount();
671 662 : const sal_Int32 nRowCount = getRowCount();
672 662 : if( nRowCount == 0 )
673 662 : return;
674 :
675 662 : Reference< XTableRows > xRows( mxTable->getRows() );
676 :
677 1324 : MergeVector aMergedCells( nRowCount );
678 1324 : Int32Vector aOptimalRows;
679 :
680 1324 : const OUString sOptimalSize("OptimalSize");
681 :
682 : // first calculate current height and initial minimum size per column,
683 : // merged cells will be counted later
684 662 : sal_Int32 nCurrentHeight = 0;
685 : sal_Int32 nCol, nRow;
686 2148 : for( nRow = 0; nRow < nRowCount; ++nRow )
687 : {
688 1486 : sal_Int32 nMinHeight = 0;
689 :
690 1486 : bool bIsEmpty = true; // check if all cells in this row are merged
691 :
692 6626 : for( nCol = 0; nCol < nColCount; ++nCol )
693 : {
694 5140 : CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
695 5140 : if( xCell.is() && !xCell->isMerged() )
696 : {
697 5074 : bIsEmpty = false;
698 :
699 5074 : sal_Int32 nRowSpan = xCell->getRowSpan();
700 5074 : if( nRowSpan > 1 )
701 : {
702 : // merged cells will be evaluated later
703 48 : aMergedCells[nRow+nRowSpan-1].push_back( xCell );
704 : }
705 : else
706 : {
707 5026 : nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
708 : }
709 : }
710 5140 : }
711 :
712 1486 : maRows[nRow].mnMinSize = nMinHeight;
713 :
714 1486 : if( bIsEmpty )
715 : {
716 0 : maRows[nRow].mnSize = 0;
717 : }
718 : else
719 : {
720 1486 : sal_Int32 nRowHeight = 0;
721 1486 : Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
722 :
723 1486 : bool bOptimal = false;
724 1486 : xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
725 1486 : if( bOptimal )
726 : {
727 78 : aOptimalRows.push_back( nRow );
728 : }
729 : else
730 : {
731 1408 : xRowSet->getPropertyValue( msSize ) >>= nRowHeight;
732 : }
733 :
734 1486 : maRows[nRow].mnSize = nRowHeight;
735 :
736 1486 : if( maRows[nRow].mnSize < nMinHeight )
737 290 : maRows[nRow].mnSize = nMinHeight;
738 :
739 1486 : nCurrentHeight += maRows[nRow].mnSize;
740 : }
741 : }
742 :
743 : // if we have optimal sized rows, distribute what is given (left)
744 662 : if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
745 : {
746 0 : sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
747 0 : sal_Int32 nDistribute = nLeft / aOptimalRows.size();
748 :
749 0 : Int32Vector::iterator iter( aOptimalRows.begin() );
750 0 : while( iter != aOptimalRows.end() )
751 : {
752 0 : sal_Int32 nOptRow = (*iter++);
753 0 : if( iter == aOptimalRows.end() )
754 0 : nDistribute = nLeft;
755 :
756 0 : maRows[nOptRow].mnSize += nDistribute;
757 0 : nLeft -= nDistribute;
758 :
759 : }
760 :
761 : DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
762 : }
763 :
764 : // now check if merged cells fit
765 1486 : for( nRow = 1; nRow < nRowCount; ++nRow )
766 : {
767 824 : bool bChanges = false;
768 824 : sal_Int32 nOldSize = maRows[nRow].mnSize;
769 :
770 824 : MergeableCellVector::iterator iter( aMergedCells[nRow].begin() );
771 1696 : while( iter != aMergedCells[nRow].end() )
772 : {
773 48 : CellRef xCell( (*iter++) );
774 48 : sal_Int32 nMinHeight = xCell->getMinimumHeight();
775 :
776 48 : for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
777 0 : nMinHeight -= maRows[nMRow].mnSize;
778 :
779 48 : if( nMinHeight > maRows[nRow].mnMinSize )
780 0 : maRows[nRow].mnMinSize = nMinHeight;
781 :
782 48 : if( nMinHeight > maRows[nRow].mnSize )
783 : {
784 0 : maRows[nRow].mnSize = nMinHeight;
785 0 : bChanges = true;
786 : }
787 48 : }
788 824 : if( bChanges )
789 0 : nCurrentHeight += maRows[nRow].mnSize - nOldSize;
790 : }
791 :
792 : // now scale if wanted and needed
793 662 : if( bFit && nCurrentHeight != rArea.getHeight() )
794 106 : distribute( maRows, rArea.getHeight() - nCurrentHeight );
795 :
796 : // last step, update left edges
797 662 : sal_Int32 nNewHeight = 0;
798 2148 : for( nRow = 0; nRow < nRowCount; ++nRow )
799 : {
800 1486 : maRows[nRow].mnPos = nNewHeight;
801 1486 : nNewHeight += maRows[nRow].mnSize;
802 :
803 1486 : if( bFit )
804 : {
805 282 : Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
806 282 : xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) );
807 : }
808 : }
809 :
810 662 : rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
811 1324 : updateCells( rArea );
812 : }
813 :
814 :
815 :
816 : /** try to fit the table into the given rectangle.
817 : If the rectangle is to small, it will be grown to fit the table. */
818 662 : void TableLayouter::LayoutTable( Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
819 : {
820 662 : if( !mxTable.is() )
821 662 : return;
822 :
823 662 : const sal_Int32 nRowCount = mxTable->getRowCount();
824 662 : const sal_Int32 nColCount = mxTable->getColumnCount();
825 :
826 662 : if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
827 : {
828 178 : if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
829 154 : maRows.resize( nRowCount );
830 :
831 178 : Reference< XTableRows > xRows( mxTable->getRows() );
832 556 : for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
833 378 : maRows[nRow].clear();
834 :
835 178 : if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
836 154 : maColumns.resize( nColCount );
837 :
838 672 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
839 672 : maColumns[nCol].clear();
840 : }
841 :
842 662 : LayoutTableWidth( rRectangle, bFitWidth );
843 662 : LayoutTableHeight( rRectangle, bFitHeight );
844 662 : UpdateBorderLayout();
845 : }
846 :
847 :
848 :
849 1336 : void TableLayouter::updateCells( Rectangle& rRectangle )
850 : {
851 1336 : const sal_Int32 nColCount = getColumnCount();
852 1336 : const sal_Int32 nRowCount = getRowCount();
853 :
854 1336 : CellPos aPos;
855 4372 : for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
856 : {
857 13456 : for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
858 : {
859 10420 : CellRef xCell( getCell( aPos ) );
860 10420 : if( xCell.is() )
861 : {
862 10420 : basegfx::B2IRectangle aCellArea;
863 10420 : if( getCellArea( xCell, aPos, aCellArea ) )
864 : {
865 10288 : Rectangle aCellRect;
866 10288 : aCellRect.Left() = aCellArea.getMinX();
867 10288 : aCellRect.Right() = aCellArea.getMaxX();
868 10288 : aCellRect.Top() = aCellArea.getMinY();
869 10288 : aCellRect.Bottom() = aCellArea.getMaxY();
870 10288 : aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
871 10288 : xCell->setCellRect( aCellRect );
872 : }
873 : }
874 10420 : }
875 : }
876 1336 : }
877 :
878 :
879 :
880 25840 : CellRef TableLayouter::getCell( const CellPos& rPos ) const
881 : {
882 25840 : CellRef xCell;
883 25840 : if( mxTable.is() ) try
884 : {
885 25840 : xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
886 : }
887 0 : catch( Exception& )
888 : {
889 : OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
890 : }
891 25840 : return xCell;
892 : }
893 :
894 :
895 :
896 20692 : bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
897 : {
898 20692 : if (!pThis || ((pThis == &gEmptyBorder) && (pOther != 0)))
899 3152 : return false;
900 17540 : if (!pOther || (pOther == &gEmptyBorder))
901 14002 : return true;
902 :
903 3538 : sal_uInt16 nThisSize = pThis->GetScaledWidth();
904 3538 : sal_uInt16 nOtherSize = pOther->GetScaledWidth();
905 :
906 3538 : if (nThisSize > nOtherSize)
907 32 : return true;
908 :
909 3506 : else if (nThisSize < nOtherSize)
910 : {
911 32 : return false;
912 : }
913 : else
914 : {
915 3474 : if ( pOther->GetInWidth() && !pThis->GetInWidth() )
916 : {
917 0 : return true;
918 : }
919 3474 : else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
920 : {
921 0 : return false;
922 : }
923 : else
924 : {
925 3474 : return true; //! ???
926 : }
927 : }
928 : }
929 :
930 :
931 :
932 20692 : void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
933 : {
934 20692 : if( pLine == 0 )
935 10338 : pLine = &gEmptyBorder;
936 :
937 20692 : SvxBorderLine *pOld = bHorizontal ? maHorizontalBorders[nCol][nRow] : maVerticalBorders[nCol][nRow];
938 :
939 20692 : if( HasPriority( pLine, pOld ) )
940 : {
941 17508 : if( (pOld != 0) && (pOld != &gEmptyBorder) )
942 3506 : delete pOld;
943 :
944 17508 : SvxBorderLine* pNew = ( pLine != &gEmptyBorder ) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
945 :
946 17508 : if( bHorizontal )
947 8826 : maHorizontalBorders[nCol][nRow] = pNew;
948 : else
949 8682 : maVerticalBorders[nCol][nRow] = pNew;
950 : }
951 20692 : }
952 :
953 :
954 :
955 740 : void TableLayouter::ClearBorderLayout()
956 : {
957 740 : ClearBorderLayout(maHorizontalBorders);
958 740 : ClearBorderLayout(maVerticalBorders);
959 740 : }
960 :
961 :
962 :
963 1480 : void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
964 : {
965 1480 : const sal_Int32 nColCount = rMap.size();
966 :
967 7260 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
968 : {
969 5780 : const sal_Int32 nRowCount = rMap[nCol].size();
970 24812 : for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
971 : {
972 19032 : SvxBorderLine* pLine = rMap[nCol][nRow];
973 19032 : if( pLine )
974 : {
975 13994 : if( pLine != &gEmptyBorder )
976 6816 : delete pLine;
977 :
978 13994 : rMap[nCol][nRow] = 0;
979 : }
980 : }
981 : }
982 1480 : }
983 :
984 :
985 :
986 662 : void TableLayouter::ResizeBorderLayout()
987 : {
988 662 : ClearBorderLayout();
989 662 : ResizeBorderLayout(maHorizontalBorders);
990 662 : ResizeBorderLayout(maVerticalBorders);
991 662 : }
992 :
993 :
994 :
995 1324 : void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
996 : {
997 1324 : const sal_Int32 nColCount = getColumnCount() + 1;
998 1324 : const sal_Int32 nRowCount = getRowCount() + 1;
999 :
1000 1324 : if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
1001 308 : rMap.resize( nColCount );
1002 :
1003 7104 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1004 : {
1005 5780 : if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
1006 1248 : rMap[nCol].resize( nRowCount );
1007 : }
1008 1324 : }
1009 :
1010 :
1011 :
1012 662 : void TableLayouter::UpdateBorderLayout()
1013 : {
1014 : // make sure old border layout is cleared and border maps have correct size
1015 662 : ResizeBorderLayout();
1016 :
1017 662 : const sal_Int32 nColCount = getColumnCount();
1018 662 : const sal_Int32 nRowCount = getRowCount();
1019 :
1020 662 : CellPos aPos;
1021 2148 : for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1022 : {
1023 6626 : for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1024 : {
1025 5140 : CellRef xCell( getCell( aPos ) );
1026 5140 : if( !xCell.is() )
1027 0 : continue;
1028 :
1029 5140 : const SvxBoxItem* pThisAttr = static_cast<const SvxBoxItem*>(xCell->GetItemSet().GetItem( SDRATTR_TABLE_BORDER ));
1030 : OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1031 :
1032 5140 : if( !pThisAttr )
1033 0 : continue;
1034 :
1035 5140 : const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1036 5140 : const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1037 :
1038 10346 : for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1039 : {
1040 5206 : SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1041 5206 : SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1042 : }
1043 :
1044 10280 : for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1045 : {
1046 5140 : SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1047 5140 : SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1048 : }
1049 5140 : }
1050 : }
1051 662 : }
1052 :
1053 :
1054 :
1055 0 : void TableLayouter::DistributeColumns( ::Rectangle& rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol )
1056 : {
1057 0 : if( mxTable.is() ) try
1058 : {
1059 0 : const sal_Int32 nColCount = getColumnCount();
1060 :
1061 0 : if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
1062 0 : return;
1063 :
1064 0 : sal_Int32 nAllWidth = 0;
1065 0 : for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1066 0 : nAllWidth += getColumnWidth(nCol);
1067 :
1068 0 : sal_Int32 nWidth = nAllWidth / (nLastCol-nFirstCol+1);
1069 :
1070 0 : Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
1071 :
1072 0 : for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1073 : {
1074 0 : if( nCol == nLastCol )
1075 0 : nWidth = nAllWidth; // last column get round errors
1076 :
1077 0 : Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
1078 0 : xColSet->setPropertyValue( msSize, Any( nWidth ) );
1079 :
1080 0 : nAllWidth -= nWidth;
1081 0 : }
1082 :
1083 0 : LayoutTable( rArea, true, false );
1084 : }
1085 0 : catch( Exception& e )
1086 : {
1087 : (void)e;
1088 : OSL_FAIL("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1089 : }
1090 : }
1091 :
1092 :
1093 :
1094 0 : void TableLayouter::DistributeRows( ::Rectangle& rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow )
1095 : {
1096 0 : if( mxTable.is() ) try
1097 : {
1098 0 : const sal_Int32 nRowCount = mxTable->getRowCount();
1099 :
1100 0 : if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
1101 0 : return;
1102 :
1103 0 : sal_Int32 nAllHeight = 0;
1104 0 : sal_Int32 nMinHeight = 0;
1105 :
1106 0 : for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1107 : {
1108 0 : nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
1109 0 : nAllHeight += maRows[nRow].mnSize;
1110 : }
1111 :
1112 0 : const sal_Int32 nRows = (nLastRow-nFirstRow+1);
1113 0 : sal_Int32 nHeight = nAllHeight / nRows;
1114 :
1115 0 : if( nHeight < nMinHeight )
1116 : {
1117 0 : sal_Int32 nNeededHeight = nRows * nMinHeight;
1118 0 : rArea.Bottom() += nNeededHeight - nAllHeight;
1119 0 : nHeight = nMinHeight;
1120 0 : nAllHeight = nRows * nMinHeight;
1121 : }
1122 :
1123 0 : Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
1124 0 : for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1125 : {
1126 0 : if( nRow == nLastRow )
1127 0 : nHeight = nAllHeight; // last row get round errors
1128 :
1129 0 : Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1130 0 : xRowSet->setPropertyValue( msSize, Any( nHeight ) );
1131 :
1132 0 : nAllHeight -= nHeight;
1133 0 : }
1134 :
1135 0 : LayoutTable( rArea, false, true );
1136 : }
1137 0 : catch( Exception& e )
1138 : {
1139 : (void)e;
1140 : OSL_FAIL("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1141 : }
1142 : }
1143 :
1144 651 : } }
1145 :
1146 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|