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