summaryrefslogtreecommitdiffstats
path: root/svx/source/table/cellcursor.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--svx/source/table/cellcursor.cxx546
1 files changed, 546 insertions, 0 deletions
diff --git a/svx/source/table/cellcursor.cxx b/svx/source/table/cellcursor.cxx
new file mode 100644
index 000000000..313ec63df
--- /dev/null
+++ b/svx/source/table/cellcursor.cxx
@@ -0,0 +1,546 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <svx/svdotable.hxx>
+#include "cellcursor.hxx"
+#include "tablelayouter.hxx"
+#include <cell.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+
+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::table {
+
+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 )
+{
+ return CellRange::getCellByPosition( nColumn, nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
+{
+ return CellRange::getCellRangeByName( aRange );
+}
+
+
+// XCellCursor
+
+
+void SAL_CALL CellCursor::gotoStart( )
+{
+ mnRight = mnLeft;
+ mnBottom = mnTop;
+}
+
+
+void SAL_CALL CellCursor::gotoEnd( )
+{
+ mnLeft = mnRight;
+ mnTop = mnBottom;
+}
+
+
+void SAL_CALL CellCursor::gotoNext( )
+{
+ 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( )
+{
+ 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 )
+{
+ 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& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ return false;
+}
+
+
+void SAL_CALL CellCursor::merge( )
+{
+ CellPos aStart, aEnd;
+ if( !GetMergedSelection( aStart, aEnd ) )
+ throw NoSupportException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
+
+ try
+ {
+ mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
+ mxTable->optimize();
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.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 )
+ {
+ static const OUStringLiteral sWidth(u"Width");
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_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 )
+ {
+ static const OUStringLiteral sHeight(u"Height");
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_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 )
+{
+ if( (nColumns < 0) || (nRows < 0) )
+ throw IllegalArgumentException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
+
+ try
+ {
+ if( nColumns > 0 )
+ split_horizontal( nColumns );
+
+ if( nRows > 0 )
+ split_vertical( nRows );
+
+ if( nColumns > 0 ||nRows > 0 )
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ throw NoSupportException();
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+}
+
+
+sal_Bool SAL_CALL CellCursor::isMergeable( )
+{
+ CellPos aStart, aEnd;
+ return GetMergedSelection( aStart, aEnd );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */