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 "svx/svdotable.hxx"
22 : #include "cellcursor.hxx"
23 : #include "tablelayouter.hxx"
24 : #include "cell.hxx"
25 : #include "svx/svdmodel.hxx"
26 : #include "svx/svdstr.hrc"
27 : #include "svdglob.hxx"
28 :
29 :
30 :
31 : using namespace ::com::sun::star::uno;
32 : using namespace ::com::sun::star::lang;
33 : using namespace ::com::sun::star::container;
34 : using namespace ::com::sun::star::beans;
35 : using namespace ::com::sun::star::table;
36 :
37 :
38 :
39 : namespace sdr { namespace table {
40 :
41 8 : CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
42 8 : : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
43 : {
44 8 : }
45 :
46 :
47 :
48 16 : CellCursor::~CellCursor()
49 : {
50 16 : }
51 :
52 :
53 : // XCellCursor
54 :
55 :
56 0 : Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException, std::exception)
57 : {
58 0 : return CellRange::getCellByPosition( nColumn, nRow );
59 : }
60 :
61 :
62 :
63 0 : Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException, std::exception)
64 : {
65 0 : return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
66 : }
67 :
68 :
69 :
70 0 : Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange ) throw (RuntimeException, std::exception)
71 : {
72 0 : return CellRange::getCellRangeByName( aRange );
73 : }
74 :
75 :
76 : // XCellCursor
77 :
78 :
79 0 : void SAL_CALL CellCursor::gotoStart( ) throw (RuntimeException, std::exception)
80 : {
81 0 : mnRight = mnLeft;
82 0 : mnBottom = mnTop;
83 0 : }
84 :
85 :
86 :
87 0 : void SAL_CALL CellCursor::gotoEnd( ) throw (RuntimeException, std::exception)
88 : {
89 0 : mnLeft = mnRight;
90 0 : mnTop = mnBottom;
91 0 : }
92 :
93 :
94 :
95 0 : void SAL_CALL CellCursor::gotoNext( ) throw (RuntimeException, std::exception)
96 : {
97 0 : if( mxTable.is() )
98 : {
99 0 : mnRight++;
100 0 : if( mnRight >= mxTable->getColumnCount() )
101 : {
102 : // if we past the last column, try skip to the row line
103 0 : mnTop++;
104 0 : if( mnTop >= mxTable->getRowCount() )
105 : {
106 : // if we past the last row, do not move cursor at all
107 0 : mnTop--;
108 0 : mnRight--;
109 : }
110 : else
111 : {
112 : // restart at the first column on the next row
113 0 : mnRight = 0;
114 : }
115 : }
116 : }
117 :
118 0 : mnLeft = mnRight;
119 0 : mnTop = mnBottom;
120 0 : }
121 :
122 :
123 :
124 0 : void SAL_CALL CellCursor::gotoPrevious( ) throw (RuntimeException, std::exception)
125 : {
126 0 : if( mxTable.is() )
127 : {
128 0 : if( mnLeft > 0 )
129 : {
130 0 : --mnLeft;
131 : }
132 0 : else if( mnTop > 0 )
133 : {
134 0 : --mnTop;
135 0 : mnLeft = mxTable->getColumnCount() - 1;
136 : }
137 : }
138 :
139 0 : mnRight = mnLeft;
140 0 : mnBottom = mnTop;
141 0 : }
142 :
143 :
144 :
145 0 : void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) throw (RuntimeException, std::exception)
146 : {
147 0 : if( mxTable.is() )
148 : {
149 0 : const sal_Int32 nLeft = mnLeft + nColumnOffset;
150 0 : if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
151 0 : mnRight = mnLeft = nLeft;
152 :
153 0 : const sal_Int32 nTop = mnTop + nRowOffset;
154 0 : if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
155 0 : mnTop = mnBottom = nTop;
156 : }
157 0 : }
158 :
159 :
160 : // XMergeableCellCursor
161 :
162 :
163 : /** returns true and the merged cell positions if a merge is valid or false if a merge is
164 : not valid for that range */
165 16 : bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
166 : {
167 16 : rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
168 16 : rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
169 :
170 : // single cell merge is never valid
171 16 : if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
172 : {
173 16 : CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
174 :
175 : // check if first cell is merged
176 16 : if( xCell.is() && xCell->isMerged() )
177 0 : findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
178 :
179 : // check if last cell is merged
180 16 : xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
181 16 : if( xCell.is() )
182 : {
183 16 : if( xCell->isMerged() )
184 : {
185 0 : findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
186 : // merge not possible if selection is only one cell and all its merges
187 0 : if( rEnd == rStart )
188 0 : return false;
189 0 : xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
190 : }
191 : }
192 16 : if( xCell.is() )
193 : {
194 16 : rEnd.mnCol += xCell->getColumnSpan()-1;
195 16 : rEnd.mnRow += xCell->getRowSpan()-1;
196 : }
197 :
198 : // now check if everything is inside the given bounds
199 : sal_Int32 nRow, nCol;
200 54 : for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
201 : {
202 76 : for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
203 : {
204 38 : xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
205 38 : if( !xCell.is() )
206 0 : continue;
207 :
208 38 : if( xCell->isMerged() )
209 : {
210 : sal_Int32 nOriginCol, nOriginRow;
211 0 : if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
212 : {
213 0 : if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
214 0 : return false;
215 :
216 0 : xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
217 0 : if( xCell.is() )
218 : {
219 0 : nOriginCol += xCell->getColumnSpan()-1;
220 0 : nOriginRow += xCell->getRowSpan()-1;
221 :
222 0 : if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
223 0 : return false;
224 : }
225 : }
226 : }
227 38 : else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
228 : {
229 0 : return false;
230 : }
231 : }
232 : }
233 16 : return true;
234 : }
235 0 : catch( Exception& )
236 : {
237 : OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
238 : }
239 0 : return false;
240 : }
241 :
242 :
243 :
244 8 : void SAL_CALL CellCursor::merge( ) throw (NoSupportException, RuntimeException, std::exception)
245 : {
246 8 : CellPos aStart, aEnd;
247 8 : if( !GetMergedSelection( aStart, aEnd ) )
248 0 : throw NoSupportException();
249 :
250 8 : if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
251 0 : throw DisposedException();
252 :
253 8 : SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
254 8 : const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
255 :
256 8 : if( bUndo )
257 0 : pModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) );
258 :
259 : try
260 : {
261 8 : mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
262 8 : mxTable->optimize();
263 8 : mxTable->setModified(sal_True);
264 : }
265 0 : catch( Exception& )
266 : {
267 : OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
268 : }
269 :
270 8 : if( bUndo )
271 0 : pModel->EndUndo();
272 :
273 8 : if( pModel )
274 8 : pModel->SetChanged();
275 8 : }
276 :
277 :
278 :
279 0 : void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
280 : {
281 0 : const sal_Int32 nRowCount = mxTable->getRowCount();
282 :
283 0 : sal_Int32 nNewCols = 0, nRow;
284 :
285 : // first check how many columns we need to add
286 0 : for( nRow = mnTop; nRow <= mnBottom; ++nRow )
287 : {
288 0 : CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
289 0 : if( xCell.is() && !xCell->isMerged() )
290 0 : nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
291 0 : }
292 :
293 0 : if( nNewCols > 0 )
294 : {
295 0 : const OUString sWidth("Width");
296 0 : Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
297 0 : Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
298 0 : sal_Int32 nWidth = 0;
299 0 : xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
300 0 : const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
301 :
302 : // reference column gets new width + rounding errors
303 0 : xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
304 :
305 0 : xCols->insertByIndex( nCol + 1, nNewCols );
306 0 : mnRight += nNewCols;
307 :
308 : // distribute new width
309 0 : for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
310 : {
311 0 : Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
312 0 : xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
313 0 : }
314 : }
315 :
316 0 : for( nRow = 0; nRow < nRowCount; ++nRow )
317 : {
318 0 : CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
319 0 : if( !xCell.is() || xCell->isMerged() )
320 : {
321 0 : if( nNewCols > 0 )
322 : {
323 : // merged cells are ignored, but newly added columns will be added to leftovers
324 0 : xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
325 0 : if( !xCell.is() || !xCell->isMerged() )
326 0 : rLeftOvers[nRow] += nNewCols;
327 : }
328 : }
329 : else
330 : {
331 0 : sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
332 0 : sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
333 :
334 0 : if( (nRow >= mnTop) && (nRow <= mnBottom) )
335 : {
336 0 : sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
337 0 : if( nColSpan == 0 )
338 0 : nCellsAvailable += nNewCols;
339 :
340 : DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
341 :
342 0 : sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
343 :
344 0 : sal_Int32 nSplitCol = nCol;
345 0 : sal_Int32 nSplits = nColumns + 1;
346 0 : while( nSplits-- )
347 : {
348 : // last split eats rounding cells
349 0 : if( nSplits == 0 )
350 0 : nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
351 :
352 0 : mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
353 0 : if( nSplits > 0 )
354 0 : nSplitCol += nSplitSpan + 1;
355 : }
356 :
357 0 : do
358 : {
359 0 : rLeftOvers[nRow++] = 0;
360 : }
361 : while( nRowSpan-- );
362 0 : --nRow;
363 : }
364 : else
365 : {
366 : // cope with outside cells, merge if needed
367 0 : if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
368 0 : mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
369 :
370 0 : do
371 : {
372 0 : rLeftOvers[nRow++] = 0; // consumed
373 : }
374 : while( nRowSpan-- );
375 0 : --nRow;
376 : }
377 : }
378 0 : }
379 0 : }
380 :
381 :
382 :
383 0 : void CellCursor::split_horizontal( sal_Int32 nColumns )
384 : {
385 0 : const sal_Int32 nRowCount = mxTable->getRowCount();
386 :
387 0 : std::vector< sal_Int32 > aLeftOvers( nRowCount );
388 :
389 0 : for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
390 0 : split_column( nCol, nColumns, aLeftOvers );
391 0 : }
392 :
393 :
394 :
395 0 : void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
396 : {
397 0 : const sal_Int32 nColCount = mxTable->getColumnCount();
398 :
399 0 : sal_Int32 nNewRows = 0, nCol;
400 :
401 : // first check how many columns we need to add
402 0 : for( nCol = mnLeft; nCol <= mnRight; ++nCol )
403 : {
404 0 : CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
405 0 : if( xCell.is() && !xCell->isMerged() )
406 0 : nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
407 0 : }
408 :
409 0 : if( nNewRows > 0 )
410 : {
411 0 : const OUString sHeight("Height");
412 0 : Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
413 0 : Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
414 0 : sal_Int32 nHeight = 0;
415 0 : xRefRow->getPropertyValue( sHeight ) >>= nHeight;
416 0 : const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
417 :
418 : // reference row gets new height + rounding errors
419 0 : xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
420 :
421 0 : xRows->insertByIndex( nRow + 1, nNewRows );
422 0 : mnBottom += nNewRows;
423 :
424 : // distribute new width
425 0 : for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
426 : {
427 0 : Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
428 0 : xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
429 0 : }
430 : }
431 :
432 0 : for( nCol = 0; nCol < nColCount; ++nCol )
433 : {
434 0 : CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
435 0 : if( !xCell.is() || xCell->isMerged() )
436 : {
437 0 : if( nNewRows )
438 : {
439 : // merged cells are ignored, but newly added columns will be added to leftovers
440 0 : xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
441 0 : if( !xCell.is() || !xCell->isMerged() )
442 0 : rLeftOvers[nCol] += nNewRows;
443 : }
444 : }
445 : else
446 : {
447 0 : sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
448 0 : sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
449 :
450 0 : if( (nCol >= mnLeft) && (nCol <= mnRight) )
451 : {
452 0 : sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
453 0 : if( nRowSpan == 0 )
454 0 : nCellsAvailable += nNewRows;
455 :
456 : DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
457 :
458 0 : sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
459 :
460 0 : sal_Int32 nSplitRow = nRow;
461 0 : sal_Int32 nSplits = nRows + 1;
462 0 : while( nSplits-- )
463 : {
464 : // last split eats rounding cells
465 0 : if( nSplits == 0 )
466 0 : nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
467 :
468 0 : mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
469 0 : if( nSplits > 0 )
470 0 : nSplitRow += nSplitSpan + 1;
471 : }
472 :
473 0 : do
474 : {
475 0 : rLeftOvers[nCol++] = 0;
476 : }
477 : while( nColSpan-- );
478 0 : --nCol;
479 : }
480 : else
481 : {
482 : // cope with outside cells, merge if needed
483 0 : if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
484 0 : mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
485 :
486 0 : do
487 : {
488 0 : rLeftOvers[nCol++] = 0; // consumed
489 : }
490 : while( nColSpan-- );
491 0 : --nCol;
492 : }
493 : }
494 0 : }
495 0 : }
496 :
497 :
498 :
499 0 : void CellCursor::split_vertical( sal_Int32 nRows )
500 : {
501 0 : const sal_Int32 nColCount = mxTable->getColumnCount();
502 :
503 0 : std::vector< sal_Int32 > aLeftOvers( nColCount );
504 :
505 0 : for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
506 0 : split_row( nRow, nRows, aLeftOvers );
507 0 : }
508 :
509 :
510 :
511 0 : void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows ) throw (NoSupportException, IllegalArgumentException, RuntimeException, std::exception)
512 : {
513 0 : if( (nColumns < 0) || (nRows < 0) )
514 0 : throw IllegalArgumentException();
515 :
516 0 : if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
517 0 : throw DisposedException();
518 :
519 0 : SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
520 0 : const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
521 0 : if( bUndo )
522 0 : pModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) );
523 :
524 : try
525 : {
526 0 : if( nColumns > 0 )
527 0 : split_horizontal( nColumns );
528 :
529 0 : if( nRows > 0 )
530 0 : split_vertical( nRows );
531 :
532 0 : if( nColumns > 0 ||nRows > 0 )
533 0 : mxTable->setModified(sal_True);
534 : }
535 0 : catch( Exception& )
536 : {
537 : OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
538 0 : throw NoSupportException();
539 : }
540 :
541 0 : if( bUndo )
542 0 : pModel->EndUndo();
543 :
544 0 : if( pModel )
545 0 : pModel->SetChanged();
546 0 : }
547 :
548 :
549 :
550 8 : sal_Bool SAL_CALL CellCursor::isMergeable( ) throw (RuntimeException, std::exception)
551 : {
552 8 : CellPos aStart, aEnd;
553 8 : return GetMergedSelection( aStart, aEnd ) ? sal_True : sal_False;
554 : }
555 :
556 :
557 :
558 435 : } }
559 :
560 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|