Bump version to 6.4-15
[LibreOffice.git] / svx / source / table / cellcursor.cxx
blob09197d1be19435b66287670dc882cc28d9715ffa
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 .
20 #include <sal/config.h>
22 #include <com/sun/star/lang/NoSupportException.hpp>
23 #include <svx/svdotable.hxx>
24 #include "cellcursor.hxx"
25 #include "tablelayouter.hxx"
26 #include <cell.hxx>
27 #include <svx/svdmodel.hxx>
28 #include <svx/strings.hrc>
29 #include <svx/dialmgr.hxx>
30 #include <tools/debug.hxx>
33 using namespace ::com::sun::star::uno;
34 using namespace ::com::sun::star::lang;
35 using namespace ::com::sun::star::container;
36 using namespace ::com::sun::star::beans;
37 using namespace ::com::sun::star::table;
40 namespace sdr { namespace table {
42 CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
43 : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
48 CellCursor::~CellCursor()
53 // XCellCursor
56 Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
58 return CellRange::getCellByPosition( nColumn, nRow );
62 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
64 return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
68 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
70 return CellRange::getCellRangeByName( aRange );
74 // XCellCursor
77 void SAL_CALL CellCursor::gotoStart( )
79 mnRight = mnLeft;
80 mnBottom = mnTop;
84 void SAL_CALL CellCursor::gotoEnd( )
86 mnLeft = mnRight;
87 mnTop = mnBottom;
91 void SAL_CALL CellCursor::gotoNext( )
93 if( mxTable.is() )
95 mnRight++;
96 if( mnRight >= mxTable->getColumnCount() )
98 // if we past the last column, try skip to the row line
99 mnTop++;
100 if( mnTop >= mxTable->getRowCount() )
102 // if we past the last row, do not move cursor at all
103 mnTop--;
104 mnRight--;
106 else
108 // restart at the first column on the next row
109 mnRight = 0;
114 mnLeft = mnRight;
115 mnTop = mnBottom;
119 void SAL_CALL CellCursor::gotoPrevious( )
121 if( mxTable.is() )
123 if( mnLeft > 0 )
125 --mnLeft;
127 else if( mnTop > 0 )
129 --mnTop;
130 mnLeft = mxTable->getColumnCount() - 1;
134 mnRight = mnLeft;
135 mnBottom = mnTop;
139 void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset )
141 if( mxTable.is() )
143 const sal_Int32 nLeft = mnLeft + nColumnOffset;
144 if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
145 mnRight = mnLeft = nLeft;
147 const sal_Int32 nTop = mnTop + nRowOffset;
148 if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
149 mnTop = mnBottom = nTop;
154 // XMergeableCellCursor
157 /** returns true and the merged cell positions if a merge is valid or false if a merge is
158 not valid for that range */
159 bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
161 rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
162 rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
164 // single cell merge is never valid
165 if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
167 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
169 // check if first cell is merged
170 if( xCell.is() && xCell->isMerged() )
171 findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
173 // check if last cell is merged
174 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
175 if( xCell.is() )
177 if( xCell->isMerged() )
179 findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
180 // merge not possible if selection is only one cell and all its merges
181 if( rEnd == rStart )
182 return false;
183 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
186 if( xCell.is() )
188 rEnd.mnCol += xCell->getColumnSpan()-1;
189 rEnd.mnRow += xCell->getRowSpan()-1;
192 // now check if everything is inside the given bounds
193 sal_Int32 nRow, nCol;
194 for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
196 for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
198 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
199 if( !xCell.is() )
200 continue;
202 if( xCell->isMerged() )
204 sal_Int32 nOriginCol, nOriginRow;
205 if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
207 if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
208 return false;
210 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
211 if( xCell.is() )
213 nOriginCol += xCell->getColumnSpan()-1;
214 nOriginRow += xCell->getRowSpan()-1;
216 if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
217 return false;
221 else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
223 return false;
227 return true;
229 catch( Exception& )
231 OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
233 return false;
237 void SAL_CALL CellCursor::merge( )
239 CellPos aStart, aEnd;
240 if( !GetMergedSelection( aStart, aEnd ) )
241 throw NoSupportException();
243 if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
244 throw DisposedException();
246 SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
247 const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
249 if( bUndo )
250 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
254 mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
255 mxTable->optimize();
256 mxTable->setModified(true);
258 catch( Exception& )
260 OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
263 if( bUndo )
264 rModel.EndUndo();
266 rModel.SetChanged();
270 void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
272 const sal_Int32 nRowCount = mxTable->getRowCount();
274 sal_Int32 nNewCols = 0, nRow;
276 // first check how many columns we need to add
277 for( nRow = mnTop; nRow <= mnBottom; ++nRow )
279 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
280 if( xCell.is() && !xCell->isMerged() )
281 nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
284 if( nNewCols > 0 )
286 const OUString sWidth("Width");
287 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
288 Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
289 sal_Int32 nWidth = 0;
290 xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
291 const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
293 // reference column gets new width + rounding errors
294 xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
296 xCols->insertByIndex( nCol + 1, nNewCols );
297 mnRight += nNewCols;
299 // distribute new width
300 for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
302 Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
303 xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
307 for( nRow = 0; nRow < nRowCount; ++nRow )
309 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
310 if( !xCell.is() || xCell->isMerged() )
312 if( nNewCols > 0 )
314 // merged cells are ignored, but newly added columns will be added to leftovers
315 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
316 if( !xCell.is() || !xCell->isMerged() )
317 rLeftOvers[nRow] += nNewCols;
320 else
322 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
323 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
325 if( (nRow >= mnTop) && (nRow <= mnBottom) )
327 sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
328 if( nColSpan == 0 )
329 nCellsAvailable += nNewCols;
331 DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
333 sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
335 sal_Int32 nSplitCol = nCol;
336 sal_Int32 nSplits = nColumns + 1;
337 while( nSplits-- )
339 // last split eats rounding cells
340 if( nSplits == 0 )
341 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
343 mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
344 if( nSplits > 0 )
345 nSplitCol += nSplitSpan + 1;
350 rLeftOvers[nRow++] = 0;
352 while( nRowSpan-- );
353 --nRow;
355 else
357 // cope with outside cells, merge if needed
358 if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
359 mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
363 rLeftOvers[nRow++] = 0; // consumed
365 while( nRowSpan-- );
366 --nRow;
373 void CellCursor::split_horizontal( sal_Int32 nColumns )
375 const sal_Int32 nRowCount = mxTable->getRowCount();
377 std::vector< sal_Int32 > aLeftOvers( nRowCount );
379 for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
380 split_column( nCol, nColumns, aLeftOvers );
384 void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
386 const sal_Int32 nColCount = mxTable->getColumnCount();
388 sal_Int32 nNewRows = 0, nCol;
390 // first check how many columns we need to add
391 for( nCol = mnLeft; nCol <= mnRight; ++nCol )
393 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
394 if( xCell.is() && !xCell->isMerged() )
395 nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
398 if( nNewRows > 0 )
400 const OUString sHeight("Height");
401 Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
402 Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
403 sal_Int32 nHeight = 0;
404 xRefRow->getPropertyValue( sHeight ) >>= nHeight;
405 const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
407 // reference row gets new height + rounding errors
408 xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
410 xRows->insertByIndex( nRow + 1, nNewRows );
411 mnBottom += nNewRows;
413 // distribute new width
414 for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
416 Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
417 xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
421 for( nCol = 0; nCol < nColCount; ++nCol )
423 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
424 if( !xCell.is() || xCell->isMerged() )
426 if( nNewRows )
428 // merged cells are ignored, but newly added columns will be added to leftovers
429 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
430 if( !xCell.is() || !xCell->isMerged() )
431 rLeftOvers[nCol] += nNewRows;
434 else
436 sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
437 sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
439 if( (nCol >= mnLeft) && (nCol <= mnRight) )
441 sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
442 if( nRowSpan == 0 )
443 nCellsAvailable += nNewRows;
445 DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
447 sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
449 sal_Int32 nSplitRow = nRow;
450 sal_Int32 nSplits = nRows + 1;
451 while( nSplits-- )
453 // last split eats rounding cells
454 if( nSplits == 0 )
455 nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
457 mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
458 if( nSplits > 0 )
459 nSplitRow += nSplitSpan + 1;
464 rLeftOvers[nCol++] = 0;
466 while( nColSpan-- );
467 --nCol;
469 else
471 // cope with outside cells, merge if needed
472 if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
473 mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
477 rLeftOvers[nCol++] = 0; // consumed
479 while( nColSpan-- );
480 --nCol;
487 void CellCursor::split_vertical( sal_Int32 nRows )
489 const sal_Int32 nColCount = mxTable->getColumnCount();
491 std::vector< sal_Int32 > aLeftOvers( nColCount );
493 for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
494 split_row( nRow, nRows, aLeftOvers );
498 void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows )
500 if( (nColumns < 0) || (nRows < 0) )
501 throw IllegalArgumentException();
503 if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
504 throw DisposedException();
506 SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
507 const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
509 if( bUndo )
510 rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
514 if( nColumns > 0 )
515 split_horizontal( nColumns );
517 if( nRows > 0 )
518 split_vertical( nRows );
520 if( nColumns > 0 ||nRows > 0 )
521 mxTable->setModified(true);
523 catch( Exception& )
525 OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
526 throw NoSupportException();
529 if( bUndo )
530 rModel.EndUndo();
532 rModel.SetChanged();
536 sal_Bool SAL_CALL CellCursor::isMergeable( )
538 CellPos aStart, aEnd;
539 return GetMergedSelection( aStart, aEnd );
545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */