From 27182231acd3a0c9898a8dba78b76dc8a827b4c0 Mon Sep 17 00:00:00 2001 From: Kohei Yoshida Date: Wed, 30 Jul 2014 13:51:28 -0400 Subject: fdo#78555: Retain formula results when moving a range of cells. * No need to re-compile RPN token array on reference change alone. We do that when the formula contains one or more names that have been updated. * Adjust undo code to get it to work without relying on ref undo document, which would cause the token arrays to be unnecessarily recompiled. * Whatever else need to be changed in order to pass all unit tests. Change-Id: I99e86d23320aca8900fef011da23a9d34e42751e --- sc/source/ui/undo/undoblk.cxx | 71 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 11 deletions(-) (limited to 'sc/source/ui/undo') diff --git a/sc/source/ui/undo/undoblk.cxx b/sc/source/ui/undo/undoblk.cxx index 982925d8c3ab..be6895fc3e6c 100644 --- a/sc/source/ui/undo/undoblk.cxx +++ b/sc/source/ui/undo/undoblk.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -1249,6 +1250,22 @@ void ScUndoDragDrop::DoUndo( ScRange aRange ) maPaintRanges.Join(aPaintRange); } +namespace { + +class DataChangeNotifier : std::unary_function +{ + ScHint maHint; +public: + DataChangeNotifier() : maHint(SC_HINT_DATACHANGED, ScAddress()) {} + + void operator() ( SvtListener* p ) + { + p->Notify(maHint); + } +}; + +} + void ScUndoDragDrop::Undo() { mnPaintExtFlags = 0; @@ -1258,32 +1275,64 @@ void ScUndoDragDrop::Undo() if (bCut) { - // Notify all listeners of the destination range, and have them update their references. + // During undo, we move cells from aDestRange to aSrcRange. + ScDocument& rDoc = pDocShell->GetDocument(); + SCCOL nColDelta = aSrcRange.aStart.Col() - aDestRange.aStart.Col(); SCROW nRowDelta = aSrcRange.aStart.Row() - aDestRange.aStart.Row(); SCTAB nTabDelta = aSrcRange.aStart.Tab() - aDestRange.aStart.Tab(); - sc::RefMovedHint aHint(aDestRange, ScAddress(nColDelta, nRowDelta, nTabDelta)); + + sc::RefUpdateContext aCxt(rDoc); + aCxt.meMode = URM_MOVE; + aCxt.maRange = aSrcRange; + aCxt.mnColDelta = nColDelta; + aCxt.mnRowDelta = nRowDelta; + aCxt.mnTabDelta = nTabDelta; + + // Global range names. + ScRangeName* pName = rDoc.GetRangeName(); + if (pName) + pName->UpdateReference(aCxt); + + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB nTab = 0; nTab < nTabCount; ++nTab) + { + // Sheet-local range names. + pName = rDoc.GetRangeName(nTab); + if (pName) + pName->UpdateReference(aCxt, nTab); + } + + // Notify all listeners of the destination range, and have them update their references. + sc::RefMovedHint aHint(aDestRange, ScAddress(nColDelta, nRowDelta, nTabDelta), aCxt); rDoc.BroadcastRefMoved(aHint); ScValidationDataList* pValidList = rDoc.GetValidationList(); if (pValidList) { // Update the references of validation entries. - sc::RefUpdateContext aCxt(rDoc); - aCxt.meMode = URM_MOVE; - aCxt.maRange = aSrcRange; - aCxt.mnColDelta = nColDelta; - aCxt.mnRowDelta = nRowDelta; - aCxt.mnTabDelta = nTabDelta; pValidList->UpdateReference(aCxt); } - } - DoUndo(aDestRange); - if (bCut) + DoUndo(aDestRange); DoUndo(aSrcRange); + // Notify all area listeners whose listened areas are partially moved, to + // recalculate. + std::vector aListeners; + rDoc.CollectAllAreaListeners(aListeners, aSrcRange, sc::AreaPartialOverlap); + + // Remove any duplicate listener entries. We must ensure that we notify + // each unique listener only once. + std::sort(aListeners.begin(), aListeners.end()); + aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end()); + + std::for_each(aListeners.begin(), aListeners.end(), DataChangeNotifier()); + } + else + DoUndo(aDestRange); + for (size_t i = 0; i < maPaintRanges.size(); ++i) { const ScRange* p = maPaintRanges[i]; -- cgit