[Libreoffice-commits] core.git: Branch 'libreoffice-4-2' - sc/inc sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Wed Feb 26 01:55:11 PST 2014


 sc/inc/formulacell.hxx                  |   12 -
 sc/inc/listenercontext.hxx              |   11 +
 sc/inc/refupdatecontext.hxx             |   18 ++
 sc/source/core/data/column.cxx          |  234 +++++++++++++++++++++++++++-----
 sc/source/core/data/formulacell.cxx     |   17 +-
 sc/source/core/data/listenercontext.cxx |   24 +++
 sc/source/core/tool/token.cxx           |   17 +-
 sc/source/ui/inc/undoblk.hxx            |    5 
 sc/source/ui/undo/undoblk.cxx           |   19 +-
 9 files changed, 293 insertions(+), 64 deletions(-)

New commits:
commit df9206c4ad9227029f85caa84df358e66ecafe9d
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Tue Feb 25 12:53:33 2014 -0500

    fdo#75386: Totally fix reference update during range move.
    
    It was just not working at all due to multiple reasons.  The
    reference update needed to be reworked for formula groups such that
    the token array is adjusted only for the top cell but all formula cells
    still needed to be processed afterwards.  The bound check also needed
    to be done against the old range prior to the move, not the new range
    after the move.
    
    During undo, the paint had to be deferred until after the two calls to
    DoUndo() else the formula cells would get re-calculated before the
    values were placed back to their old positions, causing them to mis-
    calculate wrong values.
    
    Change-Id: Iba66f80a413e0539cac5ab619226cd6f7a04f317
    (cherry picked from commit 79d03eb090a5f88863c1004ef8b7f483cbecb69d)
    Reviewed-on: https://gerrit.libreoffice.org/8352
    Reviewed-by: Andras Timar <andras.timar at collabora.com>
    Tested-by: Andras Timar <andras.timar at collabora.com>

diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index e901c6e..7e40ddd 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -141,12 +141,6 @@ private:
         const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos );
 
     /**
-     * Update reference in response to cell move.
-     */
-    bool UpdateReferenceOnMove(
-        const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos );
-
-    /**
      * Update reference in response to cell copy-n-paste.
      */
     bool UpdateReferenceOnCopy(
@@ -252,6 +246,12 @@ public:
     bool UpdateReference(
         const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc = NULL, const ScAddress* pUndoCellPos = NULL );
 
+    /**
+     * Update reference in response to cell move.
+     */
+    bool UpdateReferenceOnMove(
+        const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos );
+
     void            TransposeReference();
     void            UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
                                         ScDocument* pUndoDoc );
diff --git a/sc/inc/listenercontext.hxx b/sc/inc/listenercontext.hxx
index 1e7d067..c0260ef 100644
--- a/sc/inc/listenercontext.hxx
+++ b/sc/inc/listenercontext.hxx
@@ -17,6 +17,7 @@
 #include <boost/scoped_ptr.hpp>
 
 class ScDocument;
