summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKohei Yoshida <kohei.yoshida@collabora.com>2017-05-01 19:43:16 -0400
committerKohei Yoshida <libreoffice@kohei.us>2017-05-03 02:36:51 +0200
commit93f5cb55349e6de5003182462bfee434dc51f6ad (patch)
tree5c2366da342a93fce034ada2ad7a497610a87c27
parent6d424f07701bf26d8fb173563b567d5f097c33e2 (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.hxx11
-rw-r--r--sc/inc/column.hxx3
-rw-r--r--sc/inc/document.hxx8
-rw-r--r--sc/inc/table.hxx2
-rw-r--r--sc/inc/types.hxx14
-rw-r--r--sc/source/core/data/column4.cxx48
-rw-r--r--sc/source/core/data/document10.cxx15
-rw-r--r--sc/source/core/data/table7.cxx64
-rw-r--r--sc/source/core/data/types.cxx7
-rw-r--r--sc/source/ui/view/viewfun2.cxx22
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;