Update ooo320-m1
[ooovba.git] / svx / source / table / cellcursor.cxx
blob7ed2328745df81049a527b6d7f382544344f249a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cellcursor.cxx,v $
10 * $Revision: 1.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include "svx/svdotable.hxx"
35 #include "cellcursor.hxx"
36 #include "tablelayouter.hxx"
37 #include "cell.hxx"
38 #include "svx/svdmodel.hxx"
39 #include "svdstr.hrc"
40 #include "svdglob.hxx"
42 // -----------------------------------------------------------------------------
44 using ::rtl::OUString;
45 using namespace ::com::sun::star::uno;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::container;
48 using namespace ::com::sun::star::beans;
49 using namespace ::com::sun::star::table;
51 // -----------------------------------------------------------------------------
53 namespace sdr { namespace table {
55 // -----------------------------------------------------------------------------
56 // CellCursor
57 // -----------------------------------------------------------------------------
59 CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
60 : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
64 // -----------------------------------------------------------------------------
66 CellCursor::~CellCursor()
70 // -----------------------------------------------------------------------------
71 // XCellCursor
72 // -----------------------------------------------------------------------------
74 Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException)
76 return CellRange::getCellByPosition( nColumn, nRow );
79 // -----------------------------------------------------------------------------
81 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException)
83 return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
86 // -----------------------------------------------------------------------------
88 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange ) throw (RuntimeException)
90 return CellRange::getCellRangeByName( aRange );
93 // -----------------------------------------------------------------------------
94 // XCellCursor
95 // -----------------------------------------------------------------------------
97 void SAL_CALL CellCursor::gotoStart( ) throw (RuntimeException)
99 mnRight = mnLeft;
100 mnBottom = mnTop;
103 // -----------------------------------------------------------------------------
105 void SAL_CALL CellCursor::gotoEnd( ) throw (RuntimeException)
107 mnLeft = mnRight;
108 mnTop = mnBottom;
111 // -----------------------------------------------------------------------------
113 void SAL_CALL CellCursor::gotoNext( ) throw (RuntimeException)
115 if( mxTable.is() )
117 mnRight++;
118 if( mnRight >= mxTable->getColumnCount() )
120 // if we past the last column, try skip to the row line
121 mnTop++;
122 if( mnTop >= mxTable->getRowCount() )
124 // if we past the last row, do not move cursor at all
125 mnTop--;
126 mnRight--;
128 else
130 // restart at the first column on the next row
131 mnRight = 0;
136 mnLeft = mnRight;
137 mnTop = mnBottom;
140 // -----------------------------------------------------------------------------
142 void SAL_CALL CellCursor::gotoPrevious( ) throw (RuntimeException)
144 if( mxTable.is() )
146 if( mnLeft > 0 )
148 --mnLeft;
150 else if( mnTop > 0 )
152 --mnTop;
153 mnLeft = mxTable->getColumnCount() - 1;
157 mnRight = mnLeft;
158 mnBottom = mnTop;
161 // -----------------------------------------------------------------------------
163 void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) throw (RuntimeException)
165 if( mxTable.is() )
167 const sal_Int32 nLeft = mnLeft + nColumnOffset;
168 if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
169 mnRight = mnLeft = nLeft;
171 const sal_Int32 nTop = mnTop + nRowOffset;
172 if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
173 mnTop = mnBottom = nTop;
177 // -----------------------------------------------------------------------------
178 // XMergeableCellCursor
179 // -----------------------------------------------------------------------------
181 /** returns true and the merged cell positions if a merge is valid or false if a merge is
182 not valid for that range */
183 bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
185 rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
186 rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
188 // single cell merge is never valid
189 if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
191 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
193 // check if first cell is merged
194 if( xCell.is() && xCell->isMerged() )
195 findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
197 // check if last cell is merged
198 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
199 if( xCell.is() )
201 if( xCell->isMerged() )
203 findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
204 // merge not possible if selection is only one cell and all its merges
205 if( rEnd == rStart )
206 return false;
207 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
210 if( xCell.is() )
212 rEnd.mnCol += xCell->getColumnSpan()-1;
213 rEnd.mnRow += xCell->getRowSpan()-1;
216 // now check if everything is inside the given bounds
217 sal_Int32 nRow, nCol;
218 for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
220 for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
222 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
223 if( !xCell.is() )
224 continue;
226 if( xCell->isMerged() )
228 sal_Int32 nOriginCol, nOriginRow;
229 if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
231 if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
232 return false;
234 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
235 if( xCell.is() )
237 nOriginCol += xCell->getColumnSpan()-1;
238 nOriginRow += xCell->getRowSpan()-1;
240 if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
241 return false;
245 else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
247 return false;
251 return true;
253 catch( Exception& )
255 DBG_ERROR("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
257 return false;
260 // -----------------------------------------------------------------------------
262 void SAL_CALL CellCursor::merge( ) throw (NoSupportException, RuntimeException)
264 CellPos aStart, aEnd;
265 if( !GetMergedSelection( aStart, aEnd ) )
266 throw NoSupportException();
268 if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
269 throw DisposedException();
271 SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
272 const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
274 if( bUndo )
275 pModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) );
279 mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
280 mxTable->optimize();
281 mxTable->setModified(sal_True);
283 catch( Exception& )
285 DBG_ERROR("sdr::table::CellCursor::merge(), exception caught!");
288 if( bUndo )
289 pModel->EndUndo();
291 if( pModel )
292 pModel->SetChanged();
295 // -----------------------------------------------------------------------------
297 void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
299 const sal_Int32 nRowCount = mxTable->getRowCount();
301 sal_Int32 nNewCols = 0, nRow;
303 // first check how many columns we need to add
304 for( nRow = mnTop; nRow <= mnBottom; ++nRow )
306 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
307 if( xCell.is() && !xCell->isMerged() )
308 nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
311 if( nNewCols > 0 )
313 const OUString sWidth( RTL_CONSTASCII_USTRINGPARAM("Width") );
314 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
315 Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
316 sal_Int32 nWidth = 0;
317 xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
318 const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
320 // reference column gets new width + rounding errors
321 xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
323 xCols->insertByIndex( nCol + 1, nNewCols );
324 mnRight += nNewCols;
326 // distribute new width
327 for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
329 Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
330 xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
334 for( nRow = 0; nRow < nRowCount; ++nRow )
336 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
337 if( !xCell.is() || xCell->isMerged() )
339 if( nNewCols > 0 )
341 // merged cells are ignored, but newly added columns will be added to leftovers
342 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
343 if( !xCell.is() || !xCell->isMerged() )
344 rLeftOvers[nRow] += nNewCols;
347 else
349 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
350 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
352 if( (nRow >= mnTop) && (nRow <= mnBottom) )
354 sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
355 if( nColSpan == 0 )
356 nCellsAvailable += nNewCols;
358 DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
360 sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
362 sal_Int32 nSplitCol = nCol;
363 sal_Int32 nSplits = nColumns + 1;
364 while( nSplits-- )
366 // last split eats rounding cells
367 if( nSplits == 0 )
368 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
370 mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
371 if( nSplits > 0 )
372 nSplitCol += nSplitSpan + 1;
377 rLeftOvers[nRow++] = 0;
379 while( nRowSpan-- );
380 --nRow;
382 else
384 // cope with outside cells, merge if needed
385 if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
386 mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
390 rLeftOvers[nRow++] = 0; // consumed
392 while( nRowSpan-- );
393 --nRow;
399 // -----------------------------------------------------------------------------
401 void CellCursor::split_horizontal( sal_Int32 nColumns )
403 const sal_Int32 nRowCount = mxTable->getRowCount();
405 std::vector< sal_Int32 > aLeftOvers( nRowCount );
407 for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
408 split_column( nCol, nColumns, aLeftOvers );
411 // -----------------------------------------------------------------------------
413 void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
415 const sal_Int32 nColCount = mxTable->getColumnCount();
417 sal_Int32 nNewRows = 0, nCol;
419 // first check how many columns we need to add
420 for( nCol = mnLeft; nCol <= mnRight; ++nCol )
422 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
423 if( xCell.is() && !xCell->isMerged() )
424 nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
427 if( nNewRows > 0 )
429 const OUString sHeight( RTL_CONSTASCII_USTRINGPARAM("Height") );
430 Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
431 Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
432 sal_Int32 nHeight = 0;
433 xRefRow->getPropertyValue( sHeight ) >>= nHeight;
434 const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
436 // reference row gets new height + rounding errors
437 xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
439 xRows->insertByIndex( nRow + 1, nNewRows );
440 mnBottom += nNewRows;
442 // distribute new width
443 for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
445 Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
446 xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
450 for( nCol = 0; nCol < nColCount; ++nCol )
452 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
453 if( !xCell.is() || xCell->isMerged() )
455 if( nNewRows )
457 // merged cells are ignored, but newly added columns will be added to leftovers
458 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
459 if( !xCell.is() || !xCell->isMerged() )
460 rLeftOvers[nCol] += nNewRows;
463 else
465 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
466 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
468 if( (nCol >= mnLeft) && (nCol <= mnRight) )
470 sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
471 if( nRowSpan == 0 )
472 nCellsAvailable += nNewRows;
474 DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
476 sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
478 sal_Int32 nSplitRow = nRow;
479 sal_Int32 nSplits = nRows + 1;
480 while( nSplits-- )
482 // last split eats rounding cells
483 if( nSplits == 0 )
484 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
486 mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
487 if( nSplits > 0 )
488 nSplitRow += nSplitSpan + 1;
493 rLeftOvers[nCol++] = 0;
495 while( nColSpan-- );
496 --nCol;
498 else
500 // cope with outside cells, merge if needed
501 if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
502 mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
506 rLeftOvers[nCol++] = 0; // consumed
508 while( nColSpan-- );
509 --nCol;
515 // -----------------------------------------------------------------------------
517 void CellCursor::split_vertical( sal_Int32 nRows )
519 const sal_Int32 nColCount = mxTable->getColumnCount();
521 std::vector< sal_Int32 > aLeftOvers( nColCount );
523 for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
524 split_row( nRow, nRows, aLeftOvers );
527 // -----------------------------------------------------------------------------
529 void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows ) throw (NoSupportException, IllegalArgumentException, RuntimeException)
531 if( (nColumns < 0) || (nRows < 0) )
532 throw IllegalArgumentException();
534 if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
535 throw DisposedException();
537 SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
538 const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
539 if( bUndo )
540 pModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) );
544 if( nColumns > 0 )
545 split_horizontal( nColumns );
547 if( nRows > 0 )
548 split_vertical( nRows );
550 if( nColumns > 0 ||nRows > 0 )
551 mxTable->setModified(sal_True);
553 catch( Exception& )
555 DBG_ERROR("sdr::table::CellCursor::split(), exception caught!");
556 throw NoSupportException();
559 if( bUndo )
560 pModel->EndUndo();
562 if( pModel )
563 pModel->SetChanged();
566 // -----------------------------------------------------------------------------
568 sal_Bool SAL_CALL CellCursor::isMergeable( ) throw (RuntimeException)
570 CellPos aStart, aEnd;
571 return GetMergedSelection( aStart, aEnd ) ? sal_True : sal_False;
574 // -----------------------------------------------------------------------------
576 sal_Bool SAL_CALL CellCursor::isUnmergeable( ) throw (RuntimeException)
578 // this is true if there is at least one merged cell in the current range
579 for( sal_Int32 nRow = mnTop; nRow <= mnBottom; nRow++ )
581 for( sal_Int32 nCol = mnLeft; nCol <= mnRight; nCol++ )
583 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
584 if( xCell.is() && ( (xCell->getRowSpan() > 1) || (xCell->getColumnSpan() > 1) ) )
585 return sal_True;
588 return sal_False;
591 // -----------------------------------------------------------------------------