From 5cefed66bccab3757793686e9402b2faf37fcb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Vajngerl?= Date: Sat, 28 Feb 2026 00:22:24 +0900 Subject: [PATCH] sc: Fix backward reference when copying a sheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When extending the test for Overwrite content I also added a case to check a backwards tab reference (referencing a tab that precedes the current one). This extends the test for CopyTab, but surprisingly it failed. In a copied tab, it is needed to adjust the relative tab references, as they still need to point to the same tab, but because the reference is relative, the actual value needs to change (for example from index -1 to -2). This mechanism was missing. This combines the work for OverwriteContent and extends the method AdjustRelativeTabRefs so it can be used for both. This also adds a new sc::CopyTabMode enum to denote if we are adjusting a tab that is inserted or an existing one. Tests were adjusted with the relevant use cases. Change-Id: Ie32d0ceac34525ccaf13aed8fb9630ae42d4225e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200674 Tested-by: Jenkins CollaboraOffice Reviewed-by: Miklos Vajna Reviewed-on: https://gerrit.libreoffice.org/c/core/+/202020 Reviewed-by: Tomaž Vajngerl Tested-by: Tomaž Vajngerl (cherry picked from commit ad7c05c27c177a105418337771ed182dd11f23ba) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205410 Reviewed-by: Xisco Fauli Tested-by: Xisco Fauli --- sc/inc/column.hxx | 1 + sc/inc/formulacell.hxx | 1 + sc/inc/table.hxx | 1 + sc/inc/tokenarray.hxx | 22 +++- sc/qa/unit/ucalc.cxx | 184 ++++++++++++++++------------ sc/source/core/data/column.cxx | 22 ++++ sc/source/core/data/documen2.cxx | 1 + sc/source/core/data/formulacell.cxx | 15 ++- sc/source/core/data/table4.cxx | 6 + sc/source/core/tool/token.cxx | 28 +++-- 10 files changed, 185 insertions(+), 96 deletions(-) diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index afe855c23c6e..5697b7150625 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -597,6 +597,7 @@ public: bool GetDataEntries( SCROW nRow, std::set& rStrings) const; void UpdateInsertTabAbs(SCTAB nNewPos); + void AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode); bool TestTabRefAbs(SCTAB nTable) const; bool GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const; diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx index 76c4541c9d3f..04e40f362ffa 100644 --- a/sc/inc/formulacell.hxx +++ b/sc/inc/formulacell.hxx @@ -318,6 +318,7 @@ public: void UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt ); void UpdateInsertTabAbs(SCTAB nTable); + void AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode); void UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt ); void UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo ); bool TestTabRefAbs(SCTAB nTable); diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index b40c374582b3..cc3f793d96d9 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -1329,6 +1329,7 @@ private: void FillFormula( const ScFormulaCell* pSrcCell, SCCOL nDestCol, SCROW nDestRow, bool bLast ); void UpdateInsertTabAbs(SCTAB nNewPos); + void AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode); bool GetNextSpellingCell(SCCOL& rCol, SCROW& rRow, bool bInSel, const ScMarkData& rMark) const; bool GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark ) const; diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx index 13d3230a9700..a43a577ac64e 100644 --- a/sc/inc/tokenarray.hxx +++ b/sc/inc/tokenarray.hxx @@ -39,6 +39,13 @@ struct RefUpdateResult; struct TokenStringContext; class ColRowReorderMapType; +/** If the target tab was inserted, or an existing tab was used. */ +enum class TargetTabState +{ + Existing, + Inserted, +}; + } struct ScRawToken; @@ -151,12 +158,15 @@ public: */ void AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddress& rOldPos, const ScAddress& rNewPos, bool bCheckCopyArea ); - /** Adjust relative tab references when copying a formula between tabs when - overwriting a tab. Non-zero relative tab offsets are decremented by - input delta value so they still resolve to the original target sheet - from the new position. Same-sheet relative refs with offset 0 are left - unchanged. */ - void AdjustRelativeTabRefs(SCTAB nDelta); + /** Adjust relative tab references when copying a formula to a new table. + Non-zero relative tab offsets are recalculated so they still resolve + to the original target sheet from the new position. Same-sheet + relative refs (offset 0) are not modified. + If the target tab was newly inserted, the references pointing to + tabs at or after the insertion point are additionally shifted by + one. */ + void AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, + sc::TargetTabState eMode = sc::TargetTabState::Existing); /** When copying a sheet-local named expression, move sheet references that point to the originating sheet to point to the new sheet instead. diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx index 795c37e5c67e..b18f1c1152f9 100644 --- a/sc/qa/unit/ucalc.cxx +++ b/sc/qa/unit/ucalc.cxx @@ -7579,19 +7579,22 @@ CPPUNIT_TEST_FIXTURE(Test, testOverwriteContent) CPPUNIT_TEST_FIXTURE(Test, testCopyTabContent) { - m_pDoc->InsertTab(0, u"Tab1"_ustr); - m_pDoc->InsertTab(1, u"Tab2"_ustr); - m_pDoc->InsertTab(2, u"Tab3"_ustr); + m_pDoc->InsertTab(0, u"Tab0"_ustr); + m_pDoc->InsertTab(1, u"Tab1"_ustr); + m_pDoc->InsertTab(2, u"Tab2"_ustr); + m_pDoc->InsertTab(3, u"Tab3"_ustr); - // For cross-sheet formula checking - m_pDoc->SetValue(ScAddress(0, 0, 2), 66.0); + // For cross-sheet formula checking (forward and backward) + m_pDoc->SetValue(ScAddress(0, 0, 0), 99.0); // Tab0.A1 + m_pDoc->SetValue(ScAddress(0, 0, 3), 66.0); // Tab3.A1 std::vector> aData = { { u"Column 1"_ustr, u"Column 2"_ustr, u"Column 3"_ustr }, { u"7"_ustr, u"A"_ustr, u"=A2+5"_ustr }, - { u"=2+2"_ustr, u"C"_ustr, u"=Tab3.A1"_ustr }, - { u"=A3-2"_ustr, u"D"_ustr, u"=SUM(A2:A5)"_ustr }, - { u"3"_ustr, u"B"_ustr, u"=C3-60"_ustr } + { u"=2+2"_ustr, u"C"_ustr, u"=Tab3.A1"_ustr }, // forward cross-tab reference + { u"=A3-2"_ustr, u"D"_ustr, u"=SUM(A2:A6)"_ustr }, + { u"3"_ustr, u"B"_ustr, u"=C3-60"_ustr }, + { u"5"_ustr, u"E"_ustr, u"=Tab0.A1"_ustr }, // backward cross-tab reference }; for (size_t i = 0; i < aData.size(); ++i) @@ -7600,123 +7603,142 @@ CPPUNIT_TEST_FIXTURE(Test, testCopyTabContent) { if (!aData[i][j].isEmpty()) { - m_pDoc->SetString(j, i, 0, aData[i][j]); + m_pDoc->SetString(j, i, 1, aData[i][j]); } } } m_pDoc->CalcAll(); // Verify Tab1 values and formulas before copy - { - CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 0))); - CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 0))); - CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 0))); - CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0))); - CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 0))); - CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 0))); - CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 0))); - CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 0))); - CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(2, 3, 0))); - CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 0))); - } - - // Copy Tab1 to index 1 - Tab1_2 - m_pDoc->CopyTab(0, 1); - CPPUNIT_ASSERT_EQUAL(static_cast(4), m_pDoc->GetTableCount()); - - // Tab3 value shifted from index 2 to index 3 - CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(0, 0, 3))); - - // Tab1 (tab index 0) should be unchanged - { - CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 0))); - CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 0))); - CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 0))); - CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0))); - CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 0))); - CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 0))); - CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 0))); - CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 0))); - CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(2, 3, 0))); - CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 0))); - } - - // The copy Tab1_2 (tab index 1) should have the same data as Tab1 { CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 1))); CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 1))); CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 1))); CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 1))); + CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0, 5, 1))); CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 1))); CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 1))); CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 1))); CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 1))); + CPPUNIT_ASSERT_EQUAL(u"E"_ustr, m_pDoc->GetString(ScAddress(1, 5, 1))); + + CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 1))); // =A2+5=12 + CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 1))); // =Tab3.A1 + CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(2, 3, 1))); // =SUM(A2:A6) + CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 1))); // =C3-60 + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(2, 5, 1))); // =Tab0.A1 + } + + // Copy Tab1 to index 2 - Tab1_2 + m_pDoc->CopyTab(1, 2); + CPPUNIT_ASSERT_EQUAL(static_cast(5), m_pDoc->GetTableCount()); + + // Tab3 value shifted from index 3 to index 4 + CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(0, 0, 4))); + // Tab0 value stays at index 0 + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(0, 0, 0))); + + // Tab1 (tab index 1) should be unchanged + { + CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 1))); + CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 1))); + CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 1))); + CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 1))); + CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0, 5, 1))); + + CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 1))); + CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 1))); + CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 1))); + CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 1))); + CPPUNIT_ASSERT_EQUAL(u"E"_ustr, m_pDoc->GetString(ScAddress(1, 5, 1))); CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 1))); CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 1))); - CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(2, 3, 1))); + CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(2, 3, 1))); CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 1))); + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(2, 5, 1))); + } + + // The copy Tab1_2 (tab index 2) should have the same data as Tab1 + { + CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 2))); + CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 2))); + CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 2))); + CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 2))); + CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0, 5, 2))); + + CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 2))); + CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 2))); + CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 2))); + CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 2))); + CPPUNIT_ASSERT_EQUAL(u"E"_ustr, m_pDoc->GetString(ScAddress(1, 5, 2))); + + CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 2))); + CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 2))); + CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(2, 3, 2))); + CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 2))); + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(2, 5, 2))); } // Check the formulas in Tab1_2 { - CPPUNIT_ASSERT_EQUAL(u"=2+2"_ustr, m_pDoc->GetFormula(0, 2, 1)); - CPPUNIT_ASSERT_EQUAL(u"=A3-2"_ustr, m_pDoc->GetFormula(0, 3, 1)); - CPPUNIT_ASSERT_EQUAL(u"=A2+5"_ustr, m_pDoc->GetFormula(2, 1, 1)); - CPPUNIT_ASSERT_EQUAL(u"='Tab3'.A1"_ustr, m_pDoc->GetFormula(2, 2, 1)); - CPPUNIT_ASSERT_EQUAL(u"=SUM(A2:A5)"_ustr, m_pDoc->GetFormula(2, 3, 1)); - CPPUNIT_ASSERT_EQUAL(u"=C3-60"_ustr, m_pDoc->GetFormula(2, 4, 1)); + CPPUNIT_ASSERT_EQUAL(u"=2+2"_ustr, m_pDoc->GetFormula(0, 2, 2)); + CPPUNIT_ASSERT_EQUAL(u"=A3-2"_ustr, m_pDoc->GetFormula(0, 3, 2)); + CPPUNIT_ASSERT_EQUAL(u"=A2+5"_ustr, m_pDoc->GetFormula(2, 1, 2)); + CPPUNIT_ASSERT_EQUAL(u"='Tab3'.A1"_ustr, m_pDoc->GetFormula(2, 2, 2)); + CPPUNIT_ASSERT_EQUAL(u"=SUM(A2:A6)"_ustr, m_pDoc->GetFormula(2, 3, 2)); + CPPUNIT_ASSERT_EQUAL(u"=C3-60"_ustr, m_pDoc->GetFormula(2, 4, 2)); + CPPUNIT_ASSERT_EQUAL(u"=Tab0.A1"_ustr, m_pDoc->GetFormula(2, 5, 2)); } // Change Tab1 and verify the copy is independent - m_pDoc->SetValue(ScAddress(0, 1, 0), 10.0); // 7 -> 10 + m_pDoc->SetValue(ScAddress(0, 1, 1), 10.0); // 7 -> 10 m_pDoc->CalcAll(); // Tab1 should be changed { - CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0, 1, 0))); - CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 0))); - CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 0))); - CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 0))); - CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 0))); - CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 0))); - CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 0))); - - CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(ScAddress(2, 1, 0))); - CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 0))); - CPPUNIT_ASSERT_EQUAL(19.0, m_pDoc->GetValue(ScAddress(2, 3, 0))); - CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 0))); - } - - // No change in Tab1_2 - { - CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 1))); + CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0, 1, 1))); CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 1))); CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 1))); CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 1))); + CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0, 5, 1))); CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 1))); CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 1))); CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 1))); CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 1))); + CPPUNIT_ASSERT_EQUAL(u"E"_ustr, m_pDoc->GetString(ScAddress(1, 5, 1))); - CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 1))); + CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(ScAddress(2, 1, 1))); CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 1))); - CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(2, 3, 1))); + CPPUNIT_ASSERT_EQUAL(24.0, m_pDoc->GetValue(ScAddress(2, 3, 1))); // =SUM(A2:A6)=10+4+2+3+5 CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 1))); + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(2, 5, 1))); } + // No change in Tab1_2 + { + CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 1, 2))); + CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0, 2, 2))); + CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0, 3, 2))); + CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0, 4, 2))); + CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0, 5, 2))); + + CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(1, 1, 2))); + CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(1, 2, 2))); + CPPUNIT_ASSERT_EQUAL(u"D"_ustr, m_pDoc->GetString(ScAddress(1, 3, 2))); + CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(1, 4, 2))); + CPPUNIT_ASSERT_EQUAL(u"E"_ustr, m_pDoc->GetString(ScAddress(1, 5, 2))); + + CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(2, 1, 2))); + CPPUNIT_ASSERT_EQUAL(66.0, m_pDoc->GetValue(ScAddress(2, 2, 2))); + CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(2, 3, 2))); + CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2, 4, 2))); + CPPUNIT_ASSERT_EQUAL(99.0, m_pDoc->GetValue(ScAddress(2, 5, 2))); + } + + m_pDoc->DeleteTab(4); m_pDoc->DeleteTab(3); m_pDoc->DeleteTab(2); m_pDoc->DeleteTab(1); diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx index bf4d246703ed..d3481adf578a 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -2627,6 +2627,22 @@ public: } }; +/** For adjusting relative tab references. */ +class RelativeTabRefUpdater +{ + SCTAB mnOldTab; + SCTAB mnNewTab; + sc::TargetTabState meMode; +public: + RelativeTabRefUpdater(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode) : + mnOldTab(nOldTab), mnNewTab(nNewTab), meMode(eMode) {} + + void operator() (size_t /*nRow*/, ScFormulaCell* pCell) + { + pCell->AdjustRelativeTabRefs(mnOldTab, mnNewTab, meMode); + } +}; + class UsedRangeNameFinder { sc::UpdatedRangeNames& mrIndexes; @@ -3057,6 +3073,12 @@ void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos) CellStorageModified(); } +void ScColumn::AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode) +{ + RelativeTabRefUpdater aFunc(nOldTab, nNewTab, eMode); + sc::ProcessFormula(maCells, aFunc); +} + void ScColumn::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo ) { nTab = nTabNo; diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx index a3f8f5d33798..6db67df358de 100644 --- a/sc/source/core/data/documen2.cxx +++ b/sc/source/core/data/documen2.cxx @@ -925,6 +925,7 @@ void sc::TableContentCopier::performCopy(const ScMarkData* pOnlyMarked, void sc::TableContentCopier::updateReferencesAfterTabInsertion(sc::RefUpdateInsertTabContext& rContext) { mpTargetTab->UpdateInsertTabAbs(mnTargetTabNo); + mpTargetTab->AdjustRelativeTabRefs(mnSourceTabNo, mnTargetTabNo, sc::TargetTabState::Inserted); mpSourceTab->UpdateInsertTab(rContext); mpSourceTab->UpdateCompile(); } diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index 8534b8790761..d8c4fa34ceca 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -851,8 +851,7 @@ ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const if ((nCloneFlags & ScCloneFlags::AdjustCrossSheetRefs) != ScCloneFlags::Default) { - SCTAB nDelta = aPos.Tab() - rCell.aPos.Tab(); - pCode->AdjustRelativeTabRefs(nDelta); + pCode->AdjustRelativeTabRefs(rCell.aPos.Tab(), aPos.Tab()); } } @@ -3727,6 +3726,18 @@ void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) } } +void ScFormulaCell::AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode) +{ + if (rDocument.IsClipOrUndo()) + return; + + bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this; + if (!bAdjustCode) + return; + + pCode->AdjustRelativeTabRefs(nOldTab, nNewTab, eMode); +} + bool ScFormulaCell::TestTabRefAbs(SCTAB nTable) { if (rDocument.IsClipOrUndo()) diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx index b8c275597be0..200014cf65c7 100644 --- a/sc/source/core/data/table4.cxx +++ b/sc/source/core/data/table4.cxx @@ -2789,6 +2789,12 @@ void ScTable::UpdateInsertTabAbs(SCTAB nTable) aCol[i].UpdateInsertTabAbs(nTable); } +void ScTable::AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode) +{ + for (SCCOL i=0; i < aCol.size(); i++) + aCol[i].AdjustRelativeTabRefs(nOldTab, nNewTab, eMode); +} + bool ScTable::GetNextSpellingCell(SCCOL& rCol, SCROW& rRow, bool bInSel, const ScMarkData& rMark) const { diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx index a31a4101296a..600446bc8945 100644 --- a/sc/source/core/tool/token.cxx +++ b/sc/source/core/tool/token.cxx @@ -2682,8 +2682,25 @@ void ScTokenArray::AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddres } } -void ScTokenArray::AdjustRelativeTabRefs( SCTAB nDelta ) +namespace { + +void adjustRelativeTabRef(ScSingleRefData& rRef, SCTAB nOldTab, SCTAB nNewTab, bool bInsertedTab) +{ + if (!rRef.IsTabRel() || rRef.Tab() == 0) + return; + SCTAB nAbsTarget = nOldTab + rRef.Tab(); + if (bInsertedTab && nAbsTarget >= nNewTab) + nAbsTarget++; + SCTAB nNewOffset = nAbsTarget - nNewTab; + rRef.IncTab(nNewOffset - rRef.Tab()); +} + +} // anonymous namespace + +void ScTokenArray::AdjustRelativeTabRefs(SCTAB nOldTab, SCTAB nNewTab, sc::TargetTabState eMode) +{ + const bool bInsertedTab = (eMode == sc::TargetTabState::Inserted); TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, true); for (size_t j=0; j<2; ++j) { @@ -2700,17 +2717,14 @@ void ScTokenArray::AdjustRelativeTabRefs( SCTAB nDelta ) case svDoubleRef: { ScComplexRefData& rRef = *p->GetDoubleRef(); - if (rRef.Ref1.IsTabRel() && rRef.Ref1.Tab() != 0) - rRef.Ref1.IncTab(-nDelta); - if (rRef.Ref2.IsTabRel() && rRef.Ref2.Tab() != 0) - rRef.Ref2.IncTab(-nDelta); + adjustRelativeTabRef(rRef.Ref1, nOldTab, nNewTab, bInsertedTab); + adjustRelativeTabRef(rRef.Ref2, nOldTab, nNewTab, bInsertedTab); break; } case svSingleRef: { ScSingleRefData& rRef = *p->GetSingleRef(); - if (rRef.IsTabRel() && rRef.Tab() != 0) - rRef.IncTab(-nDelta); + adjustRelativeTabRef(rRef, nOldTab, nNewTab, bInsertedTab); break; } default: