summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKohei Yoshida <kohei.yoshida@collabora.com>2014-07-30 13:51:28 -0400
committerKohei Yoshida <kohei.yoshida@collabora.com>2014-07-31 09:05:18 -0400
commit27182231acd3a0c9898a8dba78b76dc8a827b4c0 (patch)
tree02b853f50d4201799a28f85a40de1d171db73333
parent85f8f8f8589af3c404339c0f78021a7fe21cdfcd (diff)
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
-rw-r--r--sc/inc/document.hxx2
-rw-r--r--sc/inc/refhint.hxx7
-rw-r--r--sc/inc/tokenarray.hxx5
-rw-r--r--sc/inc/types.hxx6
-rw-r--r--sc/source/core/data/bcaslot.cxx17
-rw-r--r--sc/source/core/data/documen7.cxx2
-rw-r--r--sc/source/core/data/document10.cxx13
-rw-r--r--sc/source/core/data/formulacell.cxx16
-rw-r--r--sc/source/core/inc/bcaslot.hxx6
-rw-r--r--sc/source/core/tool/refhint.cxx9
-rw-r--r--sc/source/core/tool/token.cxx24
-rw-r--r--sc/source/ui/docshell/docfunc.cxx6
-rw-r--r--sc/source/ui/undo/undoblk.cxx71
13 files changed, 148 insertions, 36 deletions
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 0dc3239441b3..6bfb13887176 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1876,6 +1876,8 @@ public:
void EndListeningCell( sc::EndListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener );
void EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells );
+ void CollectAllAreaListeners(
+ std::vector<SvtListener*>& rListeners, const ScRange& rRange, sc::AreaOverlapType eType );
void PutInFormulaTree( ScFormulaCell* pCell );
void RemoveFromFormulaTree( ScFormulaCell* pCell );
diff --git a/sc/inc/refhint.hxx b/sc/inc/refhint.hxx
index 1eff90631de6..0f65aaeae06b 100644
--- a/sc/inc/refhint.hxx
+++ b/sc/inc/refhint.hxx
@@ -15,6 +15,8 @@
namespace sc {
+struct RefUpdateContext;
+
class RefHint : public SfxSimpleHint
{
public:
@@ -42,10 +44,11 @@ class RefMovedHint : public RefHint
{
ScRange maRange;
ScAddress maMoveDelta;
+ const sc::RefUpdateContext& mrCxt;
public:
- RefMovedHint( const ScRange& rRange, const ScAddress& rMove );
+ RefMovedHint( const ScRange& rRange, const ScAddress& rMove, const sc::RefUpdateContext& rCxt );
virtual ~RefMovedHint();
/**
@@ -57,6 +60,8 @@ public:
* Get the movement vector.
*/
const ScAddress& getDelta() const;
+
+ const sc::RefUpdateContext& getContext() const;
};
class RefColReorderHint : public RefHint
diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index 8ae61450c95a..97623647f9d2 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -152,11 +152,8 @@ public:
* Move reference positions that are within specified moved range.
*
* @param rPos position of this formula cell
- * @param rMovedRange range that has been moved.
- * @param rDelta movement vector.
*/
- void MoveReference(
- const ScAddress& rPos, const ScRange& rMovedRange, const ScAddress& rDelta );
+ sc::RefUpdateResult MoveReference( const ScAddress& rPos, const sc::RefUpdateContext& rCxt );
/**
* Move reference positions in response to column reordering. A range
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 37784dc2f6ca..8dc8f181f28d 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -100,6 +100,12 @@ struct RangeMatrix
typedef boost::unordered_map<SCCOLROW,SCCOLROW> ColRowReorderMapType;
+enum AreaOverlapType
+{
+ AreaInside,
+ AreaPartialOverlap
+};
+
}
#endif
diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx
index 701e53050700..ea70ffbd3615 100644
--- a/sc/source/core/data/bcaslot.cxx
+++ b/sc/source/core/data/bcaslot.cxx
@@ -432,7 +432,8 @@ void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
}
}
-void ScBroadcastAreaSlot::GetAllListeners( const ScRange& rRange, std::vector<sc::AreaListener>& rListeners )
+void ScBroadcastAreaSlot::GetAllListeners(
+ const ScRange& rRange, std::vector<sc::AreaListener>& rListeners, sc::AreaOverlapType eType )
{
for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
@@ -442,7 +443,14 @@ void ScBroadcastAreaSlot::GetAllListeners( const ScRange& rRange, std::vector<sc
ScBroadcastArea* pArea = (*aIter).mpArea;
const ScRange& rAreaRange = pArea->GetRange();
- if (!rRange.In(rAreaRange))
+
+ if (eType == sc::AreaInside && !rRange.In(rAreaRange))
+ // The range needs to be fully inside specified range.
+ continue;
+
+ if (eType == sc::AreaPartialOverlap &&
+ (!rRange.Intersects(rAreaRange) || rRange.In(rAreaRange)))
+ // The range needs to be only partially overlapping.
continue;
SvtBroadcaster::ListenersType& rLst = pArea->GetBroadcaster().GetAllListeners();
@@ -967,7 +975,8 @@ void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
maAreasToBeErased.swap( aCopy);
}
-std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners( const ScRange& rRange )
+std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
+ const ScRange& rRange, sc::AreaOverlapType eType )
{
std::vector<sc::AreaListener> aRet;
@@ -984,7 +993,7 @@ std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners( const
while ( nOff <= nEnd )
{
ScBroadcastAreaSlot* p = *pp;
- p->GetAllListeners(rRange, aRet);
+ p->GetAllListeners(rRange, aRet, eType);
ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak);
}
}
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
index 5a4df60f1abf..168233274d23 100644
--- a/sc/source/core/data/documen7.cxx
+++ b/sc/source/core/data/documen7.cxx
@@ -152,7 +152,7 @@ void ScDocument::BroadcastRefMoved( const sc::RefMovedHint& rHint )
const ScAddress& rDelta = rHint.getDelta();
// Get all area listeners that listens on the old range, and end their listening.
- std::vector<sc::AreaListener> aAreaListeners = pBASM->GetAllListeners(rSrcRange);
+ std::vector<sc::AreaListener> aAreaListeners = pBASM->GetAllListeners(rSrcRange, sc::AreaInside);
{
std::vector<sc::AreaListener>::iterator it = aAreaListeners.begin(), itEnd = aAreaListeners.end();
for (; it != itEnd; ++it)
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx
index eb9a74291c88..49db92e81787 100644
--- a/sc/source/core/data/document10.cxx
+++ b/sc/source/core/data/document10.cxx
@@ -17,6 +17,7 @@
#include <listenercontext.hxx>
#include <tokenstringcontext.hxx>
#include <poolhelp.hxx>
+#include <bcaslot.hxx>
// Add totally brand-new methods to this source file.
@@ -319,4 +320,16 @@ void ScDocument::RegroupFormulaCells( SCTAB nTab, SCCOL nCol )
pTab->RegroupFormulaCells(nCol);
}
+void ScDocument::CollectAllAreaListeners(
+ std::vector<SvtListener*>& rListener, const ScRange& rRange, sc::AreaOverlapType eType )
+{
+ if (!pBASM)
+ return;
+
+ std::vector<sc::AreaListener> aAL = pBASM->GetAllListeners(rRange, eType);
+ std::vector<sc::AreaListener>::iterator it = aAL.begin(), itEnd = aAL.end();
+ for (; it != itEnd; ++it)
+ rListener.push_back(it->mpListener);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 3c506aeb82df..d8465010b003 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1932,7 +1932,16 @@ void ScFormulaCell::Notify( const SfxHint& rHint )
const sc::RefMovedHint& rRefMoved = static_cast<const sc::RefMovedHint&>(rRefHint);
if (!IsShared() || IsSharedTop())
- pCode->MoveReference(aPos, rRefMoved.getRange(), rRefMoved.getDelta());
+ {
+ sc::RefUpdateResult aRes = pCode->MoveReference(aPos, rRefMoved.getContext());
+ if (aRes.mbNameModified)
+ {
+ // RPN token needs to be re-generated.
+ bCompile = true;
+ CompileTokenArray();
+ SetDirtyVar();
+ }
+ }
}
break;
case sc::RefHint::ColumnReordered:
@@ -2883,6 +2892,9 @@ bool ScFormulaCell::UpdateReferenceOnMove(
sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
bValChanged = aRes.mbValueChanged;
+ if (aRes.mbNameModified)
+ // Re-compile to get the RPN token regenerated to reflect updated names.
+ bCompile = true;
}
if (bValChanged || bRefModified)
@@ -2933,7 +2945,7 @@ bool ScFormulaCell::UpdateReferenceOnMove(
bValChanged = false;
- bCompile = (bCompile || bValChanged || bRefModified || bColRowNameCompile);
+ bCompile = (bCompile || bValChanged || bColRowNameCompile);
if ( bCompile )
{
CompileTokenArray( bNewListening ); // no Listening
diff --git a/sc/source/core/inc/bcaslot.hxx b/sc/source/core/inc/bcaslot.hxx
index 29808753bb59..59812af0d114 100644
--- a/sc/source/core/inc/bcaslot.hxx
+++ b/sc/source/core/inc/bcaslot.hxx
@@ -214,7 +214,8 @@ public:
*/
void EraseArea( ScBroadcastAreas::iterator& rIter );
- void GetAllListeners( const ScRange& rRange, std::vector<sc::AreaListener>& rListeners );
+ void GetAllListeners(
+ const ScRange& rRange, std::vector<sc::AreaListener>& rListeners, sc::AreaOverlapType eType );
};
/**
@@ -309,7 +310,8 @@ public:
// only for ScBroadcastAreaSlot
void FinallyEraseAreas( ScBroadcastAreaSlot* pSlot );
- std::vector<sc::AreaListener> GetAllListeners( const ScRange& rRange );
+ std::vector<sc::AreaListener> GetAllListeners(
+ const ScRange& rRange, sc::AreaOverlapType eType );
};
class ScBulkBroadcast
diff --git a/sc/source/core/tool/refhint.cxx b/sc/source/core/tool/refhint.cxx
index 533a41b54d8a..f76489c90e77 100644
--- a/sc/source/core/tool/refhint.cxx
+++ b/sc/source/core/tool/refhint.cxx
@@ -19,8 +19,8 @@ RefHint::Type RefHint::getType() const
return meType;
}
-RefMovedHint::RefMovedHint( const ScRange& rRange, const ScAddress& rMove ) :
- RefHint(Moved), maRange(rRange), maMoveDelta(rMove) {}
+RefMovedHint::RefMovedHint( const ScRange& rRange, const ScAddress& rMove, const sc::RefUpdateContext& rCxt ) :
+ RefHint(Moved), maRange(rRange), maMoveDelta(rMove), mrCxt(rCxt) {}
RefMovedHint::~RefMovedHint() {}
@@ -34,6 +34,11 @@ const ScAddress& RefMovedHint::getDelta() const
return maMoveDelta;
}
+const sc::RefUpdateContext& RefMovedHint::getContext() const
+{
+ return mrCxt;
+}
+
RefColReorderHint::RefColReorderHint( const sc::ColRowReorderMapType& rColMap, SCTAB nTab, SCROW nRow1, SCROW nRow2 ) :
RefHint(ColumnReordered), mrColMap(rColMap), mnTab(nTab), mnRow1(nRow1), mnRow2(nRow2) {}
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 569834aec9d5..080e9588b0c4 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -2865,9 +2865,13 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
return aRes;
}
-void ScTokenArray::MoveReference(
- const ScAddress& rPos, const ScRange& rMovedRange, const ScAddress& rDelta )
+sc::RefUpdateResult ScTokenArray::MoveReference( const ScAddress& rPos, const sc::RefUpdateContext& rCxt )
{
+ sc::RefUpdateResult aRes;
+
+ ScRange aOldRange = rCxt.maRange;
+ aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta);
+
FormulaToken** p = pCode;
FormulaToken** pEnd = p + static_cast<size_t>(nLen);
for (; p != pEnd; ++p)
@@ -2879,9 +2883,9 @@ void ScTokenArray::MoveReference(
ScToken* pToken = static_cast<ScToken*>(*p);
ScSingleRefData& rRef = pToken->GetSingleRef();
ScAddress aAbs = rRef.toAbs(rPos);
- if (rMovedRange.In(aAbs))
+ if (aOldRange.In(aAbs))
{
- aAbs.Move(rDelta.Col(), rDelta.Row(), rDelta.Tab());
+ aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
rRef.SetAddress(aAbs, rPos);
}
}
@@ -2891,17 +2895,25 @@ void ScTokenArray::MoveReference(
ScToken* pToken = static_cast<ScToken*>(*p);
ScComplexRefData& rRef = pToken->GetDoubleRef();
ScRange aAbs = rRef.toAbs(rPos);
- if (rMovedRange.In(aAbs))
+ if (aOldRange.In(aAbs))
{
- aAbs.Move(rDelta.Col(), rDelta.Row(), rDelta.Tab());
+ aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
rRef.SetRange(aAbs, rPos);
}
}
break;
+ case svIndex:
+ {
+ if (isNameModified(rCxt.maUpdatedNames, aOldRange.aStart.Tab(), **p))
+ aRes.mbNameModified = true;
+ }
+ break;
default:
;
}
}
+
+ return aRes;
}
void ScTokenArray::MoveReferenceColReorder(
diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
index d41caeeffeb9..ff276352f019 100644
--- a/sc/source/ui/docshell/docfunc.cxx
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -2679,8 +2679,8 @@ bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
{
rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
nUndoFlags, false, pUndoDoc );
- pRefUndoDoc = new ScDocument( SCDOCMODE_UNDO );
- pRefUndoDoc->InitUndo( &rDoc, 0, nTabCount-1, false, false );
+// pRefUndoDoc = new ScDocument( SCDOCMODE_UNDO );
+// pRefUndoDoc->InitUndo( &rDoc, 0, nTabCount-1, false, false );
}
if ( nDestTab != nStartTab )
@@ -2689,7 +2689,7 @@ bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
nDestEndCol, nDestEndRow, nDestEndTab,
nUndoFlags, false, pUndoDoc );
- pUndoData = new ScRefUndoData( &rDoc );
+// pUndoData = new ScRefUndoData( &rDoc );
rDoc.BeginDrawUndo();
}
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 <refupdatecontext.hxx>
#include <validat.hxx>
#include <gridwin.hxx>
+#include <svl/listener.hxx>
#include <set>
#include <boost/scoped_ptr.hpp>
@@ -1249,6 +1250,22 @@ void ScUndoDragDrop::DoUndo( ScRange aRange )
maPaintRanges.Join(aPaintRange);
}
+namespace {
+
+class DataChangeNotifier : std::unary_function<SvtListener*, void>
+{
+ 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<SvtListener*> 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];