diff options
author | Kohei Yoshida <kohei.yoshida@collabora.com> | 2017-05-01 19:43:16 -0400 |
---|---|---|
committer | Kohei Yoshida <libreoffice@kohei.us> | 2017-05-03 02:36:51 +0200 |
commit | 93f5cb55349e6de5003182462bfee434dc51f6ad (patch) | |
tree | 5c2366da342a93fce034ada2ad7a497610a87c27 | |
parent | 6d424f07701bf26d8fb173563b567d5f097c33e2 (diff) |
tdf#107255: detect whether the range has only one data cell.
Change-Id: I030961d9d38b092ffdc966baa10decae0c2d070d
Reviewed-on: https://gerrit.libreoffice.org/37178
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Kohei Yoshida <libreoffice@kohei.us>
-rw-r--r-- | sc/inc/colcontainer.hxx | 11 | ||||
-rw-r--r-- | sc/inc/column.hxx | 3 | ||||
-rw-r--r-- | sc/inc/document.hxx | 8 | ||||
-rw-r--r-- | sc/inc/table.hxx | 2 | ||||
-rw-r--r-- | sc/inc/types.hxx | 14 | ||||
-rw-r--r-- | sc/source/core/data/column4.cxx | 48 | ||||
-rw-r--r-- | sc/source/core/data/document10.cxx | 15 | ||||
-rw-r--r-- | sc/source/core/data/table7.cxx | 64 | ||||
-rw-r--r-- | sc/source/core/data/types.cxx | 7 | ||||
-rw-r--r-- | sc/source/ui/view/viewfun2.cxx | 22 |
10 files changed, 184 insertions, 10 deletions
diff --git a/sc/inc/colcontainer.hxx b/sc/inc/colcontainer.hxx index 925104df2278..21463edb0bd4 100644 --- a/sc/inc/colcontainer.hxx +++ b/sc/inc/colcontainer.hxx @@ -20,22 +20,20 @@ #ifndef INCLUDED_SC_INC_COLCONTAINER_HXX #define INCLUDED_SC_INC_COLCONTAINER_HXX - #include "types.hxx" #include "address.hxx" #include <vector> - class ScColumn; class ScDocument; + class ScColContainer { -public: typedef std::vector<ScColumn*> ScColumnVector; -private: ScColumnVector aCols; ScDocument* pDocument; + public: ScColContainer( ScDocument* pDoc, const size_t nSize ); ~ScColContainer(); @@ -55,6 +53,11 @@ public: return static_cast<SCCOL>( aCols.size() ); } + bool empty() const + { + return aCols.empty(); + } + void resize( const size_t aNewSize ); void Clear(); diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index e7a81e877f13..564b6ff8114a 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -381,6 +381,9 @@ public: bool HasValueData( SCROW nRow ) const; bool HasStringCells( SCROW nStartRow, SCROW nEndRow ) const; + sc::MultiDataCellState::StateType HasDataCellsInRange( + SCROW nRow1, SCROW nRow2, SCROW* pRow1 = nullptr ) const; + bool IsFormulaDirty( SCROW nRow ) const; void CheckVectorizationState(); diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 2a39544cb7a7..cd4c5a237e04 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -1077,6 +1077,14 @@ public: /** Returns true, if there is any data to create a selection list for rPos. */ bool HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const; + /** + * Check if the specified range contains either: 1) one non-empty cell, 2) + * more than one non-empty cells, or 3) totally empty. In case the range + * contains at least one non-empty cell, specify the position of the first + * non-empty cell. + */ + sc::MultiDataCellState HasMultipleDataCells( const ScRange& rRange ) const; + /** Notes **/ SC_DLLPUBLIC ScPostIt* GetNote(const ScAddress& rPos); SC_DLLPUBLIC ScPostIt* GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab); diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 7addb6d6bf89..dda88eacdd81 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -557,6 +557,8 @@ public: bool HasStringCells( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const; + sc::MultiDataCellState HasMultipleDataCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const; + FormulaError GetErrCode( const ScAddress& rPos ) const { return IsColRowValid(rPos.Col(),rPos.Row()) ? diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx index fb86ec361c3d..53bb2616b040 100644 --- a/sc/inc/types.hxx +++ b/sc/inc/types.hxx @@ -102,6 +102,20 @@ struct RangeMatrix bool isRangeValid() const; }; +struct MultiDataCellState +{ + enum StateType { Invalid = 0, Empty, HasOneCell, HasMultipleCells }; + + StateType meState; + + SCCOL mnCol1; //< first non-empty column + SCROW mnRow1; //< first non-empty row + SCTAB mnTab1; //< first non-empty sheet + + MultiDataCellState(); + MultiDataCellState( StateType eState ); +}; + enum AreaOverlapType { AreaInside, diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx index e848ceb86343..71a33b3cd338 100644 --- a/sc/source/core/data/column4.cxx +++ b/sc/source/core/data/column4.cxx @@ -39,6 +39,54 @@ bool ScColumn::IsMerged( SCROW nRow ) const return pAttrArray->IsMerged(nRow); } +sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange( + SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const +{ + sc::CellStoreType::const_position_type aPos = maCells.position(nRow1); + sc::CellStoreType::const_iterator it = aPos.first; + size_t nOffset = aPos.second; + SCROW nRow = nRow1; + bool bHasOne = false; // whether or not we have found a non-empty block of size one. + + for (; it != maCells.end() && nRow <= nRow2; ++it) + { + if (it->type != sc::element_type_empty) + { + // non-empty block found. + assert(it->size > 0); // mtv should never contain a block of zero length. + size_t nSize = it->size - nOffset; + + SCROW nLastRow = nRow + nSize - 1; + if (nLastRow > nRow2) + // shrink the size to avoid exceeding the specified last row position. + nSize -= nLastRow - nRow2; + + if (nSize == 1) + { + // this block is of size one. + if (bHasOne) + return sc::MultiDataCellState::HasMultipleCells; + + bHasOne = true; + if (pRow1) + *pRow1 = nRow; + } + else + { + // size of this block is greater than one. + if (pRow1) + *pRow1 = nRow; + return sc::MultiDataCellState::HasMultipleCells; + } + } + + nRow += it->size - nOffset; + nOffset = 0; + } + + return bHasOne ? sc::MultiDataCellState::HasOneCell : sc::MultiDataCellState::Empty; +} + void ScColumn::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans ) { diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx index 0ceba39f8853..161729063e4b 100644 --- a/sc/source/core/data/document10.cxx +++ b/sc/source/core/data/document10.cxx @@ -40,6 +40,21 @@ bool ScDocument::IsMerged( const ScAddress& rPos ) const return pTab->IsMerged(rPos.Col(), rPos.Row()); } +sc::MultiDataCellState ScDocument::HasMultipleDataCells( const ScRange& rRange ) const +{ + if (rRange.aStart.Tab() != rRange.aEnd.Tab()) + // Currently we only support a single-sheet range. + return sc::MultiDataCellState(); + + const ScTable* pTab = FetchTable(rRange.aStart.Tab()); + if (!pTab) + return sc::MultiDataCellState(sc::MultiDataCellState::Empty); + + const ScAddress& s = rRange.aStart; + const ScAddress& e = rRange.aEnd; + return pTab->HasMultipleDataCells(s.Col(), s.Row(), e.Col(), e.Row()); +} + void ScDocument::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans ) { diff --git a/sc/source/core/data/table7.cxx b/sc/source/core/data/table7.cxx index 72595dae9b77..051e57bc7609 100644 --- a/sc/source/core/data/table7.cxx +++ b/sc/source/core/data/table7.cxx @@ -25,6 +25,70 @@ bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const return aCol[nCol].IsMerged(nRow); } +sc::MultiDataCellState ScTable::HasMultipleDataCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const +{ + if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2)) + return sc::MultiDataCellState(); + + if (nCol1 > nCol2 || nRow1 > nRow2) + // invalid range. + return sc::MultiDataCellState(); + + if (aCol.empty()) + return sc::MultiDataCellState(sc::MultiDataCellState::Empty); + + auto setFirstCell = []( sc::MultiDataCellState& rRet, SCCOL nCurCol, SCROW nCurRow, SCTAB nCurTab ) + { + if (rRet.mnCol1 < 0) + { + // First cell not yet set. Set it. + rRet.mnCol1 = nCurCol; + rRet.mnRow1 = nCurRow; + rRet.mnTab1 = nCurTab; + } + }; + + SCCOL nMaxCol = aCol.size()-1; + bool bHasOne = false; + sc::MultiDataCellState aRet(sc::MultiDataCellState::Empty); + + for (SCCOL nCol = nCol1; nCol <= nCol2 && nCol <= nMaxCol; ++nCol) + { + SCROW nFirstDataRow = -1; + switch (aCol[nCol].HasDataCellsInRange(nRow1, nRow2, &nFirstDataRow)) + { + case sc::MultiDataCellState::HasOneCell: + { + setFirstCell(aRet, nCol, nFirstDataRow, nTab); + + if (bHasOne) + { + // We've already found one data cell in another column. + aRet.meState = sc::MultiDataCellState::HasMultipleCells; + return aRet; + } + bHasOne = true; + break; + } + case sc::MultiDataCellState::HasMultipleCells: + { + setFirstCell(aRet, nCol, nFirstDataRow, nTab); + + aRet.meState = sc::MultiDataCellState::HasMultipleCells; + return aRet; + } + case sc::MultiDataCellState::Empty: + default: + ; + } + } + + if (bHasOne) + aRet.meState = sc::MultiDataCellState::HasOneCell; + + return aRet; +} + void ScTable::DeleteBeforeCopyFromClip( sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans ) { diff --git a/sc/source/core/data/types.cxx b/sc/source/core/data/types.cxx index 199819ea7506..6146f77b28ef 100644 --- a/sc/source/core/data/types.cxx +++ b/sc/source/core/data/types.cxx @@ -22,6 +22,13 @@ bool RangeMatrix::isRangeValid() const mnCol1 <= mnCol2 && mnRow1 <= mnRow2 && mnTab1 <= mnTab2; } +MultiDataCellState::MultiDataCellState() : + meState(StateType::Invalid), + mnCol1(-1), mnRow1(-1), mnTab1(-1) {} +MultiDataCellState::MultiDataCellState( StateType eState ) : + meState(eState), + mnCol1(-1), mnRow1(-1), mnTab1(-1) {} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx index 4e7f7a8866e9..09ec6c8eae51 100644 --- a/sc/source/ui/view/viewfun2.cxx +++ b/sc/source/ui/view/viewfun2.cxx @@ -1078,15 +1078,25 @@ bool ScViewFunc::MergeCells( bool bApi, bool& rDoContents, bool bCenter ) SCTAB i = *itr; aMergeOption.maTabs.insert(i); - if ( nEndRow == nStartRow ) + sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i)); + switch (aState.meState) { - if (!rDoc.IsBlockEmpty(i, nStartCol+1, nStartRow, nEndCol, nEndRow)) + case sc::MultiDataCellState::HasMultipleCells: + { + // this range contains multiple data cells. bAskDialog = true; + break; + } + case sc::MultiDataCellState::HasOneCell: + { + // this range contains only one data cell. + if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1) + rDoContents = true; // move the value to the top-left. + break; + } + default: + ; } - else - if (!rDoc.IsBlockEmpty(i, nStartCol, nStartRow+1, nStartCol, nEndRow) || - !rDoc.IsBlockEmpty(i, nStartCol+1, nStartRow, nEndCol, nEndRow)) - bAskDialog = true; } bool bOk = true; |