Kaydet (Commit) 93f5cb55 authored tarafından Kohei Yoshida's avatar Kohei Yoshida Kaydeden (comit) Kohei Yoshida

tdf#107255: detect whether the range has only one data cell.

Change-Id: I030961d9d38b092ffdc966baa10decae0c2d070d
Reviewed-on: https://gerrit.libreoffice.org/37178Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarKohei Yoshida <libreoffice@kohei.us>
üst 6d424f07
...@@ -20,22 +20,20 @@ ...@@ -20,22 +20,20 @@
#ifndef INCLUDED_SC_INC_COLCONTAINER_HXX #ifndef INCLUDED_SC_INC_COLCONTAINER_HXX
#define INCLUDED_SC_INC_COLCONTAINER_HXX #define INCLUDED_SC_INC_COLCONTAINER_HXX
#include "types.hxx" #include "types.hxx"
#include "address.hxx" #include "address.hxx"
#include <vector> #include <vector>
class ScColumn; class ScColumn;
class ScDocument; class ScDocument;
class ScColContainer class ScColContainer
{ {
public:
typedef std::vector<ScColumn*> ScColumnVector; typedef std::vector<ScColumn*> ScColumnVector;
private:
ScColumnVector aCols; ScColumnVector aCols;
ScDocument* pDocument; ScDocument* pDocument;
public: public:
ScColContainer( ScDocument* pDoc, const size_t nSize ); ScColContainer( ScDocument* pDoc, const size_t nSize );
~ScColContainer(); ~ScColContainer();
...@@ -55,6 +53,11 @@ public: ...@@ -55,6 +53,11 @@ public:
return static_cast<SCCOL>( aCols.size() ); return static_cast<SCCOL>( aCols.size() );
} }
bool empty() const
{
return aCols.empty();
}
void resize( const size_t aNewSize ); void resize( const size_t aNewSize );
void Clear(); void Clear();
......
...@@ -381,6 +381,9 @@ public: ...@@ -381,6 +381,9 @@ public:
bool HasValueData( SCROW nRow ) const; bool HasValueData( SCROW nRow ) const;
bool HasStringCells( SCROW nStartRow, SCROW nEndRow ) 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; bool IsFormulaDirty( SCROW nRow ) const;
void CheckVectorizationState(); void CheckVectorizationState();
......
...@@ -1077,6 +1077,14 @@ public: ...@@ -1077,6 +1077,14 @@ public:
/** Returns true, if there is any data to create a selection list for rPos. */ /** Returns true, if there is any data to create a selection list for rPos. */
bool HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const; 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 **/ /** Notes **/
SC_DLLPUBLIC ScPostIt* GetNote(const ScAddress& rPos); SC_DLLPUBLIC ScPostIt* GetNote(const ScAddress& rPos);
SC_DLLPUBLIC ScPostIt* GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab); SC_DLLPUBLIC ScPostIt* GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab);
......
...@@ -557,6 +557,8 @@ public: ...@@ -557,6 +557,8 @@ public:
bool HasStringCells( SCCOL nStartCol, SCROW nStartRow, bool HasStringCells( SCCOL nStartCol, SCROW nStartRow,
SCCOL nEndCol, SCROW nEndRow ) const; SCCOL nEndCol, SCROW nEndRow ) const;
sc::MultiDataCellState HasMultipleDataCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const;
FormulaError GetErrCode( const ScAddress& rPos ) const FormulaError GetErrCode( const ScAddress& rPos ) const
{ {
return IsColRowValid(rPos.Col(),rPos.Row()) ? return IsColRowValid(rPos.Col(),rPos.Row()) ?
......
...@@ -102,6 +102,20 @@ struct RangeMatrix ...@@ -102,6 +102,20 @@ struct RangeMatrix
bool isRangeValid() const; 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 enum AreaOverlapType
{ {
AreaInside, AreaInside,
......
...@@ -39,6 +39,54 @@ bool ScColumn::IsMerged( SCROW nRow ) const ...@@ -39,6 +39,54 @@ bool ScColumn::IsMerged( SCROW nRow ) const
return pAttrArray->IsMerged(nRow); 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( void ScColumn::DeleteBeforeCopyFromClip(
sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans ) sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
{ {
......
...@@ -40,6 +40,21 @@ bool ScDocument::IsMerged( const ScAddress& rPos ) const ...@@ -40,6 +40,21 @@ bool ScDocument::IsMerged( const ScAddress& rPos ) const
return pTab->IsMerged(rPos.Col(), rPos.Row()); 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( void ScDocument::DeleteBeforeCopyFromClip(
sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans ) sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans )
{ {
......
...@@ -25,6 +25,70 @@ bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const ...@@ -25,6 +25,70 @@ bool ScTable::IsMerged( SCCOL nCol, SCROW nRow ) const
return aCol[nCol].IsMerged(nRow); 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( void ScTable::DeleteBeforeCopyFromClip(
sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans ) sc::CopyFromClipContext& rCxt, const ScTable& rClipTab, sc::ColumnSpanSet& rBroadcastSpans )
{ {
......
...@@ -22,6 +22,13 @@ bool RangeMatrix::isRangeValid() const ...@@ -22,6 +22,13 @@ bool RangeMatrix::isRangeValid() const
mnCol1 <= mnCol2 && mnRow1 <= mnRow2 && mnTab1 <= mnTab2; 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: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -1078,15 +1078,25 @@ bool ScViewFunc::MergeCells( bool bApi, bool& rDoContents, bool bCenter ) ...@@ -1078,15 +1078,25 @@ bool ScViewFunc::MergeCells( bool bApi, bool& rDoContents, bool bCenter )
SCTAB i = *itr; SCTAB i = *itr;
aMergeOption.maTabs.insert(i); 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; 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; bool bOk = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment