| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_svx.hxx" |
| |
| #include "svx/svdotable.hxx" |
| #include "cellcursor.hxx" |
| #include "tablelayouter.hxx" |
| #include "cell.hxx" |
| #include "svx/svdmodel.hxx" |
| #include "svx/svdstr.hrc" |
| #include "svx/svdglob.hxx" |
| |
| // ----------------------------------------------------------------------------- |
| |
| using ::rtl::OUString; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::lang; |
| using namespace ::com::sun::star::container; |
| using namespace ::com::sun::star::beans; |
| using namespace ::com::sun::star::table; |
| |
| // ----------------------------------------------------------------------------- |
| |
| namespace sdr { namespace table { |
| |
| // ----------------------------------------------------------------------------- |
| // CellCursor |
| // ----------------------------------------------------------------------------- |
| |
| CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) |
| : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| CellCursor::~CellCursor() |
| { |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // XCellCursor |
| // ----------------------------------------------------------------------------- |
| |
| Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException) |
| { |
| return CellRange::getCellByPosition( nColumn, nRow ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException) |
| { |
| return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange ) throw (RuntimeException) |
| { |
| return CellRange::getCellRangeByName( aRange ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // XCellCursor |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::gotoStart( ) throw (RuntimeException) |
| { |
| mnRight = mnLeft; |
| mnBottom = mnTop; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::gotoEnd( ) throw (RuntimeException) |
| { |
| mnLeft = mnRight; |
| mnTop = mnBottom; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::gotoNext( ) throw (RuntimeException) |
| { |
| if( mxTable.is() ) |
| { |
| mnRight++; |
| if( mnRight >= mxTable->getColumnCount() ) |
| { |
| // if we past the last column, try skip to the row line |
| mnTop++; |
| if( mnTop >= mxTable->getRowCount() ) |
| { |
| // if we past the last row, do not move cursor at all |
| mnTop--; |
| mnRight--; |
| } |
| else |
| { |
| // restart at the first column on the next row |
| mnRight = 0; |
| } |
| } |
| } |
| |
| mnLeft = mnRight; |
| mnTop = mnBottom; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::gotoPrevious( ) throw (RuntimeException) |
| { |
| if( mxTable.is() ) |
| { |
| if( mnLeft > 0 ) |
| { |
| --mnLeft; |
| } |
| else if( mnTop > 0 ) |
| { |
| --mnTop; |
| mnLeft = mxTable->getColumnCount() - 1; |
| } |
| } |
| |
| mnRight = mnLeft; |
| mnBottom = mnTop; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) throw (RuntimeException) |
| { |
| if( mxTable.is() ) |
| { |
| const sal_Int32 nLeft = mnLeft + nColumnOffset; |
| if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) ) |
| mnRight = mnLeft = nLeft; |
| |
| const sal_Int32 nTop = mnTop + nRowOffset; |
| if( (nTop >= 0) && (nTop < mxTable->getRowCount()) ) |
| mnTop = mnBottom = nTop; |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // XMergeableCellCursor |
| // ----------------------------------------------------------------------------- |
| |
| /** returns true and the merged cell positions if a merge is valid or false if a merge is |
| not valid for that range */ |
| bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd ) |
| { |
| rStart.mnCol = mnLeft; rStart.mnRow = mnTop; |
| rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom; |
| |
| // single cell merge is never valid |
| if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) ); |
| |
| // check if first cell is merged |
| if( xCell.is() && xCell->isMerged() ) |
| findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow ); |
| |
| // check if last cell is merged |
| xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) ); |
| if( xCell.is() ) |
| { |
| if( xCell->isMerged() ) |
| { |
| findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow ); |
| // merge not possible if selection is only one cell and all its merges |
| if( rEnd == rStart ) |
| return false; |
| xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) ); |
| } |
| } |
| if( xCell.is() ) |
| { |
| rEnd.mnCol += xCell->getColumnSpan()-1; |
| rEnd.mnRow += xCell->getRowSpan()-1; |
| } |
| |
| // now check if everything is inside the given bounds |
| sal_Int32 nRow, nCol; |
| for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ ) |
| { |
| for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ ) |
| { |
| xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( !xCell.is() ) |
| continue; |
| |
| if( xCell->isMerged() ) |
| { |
| sal_Int32 nOriginCol, nOriginRow; |
| if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) ) |
| { |
| if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) ) |
| return false; |
| |
| xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) ); |
| if( xCell.is() ) |
| { |
| nOriginCol += xCell->getColumnSpan()-1; |
| nOriginRow += xCell->getRowSpan()-1; |
| |
| if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) ) |
| return false; |
| } |
| } |
| } |
| else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) ) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| catch( Exception& ) |
| { |
| DBG_ERROR("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!"); |
| } |
| return false; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::merge( ) throw (NoSupportException, RuntimeException) |
| { |
| CellPos aStart, aEnd; |
| if( !GetMergedSelection( aStart, aEnd ) ) |
| throw NoSupportException(); |
| |
| if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) ) |
| throw DisposedException(); |
| |
| SdrModel* pModel = mxTable->getSdrTableObj()->GetModel(); |
| const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled(); |
| |
| if( bUndo ) |
| pModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) ); |
| |
| try |
| { |
| mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 ); |
| mxTable->optimize(); |
| mxTable->setModified(sal_True); |
| } |
| catch( Exception& ) |
| { |
| DBG_ERROR("sdr::table::CellCursor::merge(), exception caught!"); |
| } |
| |
| if( bUndo ) |
| pModel->EndUndo(); |
| |
| if( pModel ) |
| pModel->SetChanged(); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers ) |
| { |
| const sal_Int32 nRowCount = mxTable->getRowCount(); |
| |
| sal_Int32 nNewCols = 0, nRow; |
| |
| // first check how many columns we need to add |
| for( nRow = mnTop; nRow <= mnBottom; ++nRow ) |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( xCell.is() && !xCell->isMerged() ) |
| nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] ); |
| } |
| |
| if( nNewCols > 0 ) |
| { |
| const OUString sWidth( RTL_CONSTASCII_USTRINGPARAM("Width") ); |
| Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW ); |
| Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW ); |
| sal_Int32 nWidth = 0; |
| xRefColumn->getPropertyValue( sWidth ) >>= nWidth; |
| const sal_Int32 nNewWidth = nWidth / (nNewCols + 1); |
| |
| // reference column gets new width + rounding errors |
| xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) ); |
| |
| xCols->insertByIndex( nCol + 1, nNewCols ); |
| mnRight += nNewCols; |
| |
| // distribute new width |
| for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol ) |
| { |
| Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW ); |
| xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) ); |
| } |
| } |
| |
| for( nRow = 0; nRow < nRowCount; ++nRow ) |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( !xCell.is() || xCell->isMerged() ) |
| { |
| if( nNewCols > 0 ) |
| { |
| // merged cells are ignored, but newly added columns will be added to leftovers |
| xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) ); |
| if( !xCell.is() || !xCell->isMerged() ) |
| rLeftOvers[nRow] += nNewCols; |
| } |
| } |
| else |
| { |
| sal_Int32 nRowSpan = xCell->getRowSpan() - 1; |
| sal_Int32 nColSpan = xCell->getColumnSpan() - 1; |
| |
| if( (nRow >= mnTop) && (nRow <= mnBottom) ) |
| { |
| sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow]; |
| if( nColSpan == 0 ) |
| nCellsAvailable += nNewCols; |
| |
| DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" ); |
| |
| sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1; |
| |
| sal_Int32 nSplitCol = nCol; |
| sal_Int32 nSplits = nColumns + 1; |
| while( nSplits-- ) |
| { |
| // last split eats rounding cells |
| if( nSplits == 0 ) |
| nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1; |
| |
| mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1); |
| if( nSplits > 0 ) |
| nSplitCol += nSplitSpan + 1; |
| } |
| |
| do |
| { |
| rLeftOvers[nRow++] = 0; |
| } |
| while( nRowSpan-- ); |
| --nRow; |
| } |
| else |
| { |
| // cope with outside cells, merge if needed |
| if( nColSpan < (rLeftOvers[nRow] + nNewCols) ) |
| mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 ); |
| |
| do |
| { |
| rLeftOvers[nRow++] = 0; // consumed |
| } |
| while( nRowSpan-- ); |
| --nRow; |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void CellCursor::split_horizontal( sal_Int32 nColumns ) |
| { |
| const sal_Int32 nRowCount = mxTable->getRowCount(); |
| |
| std::vector< sal_Int32 > aLeftOvers( nRowCount ); |
| |
| for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol ) |
| split_column( nCol, nColumns, aLeftOvers ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers ) |
| { |
| const sal_Int32 nColCount = mxTable->getColumnCount(); |
| |
| sal_Int32 nNewRows = 0, nCol; |
| |
| // first check how many columns we need to add |
| for( nCol = mnLeft; nCol <= mnRight; ++nCol ) |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( xCell.is() && !xCell->isMerged() ) |
| nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] ); |
| } |
| |
| if( nNewRows > 0 ) |
| { |
| const OUString sHeight( RTL_CONSTASCII_USTRINGPARAM("Height") ); |
| Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW ); |
| Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW ); |
| sal_Int32 nHeight = 0; |
| xRefRow->getPropertyValue( sHeight ) >>= nHeight; |
| const sal_Int32 nNewHeight = nHeight / (nNewRows + 1); |
| |
| // reference row gets new height + rounding errors |
| xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) ); |
| |
| xRows->insertByIndex( nRow + 1, nNewRows ); |
| mnBottom += nNewRows; |
| |
| // distribute new width |
| for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow ) |
| { |
| Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW ); |
| xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) ); |
| } |
| } |
| |
| for( nCol = 0; nCol < nColCount; ++nCol ) |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( !xCell.is() || xCell->isMerged() ) |
| { |
| if( nNewRows ) |
| { |
| // merged cells are ignored, but newly added columns will be added to leftovers |
| xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) ); |
| if( !xCell.is() || !xCell->isMerged() ) |
| rLeftOvers[nCol] += nNewRows; |
| } |
| } |
| else |
| { |
| sal_Int32 nRowSpan = xCell->getRowSpan() - 1; |
| sal_Int32 nColSpan = xCell->getColumnSpan() - 1; |
| |
| if( (nCol >= mnLeft) && (nCol <= mnRight) ) |
| { |
| sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol]; |
| if( nRowSpan == 0 ) |
| nCellsAvailable += nNewRows; |
| |
| DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" ); |
| |
| sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1; |
| |
| sal_Int32 nSplitRow = nRow; |
| sal_Int32 nSplits = nRows + 1; |
| while( nSplits-- ) |
| { |
| // last split eats rounding cells |
| if( nSplits == 0 ) |
| nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1; |
| |
| mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 ); |
| if( nSplits > 0 ) |
| nSplitRow += nSplitSpan + 1; |
| } |
| |
| do |
| { |
| rLeftOvers[nCol++] = 0; |
| } |
| while( nColSpan-- ); |
| --nCol; |
| } |
| else |
| { |
| // cope with outside cells, merge if needed |
| if( nRowSpan < (rLeftOvers[nCol] + nNewRows) ) |
| mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 ); |
| |
| do |
| { |
| rLeftOvers[nCol++] = 0; // consumed |
| } |
| while( nColSpan-- ); |
| --nCol; |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void CellCursor::split_vertical( sal_Int32 nRows ) |
| { |
| const sal_Int32 nColCount = mxTable->getColumnCount(); |
| |
| std::vector< sal_Int32 > aLeftOvers( nColCount ); |
| |
| for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow ) |
| split_row( nRow, nRows, aLeftOvers ); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows ) throw (NoSupportException, IllegalArgumentException, RuntimeException) |
| { |
| if( (nColumns < 0) || (nRows < 0) ) |
| throw IllegalArgumentException(); |
| |
| if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) ) |
| throw DisposedException(); |
| |
| SdrModel* pModel = mxTable->getSdrTableObj()->GetModel(); |
| const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled(); |
| if( bUndo ) |
| pModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) ); |
| |
| try |
| { |
| if( nColumns > 0 ) |
| split_horizontal( nColumns ); |
| |
| if( nRows > 0 ) |
| split_vertical( nRows ); |
| |
| if( nColumns > 0 ||nRows > 0 ) |
| mxTable->setModified(sal_True); |
| } |
| catch( Exception& ) |
| { |
| DBG_ERROR("sdr::table::CellCursor::split(), exception caught!"); |
| throw NoSupportException(); |
| } |
| |
| if( bUndo ) |
| pModel->EndUndo(); |
| |
| if( pModel ) |
| pModel->SetChanged(); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL CellCursor::isMergeable( ) throw (RuntimeException) |
| { |
| CellPos aStart, aEnd; |
| return GetMergedSelection( aStart, aEnd ) ? sal_True : sal_False; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| sal_Bool SAL_CALL CellCursor::isUnmergeable( ) throw (RuntimeException) |
| { |
| // this is true if there is at least one merged cell in the current range |
| for( sal_Int32 nRow = mnTop; nRow <= mnBottom; nRow++ ) |
| { |
| for( sal_Int32 nCol = mnLeft; nCol <= mnRight; nCol++ ) |
| { |
| CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) ); |
| if( xCell.is() && ( (xCell->getRowSpan() > 1) || (xCell->getColumnSpan() > 1) ) ) |
| return sal_True; |
| } |
| } |
| return sal_False; |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| } } |