Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / svx / source / table / cellcursor.cxx
blob54729906bccfd67e618a6153dd04a09491716681
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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"
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;
39 namespace sdr { namespace table {
42 // CellCursor
45 CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
46 : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
52 CellCursor::~CellCursor()
57 // XCellCursor
60 Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException, std::exception)
62 return CellRange::getCellByPosition( nColumn, nRow );
67 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException, std::exception)
69 return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
74 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange ) throw (RuntimeException, std::exception)
76 return CellRange::getCellRangeByName( aRange );
80 // XCellCursor
83 void SAL_CALL CellCursor::gotoStart( ) throw (RuntimeException, std::exception)
85 mnRight = mnLeft;
86 mnBottom = mnTop;
91 void SAL_CALL CellCursor::gotoEnd( ) throw (RuntimeException, std::exception)
93 mnLeft = mnRight;
94 mnTop = mnBottom;
99 void SAL_CALL CellCursor::gotoNext( ) throw (RuntimeException, std::exception)
101 if( mxTable.is() )
103 mnRight++;
104 if( mnRight >= mxTable->getColumnCount() )
106 // if we past the last column, try skip to the row line
107 mnTop++;
108 if( mnTop >= mxTable->getRowCount() )
110 // if we past the last row, do not move cursor at all
111 mnTop--;
112 mnRight--;
114 else
116 // restart at the first column on the next row
117 mnRight = 0;
122 mnLeft = mnRight;
123 mnTop = mnBottom;
128 void SAL_CALL CellCursor::gotoPrevious( ) throw (RuntimeException, std::exception)
130 if( mxTable.is() )
132 if( mnLeft > 0 )
134 --mnLeft;
136 else if( mnTop > 0 )
138 --mnTop;
139 mnLeft = mxTable->getColumnCount() - 1;
143 mnRight = mnLeft;
144 mnBottom = mnTop;
149 void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) throw (RuntimeException, std::exception)
151 if( mxTable.is() )
153 const sal_Int32 nLeft = mnLeft + nColumnOffset;
154 if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
155 mnRight = mnLeft = nLeft;
157 const sal_Int32 nTop = mnTop + nRowOffset;
158 if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
159 mnTop = mnBottom = nTop;
164 // XMergeableCellCursor
167 /** returns true and the merged cell positions if a merge is valid or false if a merge is
168 not valid for that range */
169 bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
171 rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
172 rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
174 // single cell merge is never valid
175 if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
177 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
179 // check if first cell is merged
180 if( xCell.is() && xCell->isMerged() )
181 findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
183 // check if last cell is merged
184 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
185 if( xCell.is() )
187 if( xCell->isMerged() )
189 findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
190 // merge not possible if selection is only one cell and all its merges
191 if( rEnd == rStart )
192 return false;
193 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
196 if( xCell.is() )
198 rEnd.mnCol += xCell->getColumnSpan()-1;
199 rEnd.mnRow += xCell->getRowSpan()-1;
202 // now check if everything is inside the given bounds
203 sal_Int32 nRow, nCol;
204 for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
206 for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
208 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
209 if( !xCell.is() )
210 continue;
212 if( xCell->isMerged() )
214 sal_Int32 nOriginCol, nOriginRow;
215 if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
217 if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
218 return false;
220 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
221 if( xCell.is() )
223 nOriginCol += xCell->getColumnSpan()-1;
224 nOriginRow += xCell->getRowSpan()-1;
226 if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
227 return false;
231 else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
233 return false;
237 return true;
239 catch( Exception& )
241 OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
243 return false;
248 void SAL_CALL CellCursor::merge( ) throw (NoSupportException, RuntimeException, std::exception)
250 CellPos aStart, aEnd;
251 if( !GetMergedSelection( aStart, aEnd ) )
252 throw NoSupportException();
254 if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
255 throw DisposedException();
257 SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
258 const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
260 if( bUndo )
261 pModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) );
265 mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
266 mxTable->optimize();
267 mxTable->setModified(sal_True);
269 catch( Exception& )
271 OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
274 if( bUndo )
275 pModel->EndUndo();
277 if( pModel )
278 pModel->SetChanged();
283 void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
285 const sal_Int32 nRowCount = mxTable->getRowCount();
287 sal_Int32 nNewCols = 0, nRow;
289 // first check how many columns we need to add
290 for( nRow = mnTop; nRow <= mnBottom; ++nRow )
292 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
293 if( xCell.is() && !xCell->isMerged() )
294 nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
297 if( nNewCols > 0 )
299 const OUString sWidth("Width");
300 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
301 Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
302 sal_Int32 nWidth = 0;
303 xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
304 const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
306 // reference column gets new width + rounding errors
307 xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
309 xCols->insertByIndex( nCol + 1, nNewCols );
310 mnRight += nNewCols;
312 // distribute new width
313 for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
315 Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
316 xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
320 for( nRow = 0; nRow < nRowCount; ++nRow )
322 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
323 if( !xCell.is() || xCell->isMerged() )
325 if( nNewCols > 0 )
327 // merged cells are ignored, but newly added columns will be added to leftovers
328 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
329 if( !xCell.is() || !xCell->isMerged() )
330 rLeftOvers[nRow] += nNewCols;
333 else
335 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
336 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
338 if( (nRow >= mnTop) && (nRow <= mnBottom) )
340 sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
341 if( nColSpan == 0 )
342 nCellsAvailable += nNewCols;
344 DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
346 sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
348 sal_Int32 nSplitCol = nCol;
349 sal_Int32 nSplits = nColumns + 1;
350 while( nSplits-- )
352 // last split eats rounding cells
353 if( nSplits == 0 )
354 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
356 mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
357 if( nSplits > 0 )
358 nSplitCol += nSplitSpan + 1;
363 rLeftOvers[nRow++] = 0;
365 while( nRowSpan-- );
366 --nRow;
368 else
370 // cope with outside cells, merge if needed
371 if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
372 mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
376 rLeftOvers[nRow++] = 0; // consumed
378 while( nRowSpan-- );
379 --nRow;
387 void CellCursor::split_horizontal( sal_Int32 nColumns )
389 const sal_Int32 nRowCount = mxTable->getRowCount();
391 std::vector< sal_Int32 > aLeftOvers( nRowCount );
393 for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
394 split_column( nCol, nColumns, aLeftOvers );
399 void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
401 const sal_Int32 nColCount = mxTable->getColumnCount();
403 sal_Int32 nNewRows = 0, nCol;
405 // first check how many columns we need to add
406 for( nCol = mnLeft; nCol <= mnRight; ++nCol )
408 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
409 if( xCell.is() && !xCell->isMerged() )
410 nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
413 if( nNewRows > 0 )
415 const OUString sHeight("Height");
416 Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
417 Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
418 sal_Int32 nHeight = 0;
419 xRefRow->getPropertyValue( sHeight ) >>= nHeight;
420 const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
422 // reference row gets new height + rounding errors
423 xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
425 xRows->insertByIndex( nRow + 1, nNewRows );
426 mnBottom += nNewRows;
428 // distribute new width
429 for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
431 Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
432 xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
436 for( nCol = 0; nCol < nColCount; ++nCol )
438 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
439 if( !xCell.is() || xCell->isMerged() )
441 if( nNewRows )
443 // merged cells are ignored, but newly added columns will be added to leftovers
444 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
445 if( !xCell.is() || !xCell->isMerged() )
446 rLeftOvers[nCol] += nNewRows;
449 else
451 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
452 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
454 if( (nCol >= mnLeft) && (nCol <= mnRight) )
456 sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
457 if( nRowSpan == 0 )
458 nCellsAvailable += nNewRows;
460 DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
462 sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
464 sal_Int32 nSplitRow = nRow;
465 sal_Int32 nSplits = nRows + 1;
466 while( nSplits-- )
468 // last split eats rounding cells
469 if( nSplits == 0 )
470 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
472 mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
473 if( nSplits > 0 )
474 nSplitRow += nSplitSpan + 1;
479 rLeftOvers[nCol++] = 0;
481 while( nColSpan-- );
482 --nCol;
484 else
486 // cope with outside cells, merge if needed
487 if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
488 mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
492 rLeftOvers[nCol++] = 0; // consumed
494 while( nColSpan-- );
495 --nCol;
503 void CellCursor::split_vertical( sal_Int32 nRows )
505 const sal_Int32 nColCount = mxTable->getColumnCount();
507 std::vector< sal_Int32 > aLeftOvers( nColCount );
509 for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
510 split_row( nRow, nRows, aLeftOvers );
515 void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows ) throw (NoSupportException, IllegalArgumentException, RuntimeException, std::exception)
517 if( (nColumns < 0) || (nRows < 0) )
518 throw IllegalArgumentException();
520 if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
521 throw DisposedException();
523 SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
524 const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
525 if( bUndo )
526 pModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) );
530 if( nColumns > 0 )
531 split_horizontal( nColumns );
533 if( nRows > 0 )
534 split_vertical( nRows );
536 if( nColumns > 0 ||nRows > 0 )
537 mxTable->setModified(sal_True);
539 catch( Exception& )
541 OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
542 throw NoSupportException();
545 if( bUndo )
546 pModel->EndUndo();
548 if( pModel )
549 pModel->SetChanged();
554 sal_Bool SAL_CALL CellCursor::isMergeable( ) throw (RuntimeException, std::exception)
556 CellPos aStart, aEnd;
557 return GetMergedSelection( aStart, aEnd ) ? sal_True : sal_False;
564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */