diff options
author | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-07-30 13:51:28 -0400 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@collabora.com> | 2014-07-31 09:05:18 -0400 |
commit | 27182231acd3a0c9898a8dba78b76dc8a827b4c0 (patch) | |
tree | 02b853f50d4201799a28f85a40de1d171db73333 | |
parent | 85f8f8f8589af3c404339c0f78021a7fe21cdfcd (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.hxx | 2 | ||||
-rw-r--r-- | sc/inc/refhint.hxx | 7 | ||||
-rw-r--r-- | sc/inc/tokenarray.hxx | 5 | ||||
-rw-r--r-- | sc/inc/types.hxx | 6 | ||||
-rw-r--r-- | sc/source/core/data/bcaslot.cxx | 17 | ||||
-rw-r--r-- | sc/source/core/data/documen7.cxx | 2 | ||||
-rw-r--r-- | sc/source/core/data/document10.cxx | 13 | ||||
-rw-r--r-- | sc/source/core/data/formulacell.cxx | 16 | ||||
-rw-r--r-- | sc/source/core/inc/bcaslot.hxx | 6 | ||||
-rw-r--r-- | sc/source/core/tool/refhint.cxx | 9 | ||||
-rw-r--r-- | sc/source/core/tool/token.cxx | 24 | ||||
-rw-r--r-- | sc/source/ui/docshell/docfunc.cxx | 6 | ||||
-rw-r--r-- | sc/source/ui/undo/undoblk.cxx | 71 |
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]; |