+class ScTokenArray;
 
 namespace sc {
 
@@ -39,9 +40,17 @@ class EndListeningContext : boost::noncopyable
     ScDocument& mrDoc;
     ColumnSpanSet maSet;
     boost::scoped_ptr<ColumnBlockPositionSet> mpPosSet;
+    ScTokenArray* mpOldCode;
+    ScAddress maPosDelta; // Add this to get the old position prior to the move.
+
 public:
-    EndListeningContext(ScDocument& rDoc);
+    EndListeningContext(ScDocument& rDoc, ScTokenArray* pOldCode = NULL);
+
+    void setPositionDelta( const ScAddress& rDelta );
+
     ScDocument& getDoc();
+    ScTokenArray* getOldCode();
+    ScAddress getOldPosition( const ScAddress& rPos ) const;
 
     ColumnBlockPosition* getBlockPosition(SCTAB nTab, SCCOL nCol);
 
diff --git a/sc/inc/refupdatecontext.hxx b/sc/inc/refupdatecontext.hxx
index ba0beed..c8e52d8 100644
--- a/sc/inc/refupdatecontext.hxx
+++ b/sc/inc/refupdatecontext.hxx
@@ -55,7 +55,8 @@ struct RefUpdateContext
     /**
      * Range of cells that are about to be moved for insert/delete/move modes.
      * For copy mode, it's the destination range of cells that are about to be
-     * pasted.
+     * pasted.  When moving a range of cells, it's the destination range, not
+     * the source range.
      */
     ScRange maRange;
 
@@ -77,8 +78,23 @@ struct RefUpdateContext
 
 struct RefUpdateResult
 {
+    /**
+     * When this flag is true, the result of the formula needs to be
+     * re-calculated either because it contains a reference that's been
+     * deleted, or the size of a range reference has changed.
+     */
     bool mbValueChanged;
+
+    /**
+     * This flag indicates whether any reference in the token array has been
+     * modified.
+     */
     bool mbReferenceModified;
+
+    /**
+     * When this flag is true, it indicates that the token array contains a
+     * range name that's been updated.
+     */
     bool mbNameModified;
 
     RefUpdateResult();
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 47ef9cb..db8cf96 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -41,6 +41,7 @@
 #include "scopetools.hxx"
 #include "sharedformula.hxx"
 #include "refupdatecontext.hxx"
+#include <listenercontext.hxx>
 
 #include <svl/poolcach.hxx>
 #include <svl/zforlist.hxx>
@@ -2328,6 +2329,67 @@ void ScColumn::MarkSubTotalCells( sc::ColumnSpanSet& rSet, SCROW nRow1, SCROW nR
 
 namespace {
 
+struct FormulaGroup
+{
+    struct {
+        ScFormulaCell* mpCell;   // non-shared formula cell
+        ScFormulaCell** mpCells; // pointer to the top formula cell in a shared group.
+    };
+    size_t mnRow;
+    size_t mnLength;
+    bool mbShared;
+
+    FormulaGroup( ScFormulaCell** pCells, size_t nRow, size_t nLength ) :
+        mpCells(pCells), mnRow(nRow), mnLength(nLength), mbShared(true) {}
+
+    FormulaGroup( ScFormulaCell* pCell, size_t nRow ) :
+        mpCell(pCell), mnRow(nRow), mnLength(0), mbShared(false) {}
+};
+
+class SharedTopFormulaCellPicker : std::unary_function<sc::CellStoreType::value_type, void>
+{
+public:
+    virtual ~SharedTopFormulaCellPicker() {}
+
+    void operator() ( sc::CellStoreType::value_type& node )
+    {
+        if (node.type != sc::element_type_formula)
+            return;
+
+        size_t nTopRow = node.position;
+
+        sc::formula_block::iterator itBeg = sc::formula_block::begin(*node.data);
+        sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
+
+        // Only pick shared formula cells that are the top cells of their
+        // respective shared ranges.
+        for (sc::formula_block::iterator it = itBeg; it != itEnd; ++it)
+        {
+            ScFormulaCell* pCell = *it;
+            size_t nRow = nTopRow + std::distance(itBeg, it);
+            if (!pCell->IsShared())
+            {
+                processNonShared(pCell, nRow);
+                continue;
+            }
+
+            if (pCell->IsSharedTop())
+            {
+                ScFormulaCell** pp = &(*it);
+                processSharedTop(pp, nRow, pCell->GetSharedLength());
+
+                // Move to the last cell in the group, to get incremented to
+                // the next cell in the next iteration.
+                size_t nOffsetToLast = pCell->GetSharedLength() - 1;
+                std::advance(it, nOffsetToLast);
+            }
+        }
+    }
+
+    virtual void processNonShared( ScFormulaCell* /*pCell*/, size_t /*nRow*/ ) {}
+    virtual void processSharedTop( ScFormulaCell** /*ppCells*/, size_t /*nRow*/, size_t /*nLength*/ ) {}
+};
+
 class UpdateRefOnCopy
 {
     const sc::RefUpdateContext& mrCxt;
@@ -2358,67 +2420,162 @@ public:
     }
 };
 
-class UpdateRefOnNonCopy
+class UpdateRefOnNonCopy : std::unary_function<FormulaGroup, void>
 {
     SCCOL mnCol;
     SCROW mnTab;
-    const sc::RefUpdateContext& mrCxt;
+    const sc::RefUpdateContext* mpCxt;
     ScDocument* mpUndoDoc;
     bool mbUpdated;
 
+    void updateRefOnMove( FormulaGroup& rGroup )
+    {
+        if (!rGroup.mbShared)
+        {
+            ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+            mbUpdated |= rGroup.mpCell->UpdateReferenceOnMove(*mpCxt, mpUndoDoc, &aUndoPos);
+            return;
+        }
+
+        // Update references of a formula group.
+        ScFormulaCell** pp = rGroup.mpCells;
+        ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+        ScFormulaCell* pTop = *pp;
+        ScTokenArray* pCode = pTop->GetCode();
+        boost::scoped_ptr<ScTokenArray> pOldCode(pCode->Clone());
+
+        ScAddress aPos = pTop->aPos;
+        ScAddress aOldPos = aPos;
+
+        if (mpCxt->maRange.In(aPos))
+        {
+            // The cell is being moved or copied to a new position. The
+            // position has already been updated prior to this call.
+            // Determine its original position before the move which will be
+            // used to adjust relative references later.
+
+            aOldPos.Set(
+                aPos.Col() - mpCxt->mnColDelta,
+                aPos.Row() - mpCxt->mnRowDelta,
+                aPos.Tab() - mpCxt->mnTabDelta);
+        }
+
+        bool bRecalcOnMove = pCode->IsRecalcModeOnRefMove();
+        if (bRecalcOnMove)
+            bRecalcOnMove = aPos != aOldPos;
+
+        sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(*mpCxt, aOldPos, aPos);
+        if (aRes.mbReferenceModified || bRecalcOnMove)
+        {
+            // Perform end-listening, start-listening, and dirtying on all
+            // formula cells in the group.
+
+            sc::StartListeningContext aStartCxt(mpCxt->mrDoc);
+
+            sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pOldCode.get());
+            aEndCxt.setPositionDelta(
+                ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
+
+            for (; pp != ppEnd; ++pp)
+            {
+                ScFormulaCell* p = *pp;
+                p->EndListeningTo(aEndCxt);
+                p->StartListeningTo(aStartCxt);
+                p->SetDirty();
+            }
+
+            if (mpUndoDoc)
+            {
+                // Insert the old formula group into the undo document.
+                ScAddress aUndoPos = aOldPos;
+                ScFormulaCell* pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, pOldCode->Clone());
+                ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(rGroup.mnLength, false);
+
+                mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
+                aUndoPos.IncRow();
+                for (size_t i = 1; i < rGroup.mnLength; ++i, aUndoPos.IncRow())
+                {
+                    pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, xGroup);
+                    mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
+                }
+            }
+        }
+    }
+
 public:
     UpdateRefOnNonCopy(
-        SCCOL nCol, SCTAB nTab, const sc::RefUpdateContext& rCxt,
+        SCCOL nCol, SCTAB nTab, const sc::RefUpdateContext* pCxt,
         ScDocument* pUndoDoc) :
-        mnCol(nCol), mnTab(nTab), mrCxt(rCxt),
+        mnCol(nCol), mnTab(nTab), mpCxt(pCxt),
         mpUndoDoc(pUndoDoc), mbUpdated(false) {}
 
-    void operator() (size_t nRow, ScFormulaCell* pCell)
+    void operator() ( FormulaGroup& rGroup )
     {
-        ScAddress aUndoPos(mnCol, nRow, mnTab);
-        mbUpdated |= pCell->UpdateReference(mrCxt, mpUndoDoc, &aUndoPos);
+        if (mpCxt->meMode == URM_MOVE)
+        {
+            updateRefOnMove(rGroup);
+            return;
+        }
+
+        if (rGroup.mbShared)
+        {
+            ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+            ScFormulaCell** pp = rGroup.mpCells;
+            ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+            for (; pp != ppEnd; ++pp, aUndoPos.IncRow())
+            {
+                ScFormulaCell* p = *pp;
+                mbUpdated |= p->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+            }
+        }
+        else
+        {
+            ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+            mbUpdated |= rGroup.mpCell->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+        }
     }
 
     bool isUpdated() const { return mbUpdated; }
 };
 
-class UpdateRefGroupBoundChecker : std::unary_function<sc::CellStoreType::value_type, void>
+class UpdateRefGroupBoundChecker : public SharedTopFormulaCellPicker
 {
     const sc::RefUpdateContext& mrCxt;
     std::vector<SCROW>& mrBounds;
+
 public:
     UpdateRefGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
         mrCxt(rCxt), mrBounds(rBounds) {}
 
-    void operator() (const sc::CellStoreType::value_type& node)
+    virtual ~UpdateRefGroupBoundChecker() {}
+
+    virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ )
     {
-        if (node.type != sc::element_type_formula)
-            return;
+        // Check its tokens and record its reference boundaries.
+        ScFormulaCell& rCell = **ppCells;
+        const ScTokenArray& rCode = *rCell.GetCode();
+        rCode.CheckRelativeReferenceBounds(
+            mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+    }
+};
 
-        sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
-        sc::formula_block::const_iterator itEnd = sc::formula_block::end(*node.data);
+class FormulaGroupPicker : public SharedTopFormulaCellPicker
+{
+    std::vector<FormulaGroup>& mrGroups;
 
-        // Only pick shared formula cells that are the top cells of their
-        // respective shared ranges.
-        for (; it != itEnd; ++it)
-        {
-            const ScFormulaCell& rCell = **it;
-            if (!rCell.IsShared())
-                continue;
+public:
+    FormulaGroupPicker( std::vector<FormulaGroup>& rGroups ) : mrGroups(rGroups) {}
 
-            if (rCell.IsSharedTop())
-            {
-                // Check its tokens and record its reference boundaries.
-                const ScTokenArray& rCode = *rCell.GetCode();
-                rCode.CheckRelativeReferenceBounds(
-                    mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+    virtual ~FormulaGroupPicker() {}
 
-                // Move to the last cell in the group, to get incremented to
-                // the next cell in the next iteration.
-                size_t nOffsetToLast = rCell.GetSharedLength() - 1;
-                std::advance(it, nOffsetToLast);
-            }
-        }
+    virtual void processNonShared( ScFormulaCell* pCell, size_t nRow )
+    {
+        mrGroups.push_back(FormulaGroup(pCell, nRow));
+    }
+
+    virtual void processSharedTop( ScFormulaCell** ppCells, size_t nRow, size_t nLength )
+    {
+        mrGroups.push_back(FormulaGroup(ppCells, nRow, nLength));
     }
 };
 
@@ -2451,8 +2608,8 @@ bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc
     if (rCxt.meMode == URM_COPY)
         return UpdateReferenceOnCopy(rCxt, pUndoDoc);
 
-    if (IsEmptyData())
-        // Cells in this column are all empty.
+    if (IsEmptyData() || pDocument->IsClipOrUndo())
+        // Cells in this column are all empty, or clip or undo doc. No update needed.
         return false;
 
     std::vector<SCROW> aBounds;
@@ -2490,8 +2647,13 @@ bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc
     // Do the actual splitting.
     sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
 
-    UpdateRefOnNonCopy aHandler(nCol, nTab, rCxt, pUndoDoc);
-    sc::ProcessFormula(maCells, aHandler);
+    // Collect all formula groups.
+    std::vector<FormulaGroup> aGroups;
+    std::for_each(maCells.begin(), maCells.end(), FormulaGroupPicker(aGroups));
+
+    // Process all collected formula groups.
+    UpdateRefOnNonCopy aHandler(nCol, nTab, &rCxt, pUndoDoc);
+    aHandler = std::for_each(aGroups.begin(), aGroups.end(), aHandler);
     if (aHandler.isUpdated())
         rCxt.maRegroupCols.set(nTab, nCol);
 
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 0de5c87..6da276a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3944,7 +3944,7 @@ void ScFormulaCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr,
         {
             case svSingleRef:
             {
-                ScAddress aCell = t->GetSingleRef().toAbs(aPos);
+                ScAddress aCell = t->GetSingleRef().toAbs(aCellPos);
                 if (aCell.IsValid())
                     pDoc->EndListeningCell(aCell, this);
             }
@@ -3966,27 +3966,32 @@ void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt )
     ScDocument& rDoc = rCxt.getDoc();
     rDoc.SetDetectiveDirty(true);  // It has changed something
 
-    if (pCode->IsRecalcModeAlways())
+    ScTokenArray* pArr = rCxt.getOldCode();
+    ScAddress aCellPos = rCxt.getOldPosition(aPos);
+    if (!pArr)
+        pArr = pCode;
+
+    if (pArr->IsRecalcModeAlways())
     {
         rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, this);
         return;
     }
 
-    pCode->Reset();
+    pArr->Reset();
     ScToken* t;
-    while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL )
+    while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
     {
         switch (t->GetType())
         {
             case svSingleRef:
             {
-                ScAddress aCell = t->GetSingleRef().toAbs(aPos);
+                ScAddress aCell = t->GetSingleRef().toAbs(aCellPos);
                 if (aCell.IsValid())
                     rDoc.EndListeningCell(rCxt, aCell, *this);
             }
             break;
             case svDoubleRef:
-                endListeningArea(this, rDoc, aPos, *t);
+                endListeningArea(this, rDoc, aCellPos, *t);
             break;
             default:
                 ;   // nothing
diff --git a/sc/source/core/data/listenercontext.cxx b/sc/source/core/data/listenercontext.cxx
index dc92346..3dfe5ed 100644
--- a/sc/source/core/data/listenercontext.cxx
+++ b/sc/source/core/data/listenercontext.cxx
@@ -52,14 +52,34 @@ ColumnBlockPosition* StartListeningContext::getBlockPosition(SCTAB nTab, SCCOL n
     return mpSet->getBlockPosition(nTab, nCol);
 }
 
-EndListeningContext::EndListeningContext(ScDocument& rDoc) :
-    mrDoc(rDoc), maSet(false), mpPosSet(new ColumnBlockPositionSet(rDoc)) {}
+EndListeningContext::EndListeningContext(ScDocument& rDoc, ScTokenArray* pOldCode) :
+    mrDoc(rDoc), maSet(false), mpPosSet(new ColumnBlockPositionSet(rDoc)),
+    mpOldCode(pOldCode), maPosDelta(0,0,0) {}
+
+void EndListeningContext::setPositionDelta( const ScAddress& rDelta )
+{
+    maPosDelta = rDelta;
+}
 
 ScDocument& EndListeningContext::getDoc()
 {
     return mrDoc;
 }
 
+ScTokenArray* EndListeningContext::getOldCode()
+{
+    return mpOldCode;
+}
+
+ScAddress EndListeningContext::getOldPosition( const ScAddress& rPos ) const
+{
+    ScAddress aOldPos = rPos;
+    aOldPos.IncCol(maPosDelta.Col());
+    aOldPos.IncRow(maPosDelta.Row());
+    aOldPos.IncTab(maPosDelta.Tab());
+    return aOldPos;
+}
+
 ColumnBlockPosition* EndListeningContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
 {
     return mpPosSet->getBlockPosition(nTab, nCol);
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index fc9ad75..92a9c77 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -3155,17 +3155,22 @@ void checkBounds(
     if (!rRef.IsRowRel())
         return;
 
+    ScRange aCheckRange = rCxt.maRange;
+    if (rCxt.meMode == URM_MOVE)
+        // Check bounds against the old range prior to the move.
+        aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta);
+
     ScRange aAbs(rRef.toAbs(rPos));
     aAbs.aEnd.IncRow(nGroupLen-1);
-    if (!rCxt.maRange.Intersects(aAbs))
+    if (!aCheckRange.Intersects(aAbs))
         return;
 
     // Get the boundary row positions.
-    if (aAbs.aEnd.Row() < rCxt.maRange.aStart.Row())
+    if (aAbs.aEnd.Row() < aCheckRange.aStart.Row())
         // No intersections.
         return;
 
-    if (aAbs.aStart.Row() <= rCxt.maRange.aStart.Row())
+    if (aAbs.aStart.Row() <= aCheckRange.aStart.Row())
     {
         //    +-+ <---- top
         //    | |
@@ -3175,11 +3180,11 @@ void checkBounds(
         // +-------+
 
         // Add offset from the reference top to the cell position.
-        SCROW nOffset = rCxt.maRange.aStart.Row() - aAbs.aStart.Row();
+        SCROW nOffset = aCheckRange.aStart.Row() - aAbs.aStart.Row();
         rBounds.push_back(rPos.Row()+nOffset);
     }
 
-    if (aAbs.aEnd.Row() >= rCxt.maRange.aEnd.Row())
+    if (aAbs.aEnd.Row() >= aCheckRange.aEnd.Row())
     {
         // only check for end range
 
@@ -3191,7 +3196,7 @@ void checkBounds(
         //    +-+
 
         // Ditto.
-        SCROW nOffset = rCxt.maRange.aEnd.Row() + 1 - aAbs.aStart.Row();
+        SCROW nOffset = aCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
         rBounds.push_back(rPos.Row()+nOffset);
     }
 }
diff --git a/sc/source/ui/inc/undoblk.hxx b/sc/source/ui/inc/undoblk.hxx
index 1b10216..758e5bb 100644
--- a/sc/source/ui/inc/undoblk.hxx
+++ b/sc/source/ui/inc/undoblk.hxx
@@ -239,6 +239,9 @@ public:
     virtual OUString GetComment() const;
 
 private:
+    sal_uInt16 mnPaintExtFlags;
+    ScRangeList maPaintRanges;
+
     ScRange         aSrcRange;
     ScRange         aDestRange;
     sal_uLong           nStartChangeAction;
@@ -247,7 +250,7 @@ private:
     sal_Bool            bKeepScenarioFlags;
 
     void            PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const;
-    void            DoUndo( ScRange aRange ) const;
+    void DoUndo( ScRange aRange );
 
     void            SetChangeTrack();
 };
diff --git a/sc/source/ui/undo/undoblk.cxx b/sc/source/ui/undo/undoblk.cxx
index 6497f0e..aebdb3e 100644
--- a/sc/source/ui/undo/undoblk.cxx
+++ b/sc/source/ui/undo/undoblk.cxx
@@ -1218,7 +1218,7 @@ void ScUndoDragDrop::PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const
     pDocShell->PostPaint( aRange, nPaint, nExtFlags );
 }
 
-void ScUndoDragDrop::DoUndo( ScRange aRange ) const
+void ScUndoDragDrop::DoUndo( ScRange aRange )
 {
     ScDocument* pDoc = pDocShell->GetDocument();
 
@@ -1231,8 +1231,7 @@ void ScUndoDragDrop::DoUndo( ScRange aRange ) const
     ScRange aPaintRange = aRange;
     pDoc->ExtendMerge( aPaintRange );           // before deleting
 
-    sal_uInt16 nExtFlags = 0;
-    pDocShell->UpdatePaintExt( nExtFlags, aPaintRange );
+    pDocShell->UpdatePaintExt(mnPaintExtFlags, aPaintRange);
 
     // do not undo objects and note captions, they are handled via drawing undo
     sal_uInt16 nUndoFlags = (IDF_ALL & ~IDF_OBJECTS) | IDF_NOCAPTIONS;
@@ -1245,16 +1244,26 @@ void ScUndoDragDrop::DoUndo( ScRange aRange ) const
     aPaintRange.aEnd.SetCol( std::max( aPaintRange.aEnd.Col(), aRange.aEnd.Col() ) );
     aPaintRange.aEnd.SetRow( std::max( aPaintRange.aEnd.Row(), aRange.aEnd.Row() ) );
 
-    pDocShell->UpdatePaintExt( nExtFlags, aPaintRange );
-    PaintArea( aPaintRange, nExtFlags );
+    pDocShell->UpdatePaintExt(mnPaintExtFlags, aPaintRange);
+    maPaintRanges.Join(aPaintRange);
 }
 
 void ScUndoDragDrop::Undo()
 {
+    mnPaintExtFlags = 0;
+    maPaintRanges.RemoveAll();
+
     BeginUndo();
     DoUndo(aDestRange);
     if (bCut)
         DoUndo(aSrcRange);
+
+    for (size_t i = 0; i < maPaintRanges.size(); ++i)
+    {
+        const ScRange* p = maPaintRanges[i];
+        PaintArea(*p, mnPaintExtFlags);
+    }
+
     EndUndo();
     SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_AREALINKS_CHANGED ) );
 }


More information about the Libreoffice-commits mailing list