sc/inc/colcontainer.hxx            |   11 ++++--
 sc/inc/column.hxx                  |    3 +
 sc/inc/document.hxx                |    8 ++++
 sc/inc/table.hxx                   |    2 +
 sc/inc/types.hxx                   |   14 ++++++++
 sc/source/core/data/column4.cxx    |   48 +++++++++++++++++++++++++++
 sc/source/core/data/document10.cxx |   15 ++++++++
 sc/source/core/data/table7.cxx     |   64 +++++++++++++++++++++++++++++++++++++
 sc/source/core/data/types.cxx      |    7 ++++
 sc/source/ui/view/viewfun2.cxx     |   22 +++++++++---
 10 files changed, 184 insertions(+), 10 deletions(-)

New commits:
commit 93f5cb55349e6de5003182462bfee434dc51f6ad
Author: Kohei Yoshida <kohei.yosh...@collabora.com>
Date:   Mon May 1 19:43:16 2017 -0400

    tdf#107255: detect whether the range has only one data cell.
    
    Change-Id: I030961d9d38b092ffdc966baa10decae0c2d070d
    Reviewed-on: https://gerrit.libreoffice.org/37178
    Tested-by: Jenkins <c...@libreoffice.org>
    Reviewed-by: Kohei Yoshida <libreoff...@kohei.us>

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;
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to