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 72 : static SvxBorderLine gEmptyBorder;
57 :
58 : // -----------------------------------------------------------------------------
59 :
60 70 : TableLayouter::TableLayouter( const TableModelRef& xTableModel )
61 : : mxTable( xTableModel )
62 : , meWritingMode( WritingMode_LR_TB )
63 70 : , msSize( RTL_CONSTASCII_USTRINGPARAM( "Size" ) )
64 : {
65 70 : }
66 :
67 : // -----------------------------------------------------------------------------
68 :
69 210 : TableLayouter::~TableLayouter()
70 : {
71 70 : ClearBorderLayout();
72 140 : }
73 :
74 : // -----------------------------------------------------------------------------
75 :
76 4644 : basegfx::B2ITuple TableLayouter::getCellSize( const CellPos& rPos ) const
77 : {
78 4644 : sal_Int32 width = 0;
79 4644 : sal_Int32 height = 0;
80 :
81 : try
82 : {
83 4644 : CellRef xCell( getCell( rPos ) );
84 4644 : if( xCell.is() && !xCell->isMerged() )
85 : {
86 4644 : CellPos aPos( rPos );
87 :
88 4644 : sal_Int32 nRowCount = getRowCount();
89 4644 : sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), (sal_Int32)1 );
90 14044 : while( nRowSpan && (aPos.mnRow < nRowCount) )
91 : {
92 4756 : if( ((sal_Int32)maRows.size()) <= aPos.mnRow )
93 0 : break;
94 :
95 4756 : height += maRows[aPos.mnRow++].mnSize;
96 4756 : nRowSpan--;
97 : }
98 :
99 4644 : sal_Int32 nColCount = getColumnCount();
100 4644 : sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), (sal_Int32)1 );
101 13932 : while( nColSpan && (aPos.mnCol < nColCount ) )
102 : {
103 4644 : if( ((sal_Int32)maColumns.size()) <= aPos.mnCol )
104 0 : break;
105 :
106 4644 : width += maColumns[aPos.mnCol++].mnSize;
107 4644 : nColSpan--;
108 : }
109 4644 : }
110 : }
111 0 : catch( Exception& )
112 : {
113 : OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
114 : }
115 :
116 4644 : return basegfx::B2ITuple( width, height );
117 : }
118 :
119 : // -----------------------------------------------------------------------------
120 :
121 4756 : bool TableLayouter::getCellArea( const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
122 : {
123 : try
124 : {
125 4756 : CellRef xCell( getCell( rPos ) );
126 4756 : if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
127 : {
128 4644 : const basegfx::B2ITuple aCellSize( getCellSize( rPos ) );
129 :
130 4644 : if( (rPos.mnCol < ((sal_Int32)maColumns.size()) && (rPos.mnRow < ((sal_Int32)maRows.size()) ) ) )
131 : {
132 4644 : const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
133 4644 : const sal_Int32 y = maRows[rPos.mnRow].mnPos;
134 :
135 4644 : rArea = basegfx::B2IRectangle( x, y, x + aCellSize.getX(), y + aCellSize.getY() );
136 4644 : return true;
137 4644 : }
138 4756 : }
139 : }
140 0 : catch( Exception& )
141 : {
142 : OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
143 : }
144 112 : 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 188 : 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 188 : sal_Int32 nSafe = 100;
435 :
436 188 : const sal_Size nCount = rLayouts.size();
437 : sal_Size nIndex;
438 :
439 188 : bool bConstrainsBroken = false;
440 :
441 192 : do
442 : {
443 : // first enforce minimum size constrains on all entities
444 438 : for( nIndex = 0; nIndex < nCount; ++nIndex )
445 : {
446 246 : Layout& rLayout = rLayouts[nIndex];
447 246 : if( rLayout.mnSize < rLayout.mnMinSize )
448 : {
449 4 : nDistribute -= rLayout.mnMinSize - rLayout.mnSize;
450 4 : 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 192 : sal_Int32 nCurrentWidth = 0;
458 438 : for( nIndex = 0; nIndex < nCount; ++nIndex )
459 : {
460 246 : Layout& rLayout = rLayouts[nIndex];
461 246 : if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
462 18 : nCurrentWidth += rLayout.mnSize;
463 : }
464 :
465 192 : bConstrainsBroken = false;
466 :
467 : // now distribute over entities
468 192 : if( (nCurrentWidth != 0) && (nDistribute != 0) )
469 : {
470 16 : sal_Int32 nDistributed = nDistribute;
471 56 : for( nIndex = 0; nIndex < nCount; ++nIndex )
472 : {
473 40 : Layout& rLayout = rLayouts[nIndex];
474 40 : if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
475 : {
476 : sal_Int32 n;
477 18 : if( nIndex == (nCount-1) )
478 6 : n = nDistributed; // for last entitie, use up rest
479 : else
480 12 : n = (nDistribute * rLayout.mnSize) / nCurrentWidth; //
481 :
482 18 : nDistributed -= n;
483 18 : rLayout.mnSize += n;
484 :
485 18 : if( rLayout.mnSize < rLayout.mnMinSize )
486 4 : bConstrainsBroken = true;
487 : }
488 : }
489 : }
490 : } while( bConstrainsBroken && --nSafe );
491 :
492 188 : sal_Int32 nSize = 0;
493 430 : for( nIndex = 0; nIndex < nCount; ++nIndex )
494 242 : nSize += rLayouts[nIndex].mnSize;
495 :
496 188 : 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 364 : void TableLayouter::LayoutTableWidth( Rectangle& rArea, bool bFit )
508 : {
509 364 : const sal_Int32 nColCount = getColumnCount();
510 364 : const sal_Int32 nRowCount = getRowCount();
511 364 : if( nColCount == 0 )
512 364 : return;
513 :
514 364 : MergeVector aMergedCells( nColCount );
515 364 : Int32Vector aOptimalColumns;
516 :
517 364 : const OUString sOptimalSize( RTL_CONSTASCII_USTRINGPARAM("OptimalSize") );
518 :
519 364 : if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
520 0 : maColumns.resize( nColCount );
521 :
522 364 : 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 364 : sal_Int32 nCurrentWidth = 0;
527 364 : sal_Int32 nCol = 0, nRow = 0;
528 1504 : for( nCol = 0; nCol < nColCount; nCol++ )
529 : {
530 1140 : sal_Int32 nMinWidth = 0;
531 :
532 1140 : bool bIsEmpty = true; // check if all cells in this column are merged
533 :
534 3502 : for( nRow = 0; nRow < nRowCount; ++nRow )
535 : {
536 2362 : CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
537 2362 : if( xCell.is() && !xCell->isMerged() )
538 : {
539 2306 : bIsEmpty = false;
540 :
541 2306 : sal_Int32 nColSpan = xCell->getColumnSpan();
542 2306 : 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 2306 : nMinWidth = std::max( nMinWidth, xCell->getMinimumSize().Width );
550 : }
551 : }
552 2362 : }
553 :
554 1140 : maColumns[nCol].mnMinSize = nMinWidth;
555 :
556 1140 : if( bIsEmpty )
557 : {
558 0 : maColumns[nCol].mnSize = 0;
559 : }
560 : else
561 : {
562 1140 : sal_Int32 nColWidth = 0;
563 1140 : Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
564 1140 : sal_Bool bOptimal = sal_False;
565 1140 : xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
566 1140 : if( bOptimal )
567 : {
568 70 : aOptimalColumns.push_back(nCol);
569 : }
570 : else
571 : {
572 1070 : xColSet->getPropertyValue( msSize ) >>= nColWidth;
573 : }
574 :
575 1140 : maColumns[nCol].mnSize = nColWidth;
576 :
577 1140 : if( maColumns[nCol].mnSize < nMinWidth )
578 98 : maColumns[nCol].mnSize = nMinWidth;
579 :
580 1140 : nCurrentWidth += maColumns[nCol].mnSize;
581 : }
582 : }
583 :
584 : // if we have optimal sized rows, distribute what is given (left)
585 364 : 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 1140 : for( nCol = 1; nCol < nColCount; ++nCol )
606 : {
607 776 : bool bChanges = false;
608 776 : MergeableCellVector::iterator iter( aMergedCells[nCol].begin() );
609 :
610 776 : const sal_Int32 nOldSize = maColumns[nCol].mnSize;
611 :
612 1552 : 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 776 : if( bChanges )
631 0 : nCurrentWidth += maColumns[nCol].mnSize - nOldSize;
632 : }
633 :
634 : // now scale if wanted and needed
635 364 : if( bFit && (nCurrentWidth != rArea.getWidth()) )
636 70 : distribute( maColumns, rArea.getWidth() - nCurrentWidth );
637 :
638 : // last step, update left edges
639 364 : sal_Int32 nNewWidth = 0;
640 :
641 364 : const bool bRTL = meWritingMode == WritingMode_RL_TB;
642 364 : RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
643 1868 : while( coliter.next(nCol ) )
644 : {
645 1140 : maColumns[nCol].mnPos = nNewWidth;
646 1140 : nNewWidth += maColumns[nCol].mnSize;
647 1140 : if( bFit )
648 : {
649 428 : Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
650 428 : xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) );
651 : }
652 : }
653 :
654 364 : rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
655 364 : updateCells( rArea );
656 : }
657 :
658 : // -----------------------------------------------------------------------------
659 :
660 364 : void TableLayouter::LayoutTableHeight( Rectangle& rArea, bool bFit )
661 : {
662 364 : const sal_Int32 nColCount = getColumnCount();
663 364 : const sal_Int32 nRowCount = getRowCount();
664 364 : if( nRowCount == 0 )
665 364 : return;
666 :
667 364 : Reference< XTableRows > xRows( mxTable->getRows() );
668 :
669 364 : MergeVector aMergedCells( nRowCount );
670 364 : Int32Vector aOptimalRows;
671 :
672 364 : 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 364 : sal_Int32 nCurrentHeight = 0;
677 : sal_Int32 nCol, nRow;
678 990 : for( nRow = 0; nRow < nRowCount; ++nRow )
679 : {
680 626 : sal_Int32 nMinHeight = 0;
681 :
682 626 : bool bIsEmpty = true; // check if all cells in this row are merged
683 :
684 2988 : for( nCol = 0; nCol < nColCount; ++nCol )
685 : {
686 2362 : CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
687 2362 : if( xCell.is() && !xCell->isMerged() )
688 : {
689 2306 : bIsEmpty = false;
690 :
691 2306 : sal_Int32 nRowSpan = xCell->getRowSpan();
692 2306 : if( nRowSpan > 1 )
693 : {
694 : // merged cells will be evaluated later
695 38 : aMergedCells[nRow+nRowSpan-1].push_back( xCell );
696 : }
697 : else
698 : {
699 2268 : nMinHeight = std::max( nMinHeight, xCell->getMinimumSize().Height );
700 : }
701 : }
702 2362 : }
703 :
704 626 : maRows[nRow].mnMinSize = nMinHeight;
705 :
706 626 : if( bIsEmpty )
707 : {
708 0 : maRows[nRow].mnSize = 0;
709 : }
710 : else
711 : {
712 626 : sal_Int32 nRowHeight = 0;
713 626 : Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
714 :
715 626 : sal_Bool bOptimal = sal_False;
716 626 : xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
717 626 : if( bOptimal )
718 : {
719 70 : aOptimalRows.push_back( nRow );
720 : }
721 : else
722 : {
723 556 : xRowSet->getPropertyValue( msSize ) >>= nRowHeight;
724 : }
725 :
726 626 : maRows[nRow].mnSize = nRowHeight;
727 :
728 626 : if( maRows[nRow].mnSize < nMinHeight )
729 312 : maRows[nRow].mnSize = nMinHeight;
730 :
731 626 : nCurrentHeight += maRows[nRow].mnSize;
732 : }
733 : }
734 :
735 : // if we have optimal sized rows, distribute what is given (left)
736 364 : 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 626 : for( nRow = 1; nRow < nRowCount; ++nRow )
758 : {
759 262 : bool bChanges = false;
760 262 : sal_Int32 nOldSize = maRows[nRow].mnSize;
761 :
762 262 : MergeableCellVector::iterator iter( aMergedCells[nRow].begin() );
763 562 : while( iter != aMergedCells[nRow].end() )
764 : {
765 38 : CellRef xCell( (*iter++) );
766 38 : sal_Int32 nMinHeight = xCell->getMinimumSize().Height;
767 :
768 38 : for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
769 0 : nMinHeight -= maRows[nMRow].mnSize;
770 :
771 38 : if( nMinHeight > maRows[nRow].mnMinSize )
772 0 : maRows[nRow].mnMinSize = nMinHeight;
773 :
774 38 : if( nMinHeight > maRows[nRow].mnSize )
775 : {
776 0 : maRows[nRow].mnSize = nMinHeight;
777 0 : bChanges = true;
778 : }
779 38 : }
780 262 : if( bChanges )
781 0 : nCurrentHeight += maRows[nRow].mnSize - nOldSize;
782 : }
783 :
784 : // now scale if wanted and needed
785 364 : if( bFit && nCurrentHeight != rArea.getHeight() )
786 118 : distribute( maRows, rArea.getHeight() - nCurrentHeight );
787 :
788 : // last step, update left edges
789 364 : sal_Int32 nNewHeight = 0;
790 990 : for( nRow = 0; nRow < nRowCount; ++nRow )
791 : {
792 626 : maRows[nRow].mnPos = nNewHeight;
793 626 : nNewHeight += maRows[nRow].mnSize;
794 :
795 626 : if( bFit )
796 : {
797 224 : Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
798 224 : xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) );
799 : }
800 : }
801 :
802 364 : rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
803 364 : 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 364 : void TableLayouter::LayoutTable( Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
811 : {
812 364 : if( !mxTable.is() )
813 364 : return;
814 :
815 364 : const sal_Int32 nRowCount = mxTable->getRowCount();
816 364 : const sal_Int32 nColCount = mxTable->getColumnCount();
817 :
818 364 : if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
819 : {
820 140 : if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
821 118 : maRows.resize( nRowCount );
822 :
823 140 : Reference< XTableRows > xRows( mxTable->getRows() );
824 376 : for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
825 236 : maRows[nRow].clear();
826 :
827 140 : if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
828 138 : maColumns.resize( nColCount );
829 :
830 576 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
831 576 : maColumns[nCol].clear();
832 : }
833 :
834 364 : LayoutTableWidth( rRectangle, bFitWidth );
835 364 : LayoutTableHeight( rRectangle, bFitHeight );
836 364 : UpdateBorderLayout();
837 : }
838 :
839 : // -----------------------------------------------------------------------------
840 :
841 732 : void TableLayouter::updateCells( Rectangle& rRectangle )
842 : {
843 732 : const sal_Int32 nColCount = getColumnCount();
844 732 : const sal_Int32 nRowCount = getRowCount();
845 :
846 732 : CellPos aPos;
847 2004 : for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
848 : {
849 6028 : for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
850 : {
851 4756 : CellRef xCell( getCell( aPos ) );
852 4756 : if( xCell.is() )
853 : {
854 4756 : basegfx::B2IRectangle aCellArea;
855 4756 : getCellArea( aPos, aCellArea );
856 :
857 4756 : Rectangle aCellRect;
858 4756 : aCellRect.Left() = aCellArea.getMinX();
859 4756 : aCellRect.Right() = aCellArea.getMaxX();
860 4756 : aCellRect.Top() = aCellArea.getMinY();
861 4756 : aCellRect.Bottom() = aCellArea.getMaxY();
862 4756 : aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
863 4756 : xCell->setCellRect( aCellRect );
864 : }
865 4756 : }
866 : }
867 732 : }
868 :
869 : // -----------------------------------------------------------------------------
870 :
871 21242 : CellRef TableLayouter::getCell( const CellPos& rPos ) const
872 : {
873 21242 : CellRef xCell;
874 21242 : if( mxTable.is() ) try
875 : {
876 21242 : 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 21242 : return xCell;
883 : }
884 :
885 : // -----------------------------------------------------------------------------
886 :
887 9336 : bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
888 : {
889 9336 : if (!pThis || ((pThis == &gEmptyBorder) && (pOther != 0)))
890 124 : return false;
891 9212 : if (!pOther || (pOther == &gEmptyBorder))
892 6434 : return true;
893 :
894 2778 : sal_uInt16 nThisSize = pThis->GetOutWidth() + pThis->GetDistance() + pThis->GetInWidth();
895 2778 : sal_uInt16 nOtherSize = pOther->GetOutWidth() + pOther->GetDistance() + pOther->GetInWidth();
896 :
897 2778 : if (nThisSize > nOtherSize)
898 0 : return true;
899 :
900 2778 : else if (nThisSize < nOtherSize)
901 : {
902 0 : return false;
903 : }
904 : else
905 : {
906 2778 : if ( pOther->GetInWidth() && !pThis->GetInWidth() )
907 : {
908 0 : return true;
909 : }
910 2778 : else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
911 : {
912 0 : return false;
913 : }
914 : else
915 : {
916 2778 : return true; //! ???
917 : }
918 : }
919 : }
920 :
921 : // -----------------------------------------------------------------------------
922 :
923 9336 : void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
924 : {
925 9336 : if( pLine == 0 )
926 1094 : pLine = &gEmptyBorder;
927 :
928 9336 : SvxBorderLine *pOld = bHorizontal ? maHorizontalBorders[nCol][nRow] : maVerticalBorders[nCol][nRow];
929 :
930 9336 : if( HasPriority( pLine, pOld ) )
931 : {
932 9212 : if( (pOld != 0) && (pOld != &gEmptyBorder) )
933 2778 : delete pOld;
934 :
935 9212 : SvxBorderLine* pNew = ( pLine != &gEmptyBorder ) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
936 :
937 9212 : if( bHorizontal )
938 4560 : maHorizontalBorders[nCol][nRow] = pNew;
939 : else
940 4652 : maVerticalBorders[nCol][nRow] = pNew;
941 : }
942 9336 : }
943 :
944 : // -----------------------------------------------------------------------------
945 :
946 434 : void TableLayouter::ClearBorderLayout()
947 : {
948 434 : ClearBorderLayout(maHorizontalBorders);
949 434 : ClearBorderLayout(maVerticalBorders);
950 434 : }
951 :
952 : // -----------------------------------------------------------------------------
953 :
954 868 : void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
955 : {
956 868 : const sal_Int32 nColCount = rMap.size();
957 :
958 3876 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
959 : {
960 3008 : const sal_Int32 nRowCount = rMap[nCol].size();
961 11992 : for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
962 : {
963 8984 : SvxBorderLine* pLine = rMap[nCol][nRow];
964 8984 : if( pLine )
965 : {
966 6434 : if( pLine != &gEmptyBorder )
967 5464 : delete pLine;
968 :
969 6434 : rMap[nCol][nRow] = 0;
970 : }
971 : }
972 : }
973 868 : }
974 :
975 : // -----------------------------------------------------------------------------
976 :
977 364 : void TableLayouter::ResizeBorderLayout()
978 : {
979 364 : ClearBorderLayout();
980 364 : ResizeBorderLayout(maHorizontalBorders);
981 364 : ResizeBorderLayout(maVerticalBorders);
982 364 : }
983 :
984 : // -----------------------------------------------------------------------------
985 :
986 728 : void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
987 : {
988 728 : const sal_Int32 nColCount = getColumnCount() + 1;
989 728 : const sal_Int32 nRowCount = getRowCount() + 1;
990 :
991 728 : if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
992 276 : rMap.resize( nColCount );
993 :
994 3736 : for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
995 : {
996 3008 : if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
997 1064 : rMap[nCol].resize( nRowCount );
998 : }
999 728 : }
1000 :
1001 : // -----------------------------------------------------------------------------
1002 :
1003 364 : void TableLayouter::UpdateBorderLayout()
1004 : {
1005 : // make sure old border layout is cleared and border maps have correct size
1006 364 : ResizeBorderLayout();
1007 :
1008 364 : const sal_Int32 nColCount = getColumnCount();
1009 364 : const sal_Int32 nRowCount = getRowCount();
1010 :
1011 364 : CellPos aPos;
1012 990 : for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1013 : {
1014 2988 : for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1015 : {
1016 2362 : CellRef xCell( getCell( aPos ) );
1017 2362 : if( !xCell.is() || xCell->isMerged() )
1018 56 : continue;
1019 :
1020 2306 : const SvxBoxItem* pThisAttr = (const SvxBoxItem*)xCell->GetItemSet().GetItem( SDRATTR_TABLE_BORDER );
1021 : OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1022 :
1023 2306 : if( !pThisAttr )
1024 0 : continue;
1025 :
1026 2306 : const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1027 2306 : const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1028 :
1029 4668 : for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1030 : {
1031 2362 : SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1032 2362 : SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1033 : }
1034 :
1035 4612 : for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1036 : {
1037 2306 : SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1038 2306 : SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1039 : }
1040 2362 : }
1041 : }
1042 364 : }
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 216 : } }
1142 :
1143 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|