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