summaryrefslogtreecommitdiffstats
path: root/sc/qa/unit/ucalc_sharedformula.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sc/qa/unit/ucalc_sharedformula.cxx2757
1 files changed, 2757 insertions, 0 deletions
diff --git a/sc/qa/unit/ucalc_sharedformula.cxx b/sc/qa/unit/ucalc_sharedformula.cxx
new file mode 100644
index 000000000..f076b57f8
--- /dev/null
+++ b/sc/qa/unit/ucalc_sharedformula.cxx
@@ -0,0 +1,2757 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include "ucalc.hxx"
+#include "helper/debughelper.hxx"
+#include "helper/qahelper.hxx"
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <cellvalue.hxx>
+#include <docsh.hxx>
+#include <undoblk.hxx>
+#include <scopetools.hxx>
+#include <docfunc.hxx>
+#include <dbdocfun.hxx>
+#include <tokenarray.hxx>
+#include <tokenstringcontext.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
+#include <bcaslot.hxx>
+#include <sharedformula.hxx>
+
+#include <svl/sharedstring.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <formula/grammar.hxx>
+
+void Test::testSharedFormulas()
+{
+ m_pDoc->InsertTab(0, "Test");
+
+ ScAddress aPos(1, 9, 0); // B10
+ m_pDoc->SetString(aPos, "=A10*2"); // Insert into B10.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("Expected to be a non-shared cell.", pFC && !pFC->IsShared());
+
+ aPos.SetRow(10); // B11
+ m_pDoc->SetString(aPos, "=A11*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(8); // B9
+ m_pDoc->SetString(aPos, "=A9*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(12); // B13
+ m_pDoc->SetString(aPos, "=A13*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This formula cell shouldn't be shared yet.", pFC && !pFC->IsShared());
+
+ // Insert a formula to B12, and B9:B13 should be shared.
+ aPos.SetRow(11); // B12
+ m_pDoc->SetString(aPos, "=A12*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Insert formulas to B15:B16.
+ aPos.SetRow(14); // B15
+ m_pDoc->SetString(aPos, "=A15*2");
+ aPos.SetRow(15); // B16
+ m_pDoc->SetString(aPos, "=A16*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(14), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Insert a formula to B14, and B9:B16 should be shared.
+ aPos.SetRow(13); // B14
+ m_pDoc->SetString(aPos, "=A14*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Insert an incompatible formula to B12, to split the shared range to B9:B11 and B13:B16.
+ aPos.SetRow(11); // B12
+ m_pDoc->SetString(aPos, "=$A$1*4");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell shouldn't be shared.", pFC && !pFC->IsShared());
+
+ aPos.SetRow(8); // B9
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(12); // B13
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Extend B13:B16 to B13:B20.
+ aPos.SetRow(16); // B17
+ m_pDoc->SetString(aPos, "=A17*2");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A18*2");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A19*2");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A20*2");
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
+ // B13:B20 should be shared.
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Empty B19. This should split it into B13:B18, and B20 non-shared.
+ aPos.SetRow(18);
+ m_pDoc->SetEmptyCell(aPos);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This cell should have been emptied.", CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
+ aPos.SetRow(12); // B13
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ // B13:B18 should be shared.
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+ // B20 should be non-shared.
+ aPos.SetRow(19); // B20
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B20 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_MESSAGE("This cell should be non-shared.", !pFC->IsShared());
+
+ // Empty B14, to make B13 non-shared and B15:B18 shared.
+ aPos.SetRow(13); // B14
+ m_pDoc->SetEmptyCell(aPos);
+ aPos.SetRow(12); // B13
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ // B13 should be non-shared.
+ CPPUNIT_ASSERT_MESSAGE("B13 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_MESSAGE("This cell should be non-shared.", !pFC->IsShared());
+ // B15:B18 should be shared.
+ aPos.SetRow(14); // B15
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(14), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Set numeric value to B15, to make B16:B18 shared.
+ aPos.SetRow(14);
+ m_pDoc->SetValue(aPos, 1.2);
+ aPos.SetRow(15);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ // B16:B18 should be shared.
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(15), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Set string value to B16 to make B17:B18 shared.
+ aPos.SetRow(15);
+ ScCellValue aCell(svl::SharedString("Test"));
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string value.", CELLTYPE_STRING, aCell.meType);
+ aCell.commit(*m_pDoc, aPos);
+ CPPUNIT_ASSERT_EQUAL(aCell.mpString->getString(), m_pDoc->GetString(aPos));
+ aPos.SetRow(16);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ // B17:B18 should be shared.
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(16), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Set edit text to B17. Now B18 should be non-shared.
+ ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
+ rEditEngine.SetTextCurrentDefaults("Edit Text");
+ aPos.SetRow(16);
+ m_pDoc->SetEditText(aPos, rEditEngine.CreateTextObject());
+ CPPUNIT_ASSERT_EQUAL(CELLTYPE_EDIT, m_pDoc->GetCellType(aPos));
+ aPos.SetRow(17);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B18 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_MESSAGE("B18 should be non-shared.", !pFC->IsShared());
+
+ // Set up a new group for shared formulas in B2:B10.
+ clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
+
+ aPos.SetRow(1);
+ m_pDoc->SetString(aPos, "=A2*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A3*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A4*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A5*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A6*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A7*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A8*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A9*10");
+ aPos.IncRow();
+ m_pDoc->SetString(aPos, "=A10*10");
+
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B10 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Delete A4:B8. This should split the grouping to B2:B3 and B9:B10.
+ clearRange(m_pDoc, ScRange(0,3,0,1,7,0));
+ aPos.SetRow(1);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B2 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(8);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B9 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Delete rows 4:8 and shift row 9 and below up to row 4. This should
+ // re-merge the two into a group of B2:B5.
+ m_pDoc->DeleteRow(ScRange(0,3,0,MAXCOL,7,0));
+ aPos.SetRow(1);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B2 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Insert 2 rows at row 4, to split it into B2:B3 and B6:B7.
+ m_pDoc->InsertRow(ScRange(0,3,0,MAXCOL,4,0));
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B2 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(5);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B6 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Test implicit intersection with shared formulas.
+ aPos.Set(2,0,0);
+ {
+ // Insert data in C1:D2 and formulas in E1:E2
+ const char* pData[][3] = {
+ { "5", "1", "=C:C/D:D" },
+ { "4", "2", "=C:C/D:D" }
+ };
+
+ insertRangeData(m_pDoc, aPos, pData, SAL_N_ELEMENTS(pData));
+ }
+ aPos.Set(4,1,0);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("E2 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ aPos.SetRow(0);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("5/1=5", 5.0, m_pDoc->GetValue(aPos));
+ aPos.SetRow(1);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("4/2=2", 2.0, m_pDoc->GetValue(aPos));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdate()
+{
+ m_pDoc->InsertTab(0, "Test");
+
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, false); // turn off auto calculation.
+
+ // Set values to A10:A12.
+ m_pDoc->SetValue(ScAddress(0,9,0), 1);
+ m_pDoc->SetValue(ScAddress(0,10,0), 2);
+ m_pDoc->SetValue(ScAddress(0,11,0), 3);
+
+ {
+ // Insert formulas that reference A10:A12 in B1:B3.
+ const char* pData[][1] = {
+ { "=A10" },
+ { "=A11" },
+ { "=A12" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(1,0,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A12", "Wrong formula in B3");
+
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // Insert cells over A11:B11 to shift to right. This should split the B1:B3 grouping into 3.
+ m_pDoc->InsertCol(ScRange(0,10,0,1,10,0));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "C11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A12", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
+ CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
+ CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+
+ // Delete cells over A11:B11 to bring it back to the previous state.
+ m_pDoc->DeleteCol(ScRange(0,10,0,1,10,0));
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A12", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // Insert cells over A11:A12 and shift down.
+ m_pDoc->InsertRow(ScRange(0,10,0,0,11,0));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A13", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A14", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Delete A11:A12 to bring it back to the way it was.
+ m_pDoc->DeleteRow(ScRange(0,10,0,0,11,0));
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "A11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A12", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // Insert cells over A11:B11 to shift to right again.
+ m_pDoc->InsertCol(ScRange(0,10,0,1,10,0));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "C11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "A12", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
+ CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
+ CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+
+ // Insert cells over A12:B12 to shift to right.
+ m_pDoc->InsertCol(ScRange(0,11,0,1,11,0));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "A10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "C11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "C12", "Wrong formula in B3");
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC && !pFC->IsShared());
+ // B2 and B3 should be grouped.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Insert cells over A10:B10 to shift to right.
+ m_pDoc->InsertCol(ScRange(0,9,0,1,9,0));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "C10", "Wrong formula in B1");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "C11", "Wrong formula in B2");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "C12", "Wrong formula in B3");
+
+ // B1:B3 should be now grouped.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateMove()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Set values in B2:B4.
+ for (SCROW i = 1; i <= 3; ++i)
+ m_pDoc->SetValue(ScAddress(1,i,0), i);
+
+ // Make sure the values are really there.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+
+ const char* aData[][1] = {
+ { "=RC[-1]" },
+ { "=RC[-1]" },
+ { "=RC[-1]" }
+ };
+
+ // Set formulas in C2:C4 that reference B2:B4 individually.
+ insertRangeData(m_pDoc, ScAddress(2,1,0), aData, SAL_N_ELEMENTS(aData));
+
+ // Check the formula results.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ // Move B2:B4 to B1:B3.
+ bool bMoved = getDocShell().GetDocFunc().MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(1,0,0), true, true, false, true);
+ CPPUNIT_ASSERT(bMoved);
+
+ // Make sure the values have been moved for real.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ // The formulas should have been adjusted for the move.
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,1,0), "R[-1]C[-1]"));
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,2,0), "R[-1]C[-1]"));
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,3,0), "R[-1]C[-1]"));
+
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+ pUndoMgr->Undo();
+
+ // The values should have moved back.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+
+ // And the formulas should have been re-adjusted to their original references.
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,1,0), "RC[-1]"));
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,2,0), "RC[-1]"));
+ CPPUNIT_ASSERT(checkFormula(*m_pDoc, ScAddress(2,3,0), "RC[-1]"));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateMove2()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, false); // turn auto calc off this time.
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Set values in B2:B3, and E2:E3.
+ for (SCROW i = 1; i <= 2; ++i)
+ {
+ m_pDoc->SetValue(ScAddress(1,i,0), i);
+ m_pDoc->SetValue(ScAddress(4,i,0), i);
+ }
+
+ // Make sure the values are really there.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,2,0)));
+
+ {
+ // Set formulas in C2:C3 that reference B2:B3 individually, and F2:F3 to E2:E3.
+ const char* pData[][1] = {
+ { "=RC[-1]" },
+ { "=RC[-1]" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(2,1,0), pData, SAL_N_ELEMENTS(pData));
+ insertRangeData(m_pDoc, ScAddress(5,1,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ m_pDoc->CalcFormulaTree(); // calculate manually.
+
+ // Check the formula results.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(5,2,0)));
+
+ // Move B2:C3 to C3:D4.
+ bool bMoved = getDocShell().GetDocFunc().MoveBlock(
+ ScRange(1,1,0,2,2,0), ScAddress(2,2,0), true, true, false, true);
+ CPPUNIT_ASSERT(bMoved);
+
+ // Make sure the range has been moved.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ // The formula cells should retain their results even with auto calc off
+ // and without recalculation.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,2,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,3,0)));
+
+ // And these formulas in F2:F3 are unaffected, therefore should not change.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(5,2,0)));
+
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+
+ // Undo the move.
+ pUndoMgr->Undo();
+
+ // Check the formula results. The results should still be intact.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(5,2,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateRange()
+{
+ m_pDoc->InsertTab(0, "Test");
+
+ // Insert values to A3:A5.
+ m_pDoc->SetValue(ScAddress(0,2,0), 1);
+ m_pDoc->SetValue(ScAddress(0,3,0), 2);
+ m_pDoc->SetValue(ScAddress(0,4,0), 3);
+
+ {
+ // Insert formulas to B3:B5.
+ const char* pData[][1] = {
+ { "=SUM($A$3:$A$5)" },
+ { "=SUM($A$3:$A$5)" },
+ { "=SUM($A$3:$A$5)" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(1,2,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM($A$3:$A$5)", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,3,0), "SUM($A$3:$A$5)", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,4,0), "SUM($A$3:$A$5)", "Wrong formula");
+
+ // B3:B5 should be shared.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
+ CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC && pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,3,0));
+ CPPUNIT_ASSERT_MESSAGE("B4 should be shared.", pFC && pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,4,0));
+ CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC && pFC->IsShared());
+
+ // Insert 2 rows at row 1.
+ m_pDoc->InsertRow(ScRange(0,0,0,MAXCOL,1,0));
+
+ // B5:B7 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,4,0));
+ CPPUNIT_ASSERT_MESSAGE("B5 should be shared.", pFC && pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,5,0));
+ CPPUNIT_ASSERT_MESSAGE("B6 should be shared.", pFC && pFC->IsShared());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,6,0));
+ CPPUNIT_ASSERT_MESSAGE("B7 should be shared.", pFC && pFC->IsShared());
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,4,0), "SUM($A$5:$A$7)", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,5,0), "SUM($A$5:$A$7)", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "SUM($A$5:$A$7)", "Wrong formula");
+
+ m_pDoc->DeleteTab(0);
+}
+
+namespace {
+
+struct SortByArea
+{
+ bool operator ()( const sc::AreaListener& rLeft, const sc::AreaListener& rRight ) const
+ {
+ if (rLeft.maArea.aStart.Tab() != rRight.maArea.aStart.Tab())
+ return rLeft.maArea.aStart.Tab() < rRight.maArea.aStart.Tab();
+
+ if (rLeft.maArea.aStart.Col() != rRight.maArea.aStart.Col())
+ return rLeft.maArea.aStart.Col() < rRight.maArea.aStart.Col();
+
+ if (rLeft.maArea.aStart.Row() != rRight.maArea.aStart.Row())
+ return rLeft.maArea.aStart.Row() < rRight.maArea.aStart.Row();
+
+ if (rLeft.maArea.aEnd.Tab() != rRight.maArea.aEnd.Tab())
+ return rLeft.maArea.aEnd.Tab() < rRight.maArea.aEnd.Tab();
+
+ if (rLeft.maArea.aEnd.Col() != rRight.maArea.aEnd.Col())
+ return rLeft.maArea.aEnd.Col() < rRight.maArea.aEnd.Col();
+
+ return rLeft.maArea.aEnd.Row() < rRight.maArea.aEnd.Row();
+ }
+};
+
+}
+
+void Test::testSharedFormulasRefUpdateRangeDeleteRow()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+ m_pDoc->InsertTab(0, "Formula");
+
+ ScRange aWholeArea(0, 0, 0, 100, 100, 0); // Large enough for all references used in the test.
+
+ const char* aData[][3] = {
+ { "1", "2", "=SUM(A1:B1)" },
+ { "3", "4", "=SUM(A2:B2)" },
+ { nullptr, nullptr, nullptr },
+ { "5", "6", "=SUM(A4:B4)" },
+ { "7", "8", "=SUM(A5:B5)" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,0,0), aData, SAL_N_ELEMENTS(aData));
+
+ // Check initial formula values.
+ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
+ CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+ CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(ScAddress(2,4,0)));
+
+ // Check the area listener status.
+ ScBroadcastAreaSlotMachine* pBASM = m_pDoc->GetBASM();
+ CPPUNIT_ASSERT(pBASM);
+ std::vector<sc::AreaListener> aListeners = pBASM->GetAllListeners(aWholeArea, sc::AreaOverlapType::Inside);
+ std::sort(aListeners.begin(), aListeners.end(), SortByArea());
+
+ // This check makes only sense if group listeners are activated.
+#if !defined(USE_FORMULA_GROUP_LISTENER) || USE_FORMULA_GROUP_LISTENER
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("There should only be 2 area listeners.", size_t(2), aListeners.size());
+ // First one should be group-listening on A1:B2.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This listener should be listening on A1:B2.", ScRange(0,0,0,1,1,0), aListeners[0].maArea);
+ CPPUNIT_ASSERT_MESSAGE("This listener should be group-listening.", aListeners[0].mbGroupListening);
+ // Second one should be group-listening on A4:B5.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This listener should be listening on A1:B2.", ScRange(0,0,0,1,1,0), aListeners[0].maArea);
+ CPPUNIT_ASSERT_MESSAGE("This listener should be group-listening.", aListeners[0].mbGroupListening);
+#endif
+
+ // Make sure that C1:C2 and C4:C5 are formula groups.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(2,3,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Delete row 3. This will merge the two formula groups.
+ ScDocFunc& rFunc = getDocShell().GetDocFunc();
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+ rFunc.DeleteCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, DelCellCmd::Rows, true);
+
+ // Make sure C1:C4 belong to the same group.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+
+ // This check makes only sense if group listeners are activated.
+#if !defined(USE_FORMULA_GROUP_LISTENER) || USE_FORMULA_GROUP_LISTENER
+ // We should only have one listener group-listening on A1:B4.
+ aListeners = pBASM->GetAllListeners(aWholeArea, sc::AreaOverlapType::Inside);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("There should only be 1 area listener.", size_t(1), aListeners.size());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This listener should be listening on A1:B4.", ScRange(0,0,0,1,3,0), aListeners[0].maArea);
+ CPPUNIT_ASSERT_MESSAGE("This listener should be group-listening.", aListeners[0].mbGroupListening);
+#endif
+
+ // Change the value of B4 and make sure the value of C4 changes.
+ rFunc.SetValueCell(ScAddress(1,3,0), 100.0, false);
+ CPPUNIT_ASSERT_EQUAL(107.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+
+ // Undo the value change in B4, and make sure C4 follows.
+ pUndoMgr->Undo();
+ CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ // Undo the deletion of row 3.
+ pUndoMgr->Undo();
+
+ // Make sure that C1:C2 and C4:C5 are formula groups again.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(2,3,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check the values of formula cells again.
+ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
+ CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+ CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(ScAddress(2,4,0)));
+
+ aListeners = pBASM->GetAllListeners(aWholeArea, sc::AreaOverlapType::Inside);
+ std::sort(aListeners.begin(), aListeners.end(), SortByArea());
+
+ // This check makes only sense if group listeners are activated.
+#if !defined(USE_FORMULA_GROUP_LISTENER) || USE_FORMULA_GROUP_LISTENER
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("There should only be 2 area listeners.", size_t(2), aListeners.size());
+ // First one should be group-listening on A1:B2.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This listener should be listening on A1:B2.", ScRange(0,0,0,1,1,0), aListeners[0].maArea);
+ CPPUNIT_ASSERT_MESSAGE("This listener should be group-listening.", aListeners[0].mbGroupListening);
+ // Second one should be group-listening on A4:B5.
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("This listener should be listening on A1:B2.", ScRange(0,0,0,1,1,0), aListeners[0].maArea);
+ CPPUNIT_ASSERT_MESSAGE("This listener should be group-listening.", aListeners[0].mbGroupListening);
+#endif
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateExternal()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+ m_pDoc->InsertTab(0, "Formula");
+
+ // Launch an external document shell.
+ ScDocShellRef xExtDocSh = new ScDocShell;
+ xExtDocSh->SetIsInUcalc();
+
+ SfxMedium* pMed = new SfxMedium("file:///extdata.fake", StreamMode::STD_READWRITE);
+ xExtDocSh->DoInitNew(pMed);
+ ScDocument& rExtDoc = xExtDocSh->GetDocument();
+
+ // Populate A1:A3.
+ rExtDoc.InsertTab(0, "Data");
+ rExtDoc.SetString(ScAddress(0,0,0), "A");
+ rExtDoc.SetString(ScAddress(0,1,0), "B");
+ rExtDoc.SetString(ScAddress(0,2,0), "C");
+
+ {
+ // Insert formula cells in A7:A10 of the host document, referencing A1:A3
+ // of the external document.
+ const char* pData[][1] = {
+ { "='file:///extdata.fake'#$Data.A1" },
+ { "='file:///extdata.fake'#$Data.A2" },
+ { "='file:///extdata.fake'#$Data.A3" },
+ { "=COUNTA('file:///extdata.fake'#$Data.A1:A3)" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,6,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ // Check the formula results.
+ CPPUNIT_ASSERT_EQUAL(OUString("A"), m_pDoc->GetString(ScAddress(0,6,0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("B"), m_pDoc->GetString(ScAddress(0,7,0)));
+ CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(0,8,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
+
+ // Check the formulas too.
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "'file:///extdata.fake'#$Data.A1", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "'file:///extdata.fake'#$Data.A2", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "'file:///extdata.fake'#$Data.A3", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "COUNTA('file:///extdata.fake'#$Data.A1:A3)", "Wrong formula!");
+
+ // Delete rows 1 and 2. This should not change the references in the formula cells below.
+ ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+ rDocFunc.DeleteCells(ScRange(0,0,0,MAXCOL,1,0), &aMark, DelCellCmd::CellsUp, true);
+
+ // Check the shifted formula cells now in A5:A8.
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "'file:///extdata.fake'#$Data.A1", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "'file:///extdata.fake'#$Data.A2", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "'file:///extdata.fake'#$Data.A3", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "COUNTA('file:///extdata.fake'#$Data.A1:A3)", "Wrong formula!");
+
+ // Undo and check the formulas again.
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+ pUndoMgr->Undo();
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "'file:///extdata.fake'#$Data.A1", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "'file:///extdata.fake'#$Data.A2", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,8,0), "'file:///extdata.fake'#$Data.A3", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,9,0), "COUNTA('file:///extdata.fake'#$Data.A1:A3)", "Wrong formula!");
+
+ // Redo the row deletion and check the formulas again.
+ pUndoMgr->Redo();
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,4,0), "'file:///extdata.fake'#$Data.A1", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,5,0), "'file:///extdata.fake'#$Data.A2", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,6,0), "'file:///extdata.fake'#$Data.A3", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,7,0), "COUNTA('file:///extdata.fake'#$Data.A1:A3)", "Wrong formula!");
+
+ xExtDocSh->DoClose();
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasInsertRow()
+{
+ struct
+ {
+ bool checkContent( ScDocument* pDoc )
+ {
+ // B1:B2 and B4:B5 should point to $A$5.
+ SCROW pRows[] = { 0, 1, 3, 4 };
+ for (size_t i = 0; i < SAL_N_ELEMENTS(pRows); ++i)
+ {
+ ScAddress aPos(1, pRows[i], 0);
+ ASSERT_FORMULA_EQUAL(*pDoc, aPos, "$A$5", "Wrong formula!");
+ }
+
+ // B1:B2 should be grouped.
+ ScFormulaCell* pFC = pDoc->GetFormulaCell(ScAddress(1,0,0));
+ if (!pFC || pFC->GetSharedTopRow() != 0 || pFC->GetSharedLength() != 2)
+ {
+ cerr << "B1:B2 should be grouped." << endl;
+ return false;
+ }
+
+ // B4:B5 should be grouped.
+ pFC = pDoc->GetFormulaCell(ScAddress(1,3,0));
+ if (!pFC || pFC->GetSharedTopRow() != 3 || pFC->GetSharedLength() != 2)
+ {
+ cerr << "B4:B5 should be grouped." << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ bool checkContentUndo( ScDocument* pDoc )
+ {
+ for (SCROW i = 0; i <= 3; ++i)
+ {
+ ScAddress aPos(1,i,0);
+ ASSERT_FORMULA_EQUAL(*pDoc, aPos, "$A$4", "Wrong formula!");
+ }
+
+ // Ensure that B5 is empty.
+ if (pDoc->GetCellType(ScAddress(1,4,0)) != CELLTYPE_NONE)
+ {
+ cerr << "B5 should be empty." << endl;
+ return false;
+ }
+
+ // B1:B4 should be grouped.
+ ScFormulaCell* pFC = pDoc->GetFormulaCell(ScAddress(1,0,0));
+ if (!pFC || pFC->GetSharedTopRow() != 0 || pFC->GetSharedLength() != 4)
+ {
+ cerr << "B1:B4 should be grouped." << endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ } aCheck;
+
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+ m_pDoc->InsertTab(0, "Test");
+
+ // Scenario inspired by fdo#76470.
+
+ // Set value to A4.
+ m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
+
+ {
+ // Set formula cells in B1:B4 all referencing A4 as absolute reference.
+ const char* pData[][1] = {
+ { "=$A$4" },
+ { "=$A$4" },
+ { "=$A$4" },
+ { "=$A$4" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(1,0,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ // Insert a new row at row 3.
+ ScDocFunc& rFunc = getDocShell().GetDocFunc();
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+ rFunc.InsertCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, INS_INSROWS_BEFORE, true, true);
+
+ bool bResult = aCheck.checkContent(m_pDoc);
+ CPPUNIT_ASSERT_MESSAGE("Failed on the initial content check.", bResult);
+
+ // Undo and check its result.
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+ pUndoMgr->Undo();
+
+ bResult = aCheck.checkContentUndo(m_pDoc);
+ CPPUNIT_ASSERT_MESSAGE("Failed on the content check after undo.", bResult);
+
+ // Redo and check its result.
+ pUndoMgr->Redo();
+ bResult = aCheck.checkContent(m_pDoc);
+ CPPUNIT_ASSERT_MESSAGE("Failed on the content check after redo.", bResult);
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasDeleteRows()
+{
+ m_pDoc->InsertTab(0, "Test");
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ {
+ // Fill data cells A1:A20 and formula cells B1:B20. Formulas in
+ // B1:B10 and B11:B20 should be different.
+ const char* pData[][2] = {
+ { "0", "=RC[-1]+1" },
+ { "1", "=RC[-1]+1" },
+ { "2", "=RC[-1]+1" },
+ { "3", "=RC[-1]+1" },
+ { "4", "=RC[-1]+1" },
+ { "5", "=RC[-1]+1" },
+ { "6", "=RC[-1]+1" },
+ { "7", "=RC[-1]+1" },
+ { "8", "=RC[-1]+1" },
+ { "9", "=RC[-1]+1" },
+ { "10", "=RC[-1]+11" },
+ { "11", "=RC[-1]+11" },
+ { "12", "=RC[-1]+11" },
+ { "13", "=RC[-1]+11" },
+ { "14", "=RC[-1]+11" },
+ { "15", "=RC[-1]+11" },
+ { "16", "=RC[-1]+11" },
+ { "17", "=RC[-1]+11" },
+ { "18", "=RC[-1]+11" },
+ { "19", "=RC[-1]+11" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
+ }
+
+ // B1:B10 should be shared.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+ // B11:B20 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,10,0));
+ CPPUNIT_ASSERT_MESSAGE("1,10 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+
+ // Delete rows 9:12
+ m_pDoc->DeleteRow(ScRange(0,8,0,MAXCOL,11,0));
+
+ // B1:B8 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
+ // B9:B16 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,8,0));
+ CPPUNIT_ASSERT_MESSAGE("1,8 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
+
+ // Delete row 3
+ m_pDoc->DeleteRow(ScRange(0,2,0,MAXCOL,2,0));
+
+ // B1:B7 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(7), pFC->GetSharedLength());
+ // B8:B15 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,7,0));
+ CPPUNIT_ASSERT_MESSAGE("1,7 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(7), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
+
+ // Delete row 5
+ m_pDoc->DeleteRow(ScRange(0,4,0,MAXCOL,4,0));
+
+ // B1:B6 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
+ // B7:B14 should be shared.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,6,0));
+ CPPUNIT_ASSERT_MESSAGE("1,6 must be a shared formula cell.", pFC && pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
+ 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(MAXROW, MAXCOL);
+ 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, DelCellCmd::CellsLeft, 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_EQUAL(svSingleRef, pToken->GetType());
+ const ScSingleRefData* pSRef = pToken->GetSingleRef();
+ CPPUNIT_ASSERT(pSRef->IsColDeleted());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pSRef->toAbs(m_pDoc, 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(pFC);
+ 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, DelCellCmd::CellsLeft, 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");
+ m_pDoc->InsertTab(1, "Sheet2");
+ m_pDoc->InsertTab(2, "Sheet3");
+
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // make sure auto calc is on.
+
+ // Switch to R1C1 for ease of repeated formula insertions.
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ // Fill numbers in A1:A8 on Sheet2.
+ for (SCROW i = 0; i <= 7; ++i)
+ m_pDoc->SetValue(ScAddress(0,i,1), i+1);
+
+ // Fill formula cells A1:A8 on Sheet1, to refer to the same cell address on Sheet2.
+ for (SCROW i = 0; i <= 7; ++i)
+ m_pDoc->SetString(ScAddress(0,i,0), "=Sheet2!RC");
+
+ // Check the results.
+ for (SCROW i = 0; i <= 7; ++i)
+ CPPUNIT_ASSERT_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(ScAddress(0,i,0)));
+
+ // Move Sheet3 to the leftmost position before Sheet1.
+ m_pDoc->MoveTab(2, 0);
+
+ // Check sheet names.
+ std::vector<OUString> aTabNames = m_pDoc->GetAllTableNames();
+ CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet3"), aTabNames[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aTabNames[1]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aTabNames[2]);
+
+ // Check the results again on Sheet1.
+ for (SCROW i = 0; i <= 7; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(ScAddress(0,i,1)));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,i,1), "Sheet2!RC", "Wrong formula expression.");
+ }
+
+ // Insert a new sheet at the left end.
+ m_pDoc->InsertTab(0, "Sheet4");
+
+ // Check sheet names.
+ aTabNames = m_pDoc->GetAllTableNames();
+ CPPUNIT_ASSERT_MESSAGE("There should be at least 4 sheets.", aTabNames.size() >= 4);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet4"), aTabNames[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet3"), aTabNames[1]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aTabNames[2]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aTabNames[3]);
+
+ // Check the results again on Sheet1.
+ for (SCROW i = 0; i <= 7; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(ScAddress(0,i,2)));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,i,2), "Sheet2!RC", "Wrong formula expression.");
+ }
+
+ // Delete Sheet4.
+ m_pDoc->DeleteTab(0);
+
+ // Check sheet names.
+ aTabNames = m_pDoc->GetAllTableNames();
+ CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet3"), aTabNames[0]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aTabNames[1]);
+ CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aTabNames[2]);
+
+ // Check the results again on Sheet1.
+ for (SCROW i = 0; i <= 7; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(ScAddress(0,i,1)));
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,i,1), "Sheet2!RC", "Wrong formula expression.");
+ }
+
+ m_pDoc->DeleteTab(2);
+ m_pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateCopySheets()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // make sure auto calc is on.
+
+ m_pDoc->InsertTab(0, "Sheet1");
+ m_pDoc->InsertTab(1, "Sheet2");
+
+ m_pDoc->SetValue(ScAddress(0,0,1), 1.0); // A1 on Sheet2
+ m_pDoc->SetValue(ScAddress(0,1,1), 2.0); // A2 on Sheet2
+
+ // Reference values on Sheet2, but use absolute sheet references.
+ m_pDoc->SetString(ScAddress(0,0,0), "=$Sheet2.A1");
+ m_pDoc->SetString(ScAddress(0,1,0), "=$Sheet2.A2");
+
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
+
+ // Copy Sheet1 and insert the copied sheet before the current Sheet1 position.
+ m_pDoc->CopyTab(0, 0);
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), "$Sheet2.A1", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "$Sheet2.A2", "Wrong formula");
+
+ // Check the values on the copied sheet.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
+
+ // Check the values on the original sheet.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,1)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,1)));
+
+ m_pDoc->DeleteTab(2);
+ m_pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasRefUpdateDeleteSheets()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // make sure auto calc is on.
+
+ m_pDoc->InsertTab(0, "Sheet1");
+ m_pDoc->InsertTab(1, "Sheet2");
+
+ // Set values to B2:B4 on Sheet2.
+ m_pDoc->SetValue(ScAddress(1,1,1), 1.0);
+ m_pDoc->SetValue(ScAddress(1,2,1), 2.0);
+ m_pDoc->SetValue(ScAddress(1,3,1), 3.0);
+
+ // Set formulas in A1:A3 on Sheet1 that reference B2:B4 on Sheet2.
+ m_pDoc->SetString(ScAddress(0,0,0), "=Sheet2.B2");
+ m_pDoc->SetString(ScAddress(0,1,0), "=Sheet2.B3");
+ m_pDoc->SetString(ScAddress(0,2,0), "=Sheet2.B4");
+
+ // Check the formula results.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), "Sheet2.B2", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "Sheet2.B3", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "Sheet2.B4", "Wrong formula");
+
+ // Delete Sheet2.
+ ScDocFunc& rFunc = getDocShell().GetDocFunc();
+ rFunc.DeleteTable(1, true);
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), "#REF!.B2", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "#REF!.B3", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "#REF!.B4", "Wrong formula");
+
+ // Undo the deletion and make sure the formulas are back to the way they were.
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+ pUndoMgr->Undo();
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,0,0), "Sheet2.B2", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,1,0), "Sheet2.B3", "Wrong formula");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(0,2,0), "Sheet2.B4", "Wrong formula");
+
+ // TODO: We can't test redo yet as ScUndoDeleteTab::Redo() relies on
+ // view shell to do its thing.
+
+ m_pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulasCopyPaste()
+{
+ m_pDoc->InsertTab(0, "Test");
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ // Fill formula cells B1:B10.
+ for (SCROW i = 0; i <= 9; ++i)
+ m_pDoc->SetString(1, i, 0, "=RC[-1]");
+
+ ScAddress aPos(1, 8, 0); // B9
+ ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B9 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Copy formulas in B6:B9 to the clipboard doc.
+ ScRange aSrcRange(1,5,0,1,8,0); // B6:B9
+ ScDocument aClipDoc(SCDOCMODE_CLIP);
+ copyToClip(m_pDoc, aSrcRange, &aClipDoc);
+ pFC = aClipDoc.GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("B9 in the clip doc should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ // Paste them to C2:C10.
+ ScRange aDestRange(2,1,0,2,9,0);
+ pasteFromClip(m_pDoc, aDestRange, &aClipDoc);
+ aPos.SetCol(2);
+ aPos.SetRow(1);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("C2 should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+
+ ScRange aRange(1,0,0,1,9,0); // B1:B10
+ ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
+ pUndoDoc->InitUndo(m_pDoc, 0, 0, true, true);
+ m_pDoc->CopyToDocument(aRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc);
+ std::unique_ptr<ScUndoPaste> pUndo(createUndoPaste(getDocShell(), aRange, ScDocumentUniquePtr(pUndoDoc)));
+
+ // First, make sure the formula cells are shared in the undo document.
+ aPos.SetCol(1);
+ for (SCROW i = 0; i <= 9; ++i)
+ {
+ aPos.SetRow(i);
+ pFC = pUndoDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("Must be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+ }
+
+ // Overwrite B1:B10.
+ for (SCROW i = 0; i <= 9; ++i)
+ m_pDoc->SetValue(ScAddress(1,i,0), i*10);
+
+ for (SCROW i = 0; i <= 9; ++i)
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Numeric cell was expected.", CELLTYPE_VALUE, m_pDoc->GetCellType(ScAddress(1,i,0)));
+
+ // Undo the action to fill B1:B10 with formula cells again.
+ pUndo->Undo();
+
+ aPos.SetCol(1);
+ for (SCROW i = 0; i <= 9; ++i)
+ {
+ aPos.SetRow(i);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(10), pFC->GetSharedLength());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("The token is expected to be shared.", pFC->GetCode(), pFC->GetSharedCode());
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaInsertColumn()
+{
+ m_pDoc->InsertTab(0, "Test");
+
+ // Set shared formula group over H2:H3.
+ m_pDoc->SetString(ScAddress(7,1,0), "=G3*B3");
+ m_pDoc->SetString(ScAddress(7,2,0), "=G4*B4");
+
+ // Insert a single column at Column F. This used to crash before fdo#74041.
+ m_pDoc->InsertCol(ScRange(5,0,0,5,MAXROW,0));
+
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(8,1,0), "H3*B3", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(8,2,0), "H4*B4", "Wrong formula!");
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaMoveBlock()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Set values to A1:A3.
+ m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
+ m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
+ m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
+
+ // Set formulas in B1:B3 to reference A1:A3.
+ m_pDoc->SetString(ScAddress(1,0,0), "=RC[-1]");
+ m_pDoc->SetString(ScAddress(1,1,0), "=RC[-1]");
+ m_pDoc->SetString(ScAddress(1,2,0), "=RC[-1]");
+
+ ScRange aFormulaRange(1,0,0,1,2,0);
+
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ clearFormulaCellChangedFlag(*m_pDoc, aFormulaRange);
+
+ // Move A1:A3 to D1:D3.
+ ScDocFunc& rFunc = getDocShell().GetDocFunc();
+ bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(3,0,0), true, true, false, true);
+ CPPUNIT_ASSERT(bMoved);
+
+ // The result should stay the same.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ clearFormulaCellChangedFlag(*m_pDoc, aFormulaRange);
+
+ // Make sure these formula cells in B1:B3 have correct positions even after the move.
+ std::vector<SCROW> aRows;
+ aRows.push_back(0);
+ aRows.push_back(1);
+ aRows.push_back(2);
+ bool bRes = checkFormulaPositions(*m_pDoc, 0, 1, aRows.data(), aRows.size());
+ CPPUNIT_ASSERT(bRes);
+
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+
+ // Undo and check the result.
+ pUndoMgr->Undo();
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ clearFormulaCellChangedFlag(*m_pDoc, aFormulaRange);
+
+ // Redo and check the result.
+ pUndoMgr->Redo();
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ // Clear the range and start over.
+ clearRange(m_pDoc, ScRange(0,0,0,MAXCOL,MAXROW,0));
+
+ // Set values 1,2,3,4,5 to A1:A5.
+ for (SCROW i = 0; i <= 4; ++i)
+ m_pDoc->SetValue(ScAddress(0,i,0), (i+1));
+
+ // Set formulas to B1:B5.
+ for (SCROW i = 0; i <= 4; ++i)
+ m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
+
+ // Check the initial formula results.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+
+ // Move A1:A2 to D2:D3.
+ bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(3,1,0), true, true, false, true);
+ CPPUNIT_ASSERT(bMoved);
+
+ // Check the formula values again. They should not change.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+
+ pUndoMgr->Undo();
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+
+ pUndoMgr->Redo();
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,3,0)));
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaUpdateOnNamedRangeChange()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ const char* const pName = "MyRange";
+ const char* const pExpr1 = "$Test.$A$1:$A$3";
+ const char* const pExpr2 = "$Test.$A$1:$A$4";
+
+ RangeNameDef aName;
+ aName.mpName = pName;
+ aName.mpExpr = pExpr1;
+ aName.mnIndex = 1;
+ std::unique_ptr<ScRangeName> pNames(new ScRangeName);
+ bool bSuccess = insertRangeNames(m_pDoc, pNames.get(), &aName, &aName + 1);
+ CPPUNIT_ASSERT(bSuccess);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pNames->size());
+ m_pDoc->SetRangeName(std::move(pNames));
+
+ // Set values to A1:A4.
+ m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
+ m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
+ m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
+ m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
+
+ // Set formula to B1:B3.
+ m_pDoc->SetString(ScAddress(1,0,0), "=SUM(MyRange)");
+ m_pDoc->SetString(ScAddress(1,1,0), "=SUM(MyRange)");
+ m_pDoc->SetString(ScAddress(1,2,0), "=SUM(MyRange)");
+
+ // Set single formula with no named range to B5.
+ m_pDoc->SetString(ScAddress(1,4,0), "=ROW()");
+
+ // Set shared formula with no named range to B7:B8.
+ m_pDoc->SetString(ScAddress(1,6,0), "=ROW()");
+ m_pDoc->SetString(ScAddress(1,7,0), "=ROW()");
+
+ // B1:B3 should be grouped.
+ ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // B7:B8 should be grouped.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,6,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+ CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,6,0)));
+ CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(1,7,0)));
+
+ // Set a single formula to C1.
+ m_pDoc->SetString(ScAddress(2,0,0), "=AVERAGE(MyRange)");
+ pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("C1 should not be shared.", !pFC->IsShared());
+ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0)));
+
+ // Update the range of MyRange.
+ pNames.reset(new ScRangeName);
+ aName.mpExpr = pExpr2;
+ bSuccess = insertRangeNames(m_pDoc, pNames.get(), &aName, &aName + 1);
+ CPPUNIT_ASSERT(bSuccess);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pNames->size());
+ ScDocFunc& rFunc = getDocShell().GetDocFunc();
+
+ typedef std::map<OUString, std::unique_ptr<ScRangeName>> NameMapType;
+ NameMapType aNewNames;
+ OUString aScope(STR_GLOBAL_RANGE_NAME);
+ aNewNames.insert(std::make_pair(aScope, std::move(pNames)));
+ rFunc.ModifyAllRangeNames(aNewNames);
+
+ // Check to make sure all displayed formulas are still good.
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,0,0), "SUM(MyRange)", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,1,0), "SUM(MyRange)", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,2,0), "SUM(MyRange)", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,4,0), "ROW()", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,6,0), "ROW()", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(1,7,0), "ROW()", "Wrong formula!");
+ ASSERT_FORMULA_EQUAL(*m_pDoc, ScAddress(2,0,0), "AVERAGE(MyRange)", "Wrong formula!");
+
+ // Check the calculation results as well.
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+ CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,4,0)));
+ CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,6,0)));
+ CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(1,7,0)));
+ CPPUNIT_ASSERT_EQUAL(2.5, m_pDoc->GetValue(ScAddress(2,0,0)));
+
+ // Change the value of A4 and make sure the value change gets propagated.
+ m_pDoc->SetValue(ScAddress(0,3,0), 0.0);
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaUpdateOnDBChange()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "RangeTest");
+
+ // Put 1, 2, 3, 4 in A1:A4.
+ for (SCROW i = 0; i <= 3; ++i)
+ m_pDoc->SetValue(ScAddress(0,i,0), (i+1));
+
+ ScDBCollection* pDBs = m_pDoc->GetDBCollection();
+ CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
+
+ // Define database range 'MyRange' for A1:A2.
+ std::unique_ptr<ScDBData> pData(new ScDBData("MyRange", 0, 0, 0, 0, 1));
+ bool bInserted = pDBs->getNamedDBs().insert(std::move(pData));
+ CPPUNIT_ASSERT_MESSAGE("Failed to insert a new database range.", bInserted);
+
+ // Insert in C2:C4 a group of formula cells that reference MyRange.
+ for (SCROW i = 1; i <= 3; ++i)
+ m_pDoc->SetString(ScAddress(2,i,0), "=SUM(MyRange)");
+
+ // Make sure C2:C4 is a formula group.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT(pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // Check the initial formula results.
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ ScDBDocFunc aFunc(getDocShell());
+
+ // Change the range referenced by MyRange to A1:A4.
+ ScDBCollection aNewDBs(m_pDoc);
+ std::unique_ptr<ScDBData> pNewData(new ScDBData("MyRange", 0, 0, 0, 0, 3));
+ bInserted = aNewDBs.getNamedDBs().insert(std::move(pNewData));
+ CPPUNIT_ASSERT_MESSAGE("Failed to insert a new database range.", bInserted);
+
+ std::vector<ScRange> aDeleted;
+ aFunc.ModifyAllDBData(aNewDBs, aDeleted);
+
+ // Check the updated formula results.
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
+ CPPUNIT_ASSERT(pUndoMgr);
+
+ // Undo and check the results.
+ pUndoMgr->Undo();
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ // Redo and check the results.
+ pUndoMgr->Redo();
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,2,0)));
+ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,3,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaAbsCellListener()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
+
+ const char* pData[][1] = {
+ { "=$A$1" },
+ { "=$A$1" },
+ { "=$A$1" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(1,0,0), pData, SAL_N_ELEMENTS(pData));
+
+ // A1 should have 3 listeners listening into it.
+ const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
+ CPPUNIT_ASSERT(pBC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pBC->GetAllListeners().size());
+
+ // Check the formula results.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ // Change the value of A1 and make sure B1:B3 follows.
+ m_pDoc->SetValue(ScAddress(0,0,0), 2.5);
+
+ CPPUNIT_ASSERT_EQUAL(2.5, m_pDoc->GetValue(ScAddress(1,0,0)));
+ CPPUNIT_ASSERT_EQUAL(2.5, m_pDoc->GetValue(ScAddress(1,1,0)));
+ CPPUNIT_ASSERT_EQUAL(2.5, m_pDoc->GetValue(ScAddress(1,2,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+static double checkNewValuesNotification( ScDocument* pDoc, const ScAddress& rOrgPos )
+{
+ ScAddress aPos(rOrgPos);
+ aPos.IncCol();
+ pDoc->SetValues( aPos, {1024.0, 2048.0, 4096.0, 8192.0, 16384.0});
+ aPos = rOrgPos;
+ double fVal = 0.0;
+ for (SCROW i=0; i < 5; ++i)
+ {
+ fVal += pDoc->GetValue(aPos);
+ aPos.IncRow();
+ }
+ return fVal;
+}
+
+void Test::testSharedFormulaUnshareAreaListeners()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ const char* pData[][2] = {
+ { "=SUM(B1:B2)", "1" },
+ { "=SUM(B2:B3)", "2" },
+ { "=SUM(B3:B4)", "4" },
+ { nullptr, "8" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
+
+ // Check that A1:A3 is a formula group.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ m_pDoc->SetValue(ScAddress(0,1,0), 23.0); // unshare at A2
+ m_pDoc->SetValue(ScAddress(1,1,0), 16.0); // change value of B2
+ m_pDoc->SetValue(ScAddress(1,2,0), 32.0); // change value of B3
+ // A1 and A3 should be recalculated.
+ CPPUNIT_ASSERT_EQUAL(17.0, m_pDoc->GetValue(ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(ScAddress(0,2,0)));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 1,3,0));
+
+ for (int nRun = 0; nRun < 7; ++nRun)
+ {
+ // Data in A2:C6
+ const ScAddress aOrgPos(0,1,0);
+ const char* pData2[][3] = {
+ { "=SUM(B2:C2)", "1", "2" },
+ { "=SUM(B3:C3)", "4", "8" },
+ { "=SUM(B4:C4)", "16", "32" },
+ { "=SUM(B5:C5)", "64", "128" },
+ { "=SUM(B6:C6)", "256", "512" },
+ };
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Check that A2:A6 is a formula group.
+ pFC = m_pDoc->GetFormulaCell(aOrgPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aOrgPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength());
+
+ // Overwrite and thus unshare formula in A3.
+ // Check different code paths with different methods.
+ ScAddress aPos(aOrgPos);
+ aPos.IncRow(2);
+ switch (nRun)
+ {
+ case 0:
+ // Directly set a different formula cell, which bypasses
+ // ScDocument::SetString(), mimicking formula input in view.
+ {
+ ScFormulaCell* pCell = new ScFormulaCell( m_pDoc, aPos, "=B4");
+ ScDocFunc& rDocFunc = getDocShell().GetDocFunc();
+ rDocFunc.SetFormulaCell( aPos, pCell, false);
+ }
+ break;
+ case 1:
+ m_pDoc->SetString( aPos, "=B4"); // set formula
+ break;
+ case 2:
+ m_pDoc->SetString( aPos, "x"); // set string
+ break;
+ case 3:
+ m_pDoc->SetString( aPos, "4096"); // set number/numeric
+ break;
+ case 4:
+ m_pDoc->SetValue( aPos, 4096.0); // set numeric
+ break;
+ case 5:
+ m_pDoc->SetValues( aPos, {4096.0}); // set numeric vector
+ break;
+ case 6:
+ // Set formula cell vector.
+ {
+ ScFormulaCell* pCell = new ScFormulaCell( m_pDoc, aPos, "=B4");
+ std::vector<ScFormulaCell*> aCells;
+ aCells.push_back(pCell);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+ }
+ break;
+ }
+
+ // Check that A2:A3 and A5:A6 are two formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(3);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check that listeners were set up and formulas are updated when B2:B6
+ // get new values input (tdf#123736).
+ aPos = aOrgPos;
+ aPos.IncCol();
+ m_pDoc->SetValues( aPos, {1024.0, 2048.0, 4096.0, 8192.0, 16384.0});
+
+ aPos = aOrgPos;
+ CPPUNIT_ASSERT_EQUAL(1026.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL(2056.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ if (nRun != 2) // if not string
+ CPPUNIT_ASSERT_EQUAL(4096.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL(8320.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL(16896.0, m_pDoc->GetValue(aPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,5,0));
+ }
+
+ // Check detach/regroup combinations of overlapping when setting formula
+ // cell vectors.
+ {
+ // Fixed data in A3:C7, modified formula range A1:A9
+ const ScAddress aOrgPos(0,2,0);
+ ScAddress aPos( ScAddress::UNINITIALIZED);
+ ScFormulaCell* pCell;
+ std::vector<ScFormulaCell*> aCells;
+ const char* pData2[][3] = {
+ { "=SUM(B3:C3)", "1", "2" },
+ { "=SUM(B4:C4)", "4", "8" },
+ { "=SUM(B5:C5)", "16", "32" },
+ { "=SUM(B6:C6)", "64", "128" },
+ { "=SUM(B7:C7)", "256", "512" },
+ };
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Add grouping formulas in A1:A2, keep A3:A7
+ aPos = ScAddress(0,0,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B1:C1)");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B2:C2)");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check it is one formula group.
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Add formulas in A1:A2, keep A3:A7
+ aPos = ScAddress(0,0,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B1+C1");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B2+C2");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Add formula in A2, overwrite A3, keep A4:A7
+ aPos = ScAddress(0,1,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B2+C2");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B3+C3");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A4", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(4), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Overwrite A3:A4, keep A5:A7
+ aPos = ScAddress(0,2,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B3+C3");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B4+C4");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(3), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3, overwrite A4:A5, keep A6:A7
+ aPos = ScAddress(0,3,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B4+C4");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B5+C5");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", !pFC->IsSharedTop());
+ aPos.IncRow(1);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A4", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A6", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3:A4, overwrite A5:A6, keep A7
+ aPos = ScAddress(0,4,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B5+C5");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B6+C6");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ aPos.IncRow(2);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A7", !pFC->IsSharedTop());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3:A5, overwrite A6:A7
+ aPos = ScAddress(0,5,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B6+C6");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B7+C7");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(3), pFC->GetSharedLength());
+ aPos.IncRow(3);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A6", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3:A6, overwrite A7, add A8
+ aPos = ScAddress(0,6,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B7+C7");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B8+C8");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(4), pFC->GetSharedLength());
+ aPos.IncRow(4);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A7", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3:A7, add A8:A9
+ aPos = ScAddress(0,7,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B8+C8");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=B9+C9");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check formula groups.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength());
+ aPos.IncRow(5);
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A7", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Keep A3:A7, add grouping formulas in A8:A9
+ aPos = ScAddress(0,7,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B8:C8)");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B9:C9)");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check it is one formula group.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+
+ insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2));
+
+ // Overwrite grouping formulas in A4:A5
+ aPos = ScAddress(0,3,0);
+ std::vector<ScFormulaCell*>().swap( aCells);
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B4:C4)");
+ aCells.push_back(pCell);
+ aPos.IncRow();
+ pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B5:C5)");
+ aCells.push_back(pCell);
+ aPos.IncRow(-1);
+ m_pDoc->SetFormulaCells( aPos, aCells);
+
+ // Check it is one formula group.
+ aPos = aOrgPos;
+ pFC = m_pDoc->GetFormulaCell(aPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength());
+
+ // Check notification of setting new values.
+ CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos));
+
+ clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0));
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaListenerDeleteArea()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test0");
+ m_pDoc->InsertTab(1, "Test1");
+
+ const char* pData0[][3] = {
+ { "", "", "=Test1.C1" },
+ { "", "", "=Test1.C2" }
+ };
+ const char* pData1[][3] = {
+ { "=Test0.A1", "=Test0.B1", "=SUM(A1:B1)" },
+ { "=Test0.A2", "=Test0.B2", "=SUM(A2:B2)" },
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,0,0), pData0, SAL_N_ELEMENTS(pData0));
+ insertRangeData(m_pDoc, ScAddress(0,0,1), pData1, SAL_N_ELEMENTS(pData1));
+
+ // Check that Test1.A1:A2 and Test1.B1:B2 are formula groups.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,0,1));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,1));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ m_pDoc->SetValue(ScAddress(0,1,0), 1.0); // change value of Test0.A2
+ m_pDoc->SetValue(ScAddress(1,1,0), 2.0); // change value of Test0.B2
+ // Test0.C2 should be recalculated.
+ CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+
+ // Delete Test0.B2
+ clearRange(m_pDoc, ScRange(1,1,0));
+ // Test0.C2 should be recalculated.
+ CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
+
+ m_pDoc->DeleteTab(1);
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaUpdateOnReplacement()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ const char* pData[][1] = {
+ { "1" },
+ { "=SUM($A$1:$A1)" },
+ { "=SUM($A$1:$A2)" },
+ { "=SUM($A$1:$A3)" },
+ { "=SUM($A$1:$A4)" },
+ { "=SUM($A$1:$A5)" },
+ { "=SUM($A$1:$A6)" },
+ { "=SUM($A$1:$A7)" }
+ };
+
+ insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
+
+ // Check that A2:A8 is a formula group.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ { // Check initial results.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ // Set up an undo object for deleting A4.
+ ScRange aUndoRange(0,3,0,0,3,0);
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+ aMark.SetMultiMarkArea(aUndoRange);
+ ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
+ pUndoDoc->InitUndo(m_pDoc, 0, 0);
+ m_pDoc->CopyToDocument(aUndoRange, InsertDeleteFlags::CONTENTS, false, *pUndoDoc, &aMark);
+ ScUndoDeleteContents aUndo(&getDocShell(), aMark, aUndoRange, std::move(pUndoDoc), false, InsertDeleteFlags::CONTENTS, true);
+
+ // Delete A4.
+ clearRange(m_pDoc, aUndoRange);
+
+ // Check that A2:A3 and A5:A8 are formula groups.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+
+ { // Check results of A4 deleted.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 0.0, 4.0, 8.0, 16.0, 32.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ // Restore A4.
+ aUndo.Undo();
+
+ // Check that A2:A8 is a formula group.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ { // Check initial results.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ // Delete A4 using selection.
+ m_pDoc->DeleteSelection(InsertDeleteFlags::ALL, aMark);
+
+ // Check that A2:A3 and A5:A8 are formula groups.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+
+ { // Check results of A4 deleted.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 0.0, 4.0, 8.0, 16.0, 32.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ // Restore A4.
+ aUndo.Undo();
+
+ // Check that A2:A8 is a formula group.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ { // Check initial results.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ // Replace A4 with 0.
+ m_pDoc->SetString( ScAddress(0,3,0), "0");
+
+ // Check that A2:A3 and A5:A8 are formula groups.
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,1,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+ pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
+
+ { // Check results of A4 set to zero.
+ ScAddress aPos(0,0,0);
+ const double fResult[] = { 1.0, 1.0, 2.0, 0.0, 4.0, 8.0, 16.0, 32.0 };
+ for (SCROW nRow = 1; nRow < 8; ++nRow)
+ {
+ aPos.SetRow(nRow);
+ CPPUNIT_ASSERT_EQUAL( fResult[nRow], m_pDoc->GetValue( aPos));
+ }
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaDeleteTopCell()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ const char* pData[][2] = {
+ { "=SUM(B$1:B$2)", "1" },
+ { "=SUM(B$1:B$2)", "2" }
+ };
+
+ insertRangeData( m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
+
+ // Check that A1:A2 is a formula group.
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0));
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
+
+ // Check results A1:A2.
+ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,0,0)));
+ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,1,0)));
+
+ // Delete cell A1.
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+ getDocShell().GetDocFunc().DeleteCell( ScAddress(0,0,0), aMark, InsertDeleteFlags::CONTENTS, false);
+ // Check it's gone.
+ CPPUNIT_ASSERT(!m_pDoc->GetFormulaCell( ScAddress(0,0,0)));
+
+ // Check result A2.
+ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,1,0)));
+
+ // Replace B1 with 4.
+ m_pDoc->SetString( ScAddress(1,0,0), "4");
+
+ // Check result A2.
+ CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue( ScAddress(0,1,0)));
+
+ m_pDoc->DeleteTab(0);
+}
+
+void Test::testSharedFormulaCutCopyMoveIntoRef()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ // tdf#123714 case 1
+ {
+ m_pDoc->InsertTab(0, "Test");
+
+ // Data in A1:C3
+ std::vector<std::vector<const char*>> aData = {
+ { "=B1", "", "1" },
+ { "=B2", "", "1" },
+ { "=B3", "", "" }
+ };
+ const ScAddress aOrgPos(0,0,0);
+ insertRangeData( m_pDoc, aOrgPos, aData);
+
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+
+ // Set up clip document.
+ ScDocument aClipDoc(SCDOCMODE_CLIP);
+ aClipDoc.ResetClip(m_pDoc, &aMark);
+ // Cut C1:C2 to clipboard.
+ cutToClip( getDocShell(), ScRange(2,0,0, 2,1,0), &aClipDoc, false);
+
+ // Paste to B1:B2
+ ScRange aPasteRange(1,0,0, 1,1,0);
+ aMark.SetMarkArea(aPasteRange);
+ m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
+
+ // Check data in A1:A2 after Paste.
+ ScAddress aPos(aOrgPos);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("A1", 1.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("A2", 1.0, m_pDoc->GetValue(aPos));
+
+ m_pDoc->DeleteTab(0);
+ }
+
+ // tdf#123714 case 2
+ {
+ m_pDoc->InsertTab(0, "Test");
+
+ // Data in A1:C3
+ std::vector<std::vector<const char*>> aData = {
+ { "1", "2", "=SUM(A1:B1)" },
+ { "4", "8", "=SUM(A2:B2)" },
+ { "16", "32", "=SUM(A3:B3)" },
+ { "64", "128", "=SUM(A4:B4)" },
+ };
+ const ScAddress aOrgPos(0,0,0);
+ insertRangeData( m_pDoc, aOrgPos, aData);
+
+ ScAddress aPos;
+ // Check results in C1:C4
+ const double fVec0[] = { 3.0, 12.0, 48.0, 192.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec0[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+
+ // Set up clip document.
+ ScDocument aClipDoc(SCDOCMODE_CLIP);
+ aClipDoc.ResetClip(m_pDoc, &aMark);
+ // Cut B1:B2 to clipboard.
+ cutToClip( getDocShell(), ScRange(1,0,0, 1,1,0), &aClipDoc, false);
+
+ // Check results in C1:C4 after Cut.
+ const double fVec1[] = { 1.0, 4.0, 48.0, 192.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec1[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Paste to B3:B4
+ ScRange aPasteRange(1,2,0, 1,3,0);
+ aMark.SetMarkArea(aPasteRange);
+ m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
+
+ // Check results in C1:C4 after Paste.
+ const double fVec2[] = { 1.0, 4.0, 18.0, 72.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec2[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Paste to B1:B2
+ aPasteRange = ScRange(1,0,0, 1,1,0);
+ aMark.SetMarkArea(aPasteRange);
+ m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
+
+ // Check results in C1:C4 after Paste.
+ const double fVec3[] = { 3.0, 12.0, 18.0, 72.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec3[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ m_pDoc->DeleteTab(0);
+ }
+}
+
+// tdf#121002
+void Test::testSharedFormulaCutCopyMoveWithRef()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Data in A1:C4
+ std::vector<std::vector<const char*>> aData = {
+ { "", "", "=SUM(A1:B1)" },
+ { "", "", "=SUM(A2:B2)" },
+ { "1", "2", "=SUM(A3:B3)" },
+ { "4", "8", "=SUM(A4:B4)" }
+ };
+ const ScAddress aOrgPos(0,0,0);
+ insertRangeData( m_pDoc, aOrgPos, aData);
+
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+
+ ScAddress aPos( ScAddress::UNINITIALIZED);
+
+ // Check results in C1:C4
+ const double fVec0[] = { 0.0, 0.0, 3.0, 12.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec0[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Set up clip document.
+ ScDocument aClipDoc(SCDOCMODE_CLIP);
+ aClipDoc.ResetClip(m_pDoc, &aMark);
+ // Cut A3:B3 to clipboard.
+ cutToClip( getDocShell(), ScRange(0,2,0, 1,2,0), &aClipDoc, false);
+
+ // Check results in C1:C4 after Cut.
+ const double fVec1[] = { 0.0, 0.0, 0.0, 12.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec1[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Paste to A1:B1
+ ScRange aPasteRange(0,0,0, 1,0,0);
+ aMark.SetMarkArea(aPasteRange);
+ m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
+
+ // Check results in C1:C4 after Paste.
+ const double fVec2[] = { 3.0, 0.0, 3.0, 12.0 };
+ aPos = ScAddress(2,0,0);
+ for (SCROW i=0; i < 4; ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL( fVec2[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Check formulas in C1:C4 after Paste.
+ const OUStringLiteral sForm[] = { "=SUM(A1:B1)", "=SUM(A2:B2)", "=SUM(A1:B1)", "=SUM(A4:B4)" };
+ for (SCROW i=0; i < 4; ++i)
+ {
+ OUString aFormula;
+ m_pDoc->GetFormula( 2,i,0, aFormula);
+ CPPUNIT_ASSERT_EQUAL( OUString(sForm[i]), aFormula);
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+// tdf#120013
+void Test::testSharedFormulaCutCopyMoveWithinRun()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Data in C3:E9
+ const std::vector<std::vector<const char*>> aData = {
+ { "2200", "", "=SUM(C$3:C3)-SUM(D$3:D3)" },
+ { "", "", "=SUM(C$3:C4)-SUM(D$3:D4)" },
+ { "", "1900", "=SUM(C$3:C5)-SUM(D$3:D5)" },
+ { "", "", "=SUM(C$3:C6)-SUM(D$3:D6)" },
+ { "1600", "", "=SUM(C$3:C7)-SUM(D$3:D7)" },
+ { "", "1000", "=SUM(C$3:C8)-SUM(D$3:D8)" },
+ { "", "", "=SUM(C$3:C9)-SUM(D$3:D9)" }
+ };
+ const ScAddress aOrgPos(2,2,0);
+ insertRangeData( m_pDoc, aOrgPos, aData);
+
+ // Check that E3:E9 is a formula group.
+ const ScAddress aFormulaPos(4,2,0);
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( aFormulaPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula top row.", aFormulaPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula length.", static_cast<SCROW>(7), pFC->GetSharedLength());
+
+ ScAddress aPos( ScAddress::UNINITIALIZED);
+
+ // Check results in E3:E9
+ const double fVec0[] = { 2200.0, 2200.0, 300.0, 300.0, 1900.0, 900.0, 900.0 };
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec0), aData.size());
+ aPos = aFormulaPos;
+ for (size_t i=0; i < SAL_N_ELEMENTS(fVec0); ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9", fVec0[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ ScMarkData aMark(MAXROW, MAXCOL);
+ aMark.SelectOneTable(0);
+
+ // Set up clip document.
+ ScDocument aClipDoc(SCDOCMODE_CLIP);
+ aClipDoc.ResetClip(m_pDoc, &aMark);
+ // Cut A8:D8 to clipboard.
+ cutToClip( getDocShell(), ScRange(0,7,0, 3,7,0), &aClipDoc, false);
+
+ // Check results in E3:E9 after Cut.
+ const double fVec1[] = { 2200.0, 2200.0, 300.0, 300.0, 1900.0, 1900.0, 1900.0 };
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec1), aData.size());
+ aPos = aFormulaPos;
+ for (size_t i=0; i < SAL_N_ELEMENTS(fVec1); ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9 after Cut.", fVec1[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ // Paste to A4:D4
+ ScRange aPasteRange(0,3,0, 3,3,0);
+ aMark.SetMarkArea(aPasteRange);
+ m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc);
+
+ // Check results in E3:E9 after Paste.
+ const double fVec2[] = { 2200.0, 1200.0, -700.0, -700.0, 900.0, 900.0, 900.0 };
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec2), aData.size());
+ aPos = aFormulaPos;
+ for (size_t i=0; i < SAL_N_ELEMENTS(fVec2); ++i)
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9 after Paste.", fVec2[i], m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+// tdf#129396
+void Test::testSharedFormulaInsertShift()
+{
+ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
+
+ m_pDoc->InsertTab(0, "Test");
+
+ // Data in A1:C2
+ const std::vector<std::vector<const char*>> aData = {
+ { "1", "2", "=SUM(A1:B1)" },
+ { "4", "8", "=SUM(A2:B2)" }
+ };
+ const ScAddress aOrgPos(0,0,0);
+ insertRangeData( m_pDoc, aOrgPos, aData);
+
+ {
+ // Check that C1:C2 is a formula group.
+ const ScAddress aFormulaPos(2,0,0);
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( aFormulaPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula top row.", aFormulaPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ }
+
+ {
+ // Check results in C1:C2
+ ScAddress aPos( aOrgPos);
+ aPos.SetCol(2);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "C1", 3.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "C2", 12.0, m_pDoc->GetValue(aPos));
+ }
+
+ const bool bSuccess = m_pDoc->InsertCol( 0, 0, 1, 0, 2, 1);
+ CPPUNIT_ASSERT_MESSAGE( "InsertCol", bSuccess);
+
+ {
+ // Check that D1:D2 is a formula group.
+ const ScAddress aFormulaPos(3,0,0);
+ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( aFormulaPos);
+ CPPUNIT_ASSERT(pFC);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula top row.", aFormulaPos.Row(), pFC->GetSharedTopRow());
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula length.", static_cast<SCROW>(2), pFC->GetSharedLength());
+ }
+
+ {
+ // Modify values in B1:B2
+ ScAddress aPos( aOrgPos);
+ aPos.SetCol(1);
+ m_pDoc->SetValue(aPos, 16.0);
+ aPos.IncRow();
+ m_pDoc->SetValue(aPos, 32.0);
+ }
+
+ {
+ // Check results in D1:D2
+ ScAddress aPos( aOrgPos);
+ aPos.SetCol(3);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "D1", 17.0, m_pDoc->GetValue(aPos));
+ aPos.IncRow();
+ CPPUNIT_ASSERT_EQUAL_MESSAGE( "D2", 36.0, m_pDoc->GetValue(aPos));
+ }
+
+ m_pDoc->DeleteTab(0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */