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