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