sc/inc/formulacell.hxx | 22 ++++--- sc/qa/unit/ucalc.hxx | 2 sc/qa/unit/ucalc_sharedformula.cxx | 90 +++++++++++++++++++++++++++++ sc/source/core/data/column.cxx | 109 ++++++++++++++++++++++++++++++------ sc/source/core/data/formulacell.cxx | 6 + sc/source/core/tool/compiler.cxx | 4 - sc/source/core/tool/token.cxx | 31 ++++++++++ 7 files changed, 236 insertions(+), 28 deletions(-)
New commits: commit f32df2d590d0ee14f09664934457ba9e8de8cbe6 Author: Kohei Yoshida <kohei.yosh...@collabora.com> Date: Fri Feb 28 21:25:01 2014 -0500 fdo#75053: Adjust reference update on shift for formula groups. This is similar to my earlier fix for reference update on moving of cells. Change-Id: I592599507bfcab12f611eeae7b56c99da6c31919 diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index 42e00a9..f75895b 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -132,14 +132,6 @@ private: }; void InterpretTail( ScInterpretTailParameter ); - bool UpdatePosOnShift( const sc::RefUpdateContext& rCxt ); - - /** - * Update reference in response to cell insertion or deletion. - */ - bool UpdateReferenceOnShift( - const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ); - /** * Update reference in response to cell copy-n-paste. */ @@ -213,6 +205,7 @@ public: void ResetDirty(); bool NeedsListening() const; void SetNeedsListening( bool bVar ); + void SetNeedsDirty( bool bVar ); void SetNeedNumberFormat( bool bVal ); short GetFormatType() const; void Compile(const OUString& rFormula, @@ -246,6 +239,19 @@ public: const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc = NULL, const ScAddress* pUndoCellPos = NULL ); /** + * Shift the position of formula cell as part of reference update. + * + * @return true if the position has shifted, false otherwise. + */ + bool UpdatePosOnShift( const sc::RefUpdateContext& rCxt ); + + /** + * Update reference in response to cell insertion or deletion. + */ + bool UpdateReferenceOnShift( + const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ); + + /** * Update reference in response to cell move. */ bool UpdateReferenceOnMove( diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx index 4b8d38c..b7fd479 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -2428,6 +2428,68 @@ class UpdateRefOnNonCopy : std::unary_function<FormulaGroup, void> ScDocument* mpUndoDoc; bool mbUpdated; + void updateRefOnShift( FormulaGroup& rGroup ) + { + if (!rGroup.mbShared) + { + ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab); + mbUpdated |= rGroup.mpCell->UpdateReferenceOnShift(*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 aOldPos = pTop->aPos; + + // Run this before the position gets updated. + sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(*mpCxt, aOldPos); + + if (pTop->UpdatePosOnShift(*mpCxt)) + { + // Update the positions of all formula cells. + for (++pp; pp != ppEnd; ++pp) // skip the top cell. + { + ScFormulaCell* pFC = *pp; + pFC->aPos.Move(mpCxt->mnColDelta, mpCxt->mnRowDelta, mpCxt->mnTabDelta); + } + + if (pCode->IsRecalcModeOnRefMove()) + aRes.mbValueChanged = true; + } + + if (aRes.mbReferenceModified) + { + sc::StartListeningContext aStartCxt(mpCxt->mrDoc); + sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pOldCode.get()); + aEndCxt.setPositionDelta( + ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta)); + + for (pp = rGroup.mpCells; pp != ppEnd; ++pp) + { + ScFormulaCell* p = *pp; + p->EndListeningTo(aEndCxt); + p->SetNeedsListening(true); + } + + mbUpdated = true; + + fillUndoDoc(aOldPos, rGroup.mnLength, *pOldCode); + } + + if (aRes.mbValueChanged) + { + for (pp = rGroup.mpCells; pp != ppEnd; ++pp) + { + ScFormulaCell* p = *pp; + p->SetNeedsDirty(true); + } + } + } + void updateRefOnMove( FormulaGroup& rGroup ) { if (!rGroup.mbShared) @@ -2484,21 +2546,26 @@ class UpdateRefOnNonCopy : std::unary_function<FormulaGroup, void> 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); - } - } + fillUndoDoc(aOldPos, rGroup.mnLength, *pOldCode); + } + } + + void fillUndoDoc( const ScAddress& rOldPos, SCROW nLength, const ScTokenArray& rOldCode ) + { + if (!mpUndoDoc) + return; + + // Insert the old formula group into the undo document. + ScAddress aUndoPos = rOldPos; + ScFormulaCell* pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, rOldCode.Clone()); + ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(nLength, false); + + mpUndoDoc->SetFormulaCell(aUndoPos, pFC); + aUndoPos.IncRow(); + for (SCROW i = 1; i < nLength; ++i, aUndoPos.IncRow()) + { + pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, xGroup); + mpUndoDoc->SetFormulaCell(aUndoPos, pFC); } } @@ -2511,10 +2578,16 @@ public: void operator() ( FormulaGroup& rGroup ) { - if (mpCxt->meMode == URM_MOVE) + switch (mpCxt->meMode) { - updateRefOnMove(rGroup); - return; + case URM_INSDEL: + updateRefOnShift(rGroup); + return; + case URM_MOVE: + updateRefOnMove(rGroup); + return; + default: + ; } if (rGroup.mbShared) diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index a872e91..d0ebd2f 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -957,6 +957,12 @@ bool ScFormulaCell::GetDirty() const { return bDirty; } void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; } bool ScFormulaCell::NeedsListening() const { return bNeedListening; } void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; } + +void ScFormulaCell::SetNeedsDirty( bool bVar ) +{ + mbPostponedDirty = bVar; +} + void ScFormulaCell::SetNeedNumberFormat( bool bVal ) { mbNeedsNumberFormat = bVal; } short ScFormulaCell::GetFormatType() const { return nFormatType; } diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx index afb8f5f..68f2ba5 100644 --- a/sc/source/core/tool/compiler.cxx +++ b/sc/source/core/tool/compiler.cxx @@ -769,13 +769,13 @@ struct ConventionOOO_A1 : public Convention_A1 rBuffer.append('.'); if (!rRef.IsColRel()) rBuffer.append('$'); - if (!ValidCol(rAbsRef.Col())) + if (!ValidCol(rAbsRef.Col()) || rRef.IsColDeleted()) rBuffer.append(rErrRef); else MakeColStr(rBuffer, rAbsRef.Col()); if (!rRef.IsRowRel()) rBuffer.append('$'); - if (!ValidRow(rAbsRef.Row())) + if (!ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted()) rBuffer.append(rErrRef); else MakeRowStr(rBuffer, rAbsRef.Row()); diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index 7052049..c7c8e42 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -2480,6 +2480,25 @@ void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt ) rRef.SetTabDeleted(true); } +void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt ) +{ + if (rCxt.mnColDelta) + { + if (rRef.IsColDeleted()) + rRef.SetColDeleted(false); + } + else if (rCxt.mnRowDelta) + { + if (rRef.IsRowDeleted()) + rRef.SetRowDeleted(false); + } + else if (rCxt.mnTabDelta) + { + if (rRef.IsTabDeleted()) + rRef.SetTabDeleted(false); + } +} + void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt ) { if (rCxt.mnColDelta < 0) @@ -2647,6 +2666,18 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateCon break; } + if (!rCxt.isDeleted() && rRef.IsDeleted()) + { + // Check if the token has reference to previously deleted region. + ScAddress aCheckPos = rRef.toAbs(aNewPos); + if (rCxt.maRange.In(aCheckPos)) + { + restoreDeletedRef(rRef, rCxt); + aRes.mbValueChanged = true; + break; + } + } + if (rCxt.maRange.In(aAbs)) { aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta); commit aa6c5b7faecdb57cbdeac051e304531c1a1cf63b Author: Kohei Yoshida <kohei.yosh...@collabora.com> Date: Fri Feb 28 09:58:05 2014 -0500 fdo#75053: Write test for this. Change-Id: I51c552c23af5d1669e35817e4f54f75be6b54180 diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx index 4f8810a..f51556f 100644 --- a/sc/qa/unit/ucalc.hxx +++ b/sc/qa/unit/ucalc.hxx @@ -267,6 +267,7 @@ public: void testSharedFormulasRefUpdate(); void testSharedFormulasRefUpdateRange(); void testSharedFormulasDeleteRows(); + void testSharedFormulasDeleteColumns(); void testSharedFormulasRefUpdateMoveSheets(); void testSharedFormulasRefUpdateCopySheets(); void testSharedFormulasCopyPaste(); @@ -439,6 +440,7 @@ public: CPPUNIT_TEST(testSharedFormulasRefUpdate); CPPUNIT_TEST(testSharedFormulasRefUpdateRange); CPPUNIT_TEST(testSharedFormulasDeleteRows); + CPPUNIT_TEST(testSharedFormulasDeleteColumns); CPPUNIT_TEST(testSharedFormulasRefUpdateMoveSheets); CPPUNIT_TEST(testSharedFormulasRefUpdateCopySheets); CPPUNIT_TEST(testSharedFormulasCopyPaste); diff --git a/sc/qa/unit/ucalc_sharedformula.cxx b/sc/qa/unit/ucalc_sharedformula.cxx index d8974ae..677b0e2 100644 --- a/sc/qa/unit/ucalc_sharedformula.cxx +++ b/sc/qa/unit/ucalc_sharedformula.cxx @@ -16,6 +16,8 @@ #include "undoblk.hxx" #include "scopetools.hxx" #include <docfunc.hxx> +#include <tokenarray.hxx> +#include <tokenstringcontext.hxx> #include "svl/sharedstring.hxx" #include "formula/grammar.hxx" @@ -519,6 +521,94 @@ void Test::testSharedFormulasDeleteRows() CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength()); } +void Test::testSharedFormulasDeleteColumns() +{ + using namespace formula; + + m_pDoc->InsertTab(0, "Test"); + + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. + FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); + + ScDocFunc& rFunc = getDocShell().GetDocFunc(); + ScMarkData aMark; + aMark.SelectOneTable(0); + + // First, test a single cell case. A value in B1 and formula in C1. + m_pDoc->SetValue(ScAddress(1,0,0), 11.0); + m_pDoc->SetString(ScAddress(2,0,0), "=RC[-1]"); + CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,0,0))); + + // Delete column B. + rFunc.DeleteCells(ScRange(1,0,0,1,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true); + CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(ScAddress(1,0,0))); + + // The reference should still point to row 1 but the column status should be set to 'deleted'. + const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0)); + CPPUNIT_ASSERT(pFC); + const ScTokenArray* pCode = pFC->GetCode(); + CPPUNIT_ASSERT(pCode && pCode->GetLen() == 1); + const FormulaToken* pToken = pCode->GetArray()[0]; + CPPUNIT_ASSERT(pToken->GetType() == svSingleRef); + const ScSingleRefData* pSRef = &static_cast<const ScToken*>(pToken)->GetSingleRef(); + CPPUNIT_ASSERT(pSRef->IsColDeleted()); + CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pSRef->toAbs(ScAddress(1,0,0)).Row()); + + // The formula string should show #REF! in lieu of the column position (only for Calc A1 syntax). + sc::CompileFormulaContext aCFCxt(m_pDoc, FormulaGrammar::GRAM_ENGLISH); + CPPUNIT_ASSERT_EQUAL(OUString("=#REF!1"), pFC->GetFormula(aCFCxt)); + + SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); + CPPUNIT_ASSERT(pUndoMgr); + + // Undo and make sure the deleted flag is gone. + pUndoMgr->Undo(); + CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,0,0))); + pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0)); + CPPUNIT_ASSERT_EQUAL(OUString("=B1"), pFC->GetFormula(aCFCxt)); + + // Clear row 1 and move over to a formula group case. + clearRange(m_pDoc, ScRange(0,0,0,MAXCOL,0,0)); + + // Fill A1:B2 with numbers, and C1:C2 with formula that reference those numbers. + for (SCROW i = 0; i <= 1; ++i) + { + m_pDoc->SetValue(ScAddress(0,i,0), (i+1)); + m_pDoc->SetValue(ScAddress(1,i,0), (i+11)); + m_pDoc->SetString(ScAddress(2,i,0), "=RC[-2]+RC[-1]"); + double fCheck = m_pDoc->GetValue(ScAddress(0,i,0)); + fCheck += m_pDoc->GetValue(ScAddress(1,i,0)); + CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i,0))); + } + + // Delete column B. + rFunc.DeleteCells(ScRange(1,0,0,1,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true); + + for (SCROW i = 0; i <= 1; ++i) + { + ScAddress aPos(1,i,0); + CPPUNIT_ASSERT_EQUAL(OUString("#REF!"), m_pDoc->GetString(aPos)); + } + + pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0)); // B1 + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_EQUAL(OUString("=A1+#REF!1"), pFC->GetFormula(aCFCxt)); + pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0)); // B2 + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_EQUAL(OUString("=A2+#REF!2"), pFC->GetFormula(aCFCxt)); + + // Undo deletion of column B and check the results of C1:C2. + pUndoMgr->Undo(); + for (SCROW i = 0; i <= 1; ++i) + { + double fCheck = m_pDoc->GetValue(ScAddress(0,i,0)); + fCheck += m_pDoc->GetValue(ScAddress(1,i,0)); + CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i,0))); + } + + m_pDoc->DeleteTab(0); +} + void Test::testSharedFormulasRefUpdateMoveSheets() { m_pDoc->InsertTab(0, "Sheet1"); _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits