1
0
Fork 0
libreoffice/sc/qa/unit/ucalc_sharedformula.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

2795 lines
116 KiB
C++

/* -*- 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 "helper/debughelper.hxx"
#include "helper/qahelper.hxx"
#include <editutil.hxx>
#include <formulacell.hxx>
#include <cellvalue.hxx>
#include <scopetools.hxx>
#include <docfunc.hxx>
#include <dbdocfun.hxx>
#include <tokenstringcontext.hxx>
#include <globalnames.hxx>
#include <dbdata.hxx>
#include <bcaslot.hxx>
#include <undomanager.hxx>
#include <sfx2/docfile.hxx>
#include <memory>
#include <string_view>
using ::std::cerr;
using ::std::endl;
class TestSharedFormula : public ScUcalcTestBase
{
};
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulas)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
ScAddress aPos(1, 9, 0); // B10
m_pDoc->SetString(aPos, u"=A10*2"_ustr); // Insert into B10.
const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("Expected to be a non-shared cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("Expected to be a non-shared cell.", !pFC->IsShared());
aPos.SetRow(10); // B11
m_pDoc->SetString(aPos, u"=A11*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=A9*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=A13*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This formula cell shouldn't be shared yet.", pFC);
CPPUNIT_ASSERT_MESSAGE("This formula cell shouldn't be shared yet.", !pFC->IsShared());
// Insert a formula to B12, and B9:B13 should be shared.
aPos.SetRow(11); // B12
m_pDoc->SetString(aPos, u"=A12*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=A15*2"_ustr);
aPos.SetRow(15); // B16
m_pDoc->SetString(aPos, u"=A16*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=A14*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=$A$1*4"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell shouldn't be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell shouldn't be shared.", !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);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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, u"=A17*2"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A18*2"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A19*2"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A20*2"_ustr);
pFC = m_pDoc->GetFormulaCell(aPos);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", 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(u"Test"_ustr));
CPPUNIT_ASSERT_EQUAL_MESSAGE("This should be a string value.", CELLTYPE_STRING, aCell.getType());
aCell.commit(*m_pDoc, aPos);
CPPUNIT_ASSERT_EQUAL(aCell.getSharedString()->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(u"Edit Text"_ustr);
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, u"=A2*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A3*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A4*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A5*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A6*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A7*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A8*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A9*10"_ustr);
aPos.IncRow();
m_pDoc->SetString(aPos, u"=A10*10"_ustr);
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,m_pDoc->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,m_pDoc->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 std::vector<std::vector<const char*>> aData = {
{ "5", "1", "=C:C/D:D" },
{ "4", "2", "=C:C/D:D" }
};
insertRangeData(m_pDoc, aPos, aData);
}
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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdate)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
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 std::vector<std::vector<const char*>> aData = {
{ "=A10" },
{ "=A11" },
{ "=A12" }
};
insertRangeData(m_pDoc, ScAddress(1,0,0), aData);
}
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=A11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A12"_ustr, m_pDoc->GetFormula(1,2,0));
const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", 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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=C11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A12"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", !pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", !pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", !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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=A11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A12"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", 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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=A13"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A14"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", !pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", 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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=A11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A12"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", 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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=C11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=A12"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", !pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B2 should be a non-shared formula cell.", !pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B3 should be a non-shared formula cell.", !pFC->IsShared());
// Insert cells over A12:B12 to shift to right.
m_pDoc->InsertCol(ScRange(0,11,0,1,11,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=A10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=C11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=C12"_ustr, m_pDoc->GetFormula(1,2,0));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", pFC);
CPPUNIT_ASSERT_MESSAGE("B1 should be a non-shared formula cell.", !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);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", 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));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B1", u"=C10"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B2", u"=C11"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula in B3", u"=C12"_ustr, m_pDoc->GetFormula(1,2,0));
// 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);
CPPUNIT_ASSERT_MESSAGE("This must be a shared formula cell.", pFC->IsShared());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, 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, u"Test"_ustr);
// 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 std::vector<std::vector<const char*>> aData = {
{ "=RC[-1]" },
{ "=RC[-1]" },
{ "=RC[-1]" }
};
// Set formulas in C2:C4 that reference B2:B4 individually.
insertRangeData(m_pDoc, ScAddress(2,1,0), 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 = m_xDocShell->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_EQUAL_MESSAGE("Wrong formula", u"=R[-1]C[-1]"_ustr, m_pDoc->GetFormula(2,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=R[-1]C[-1]"_ustr, m_pDoc->GetFormula(2,2,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=R[-1]C[-1]"_ustr, m_pDoc->GetFormula(2,3,0));
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_EQUAL_MESSAGE("Wrong formula", u"=RC[-1]"_ustr, m_pDoc->GetFormula(2,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=RC[-1]"_ustr, m_pDoc->GetFormula(2,2,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=RC[-1]"_ustr, m_pDoc->GetFormula(2,3,0));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, 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, u"Test"_ustr);
// 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 std::vector<std::vector<const char*>> aData = {
{ "=RC[-1]" },
{ "=RC[-1]" }
};
insertRangeData(m_pDoc, ScAddress(2,1,0), aData);
insertRangeData(m_pDoc, ScAddress(5,1,0), aData);
}
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 = m_xDocShell->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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateRange)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
// 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 std::vector<std::vector<const char*>> aData = {
{ "=SUM($A$3:$A$5)" },
{ "=SUM($A$3:$A$5)" },
{ "=SUM($A$3:$A$5)" }
};
insertRangeData(m_pDoc, ScAddress(1,2,0), aData);
}
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$3:$A$5)"_ustr, m_pDoc->GetFormula(1,2,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$3:$A$5)"_ustr, m_pDoc->GetFormula(1,3,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$3:$A$5)"_ustr, m_pDoc->GetFormula(1,4,0));
// B3:B5 should be shared.
const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,3,0));
CPPUNIT_ASSERT_MESSAGE("B4 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B4 should be shared.", pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,4,0));
CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B3 should be shared.", pFC->IsShared());
// Insert 2 rows at row 1.
m_pDoc->InsertRow(ScRange(0,0,0,m_pDoc->MaxCol(),1,0));
// B5:B7 should be shared.
pFC = m_pDoc->GetFormulaCell(ScAddress(1,4,0));
CPPUNIT_ASSERT_MESSAGE("B5 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B5 should be shared.", pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,5,0));
CPPUNIT_ASSERT_MESSAGE("B6 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B6 should be shared.", pFC->IsShared());
pFC = m_pDoc->GetFormulaCell(ScAddress(1,6,0));
CPPUNIT_ASSERT_MESSAGE("B7 should be shared.", pFC);
CPPUNIT_ASSERT_MESSAGE("B7 should be shared.", pFC->IsShared());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$5:$A$7)"_ustr, m_pDoc->GetFormula(1,4,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$5:$A$7)"_ustr, m_pDoc->GetFormula(1,5,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=SUM($A$5:$A$7)"_ustr, m_pDoc->GetFormula(1,6,0));
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();
}
};
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateRangeDeleteRow)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Formula"_ustr);
ScRange aWholeArea(0, 0, 0, 100, 100, 0); // Large enough for all references used in the test.
const std::vector<std::vector<const char*>> aData = {
{ "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);
// 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 = m_xDocShell->GetDocFunc();
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
rFunc.DeleteCells(ScRange(0,2,0,m_pDoc->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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateExternal)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Formula"_ustr);
// Launch an external document shell.
ScDocShellRef xExtDocSh = new ScDocShell;
SfxMedium* pMed = new SfxMedium(u"file:///extdata.fake"_ustr, StreamMode::STD_READWRITE);
xExtDocSh->DoLoad(pMed);
ScDocument& rExtDoc = xExtDocSh->GetDocument();
// Populate A1:A3.
rExtDoc.InsertTab(0, u"Data"_ustr);
rExtDoc.SetString(ScAddress(0,0,0), u"A"_ustr);
rExtDoc.SetString(ScAddress(0,1,0), u"B"_ustr);
rExtDoc.SetString(ScAddress(0,2,0), u"C"_ustr);
{
// Insert formula cells in A7:A10 of the host document, referencing A1:A3
// of the external document.
const std::vector<std::vector<const char*>> aData = {
{ "='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), aData);
}
// Check the formula results.
CPPUNIT_ASSERT_EQUAL(u"A"_ustr, m_pDoc->GetString(ScAddress(0,6,0)));
CPPUNIT_ASSERT_EQUAL(u"B"_ustr, m_pDoc->GetString(ScAddress(0,7,0)));
CPPUNIT_ASSERT_EQUAL(u"C"_ustr, m_pDoc->GetString(ScAddress(0,8,0)));
CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
// Check the formulas too.
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A1"_ustr, m_pDoc->GetFormula(0,6,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A2"_ustr, m_pDoc->GetFormula(0,7,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A3"_ustr, m_pDoc->GetFormula(0,8,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=COUNTA('file:///extdata.fake'#$Data.A1:A3)"_ustr, m_pDoc->GetFormula(0,9,0));
// Delete rows 1 and 2. This should not change the references in the formula cells below.
ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
rDocFunc.DeleteCells(ScRange(0,0,0,m_pDoc->MaxCol(),1,0), &aMark, DelCellCmd::CellsUp, true);
// Check the shifted formula cells now in A5:A8.
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A1"_ustr, m_pDoc->GetFormula(0,4,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A2"_ustr, m_pDoc->GetFormula(0,5,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A3"_ustr, m_pDoc->GetFormula(0,6,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=COUNTA('file:///extdata.fake'#$Data.A1:A3)"_ustr, m_pDoc->GetFormula(0,7,0));
// Undo and check the formulas again.
SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
CPPUNIT_ASSERT(pUndoMgr);
pUndoMgr->Undo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A1"_ustr, m_pDoc->GetFormula(0,6,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A2"_ustr, m_pDoc->GetFormula(0,7,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A3"_ustr, m_pDoc->GetFormula(0,8,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=COUNTA('file:///extdata.fake'#$Data.A1:A3)"_ustr, m_pDoc->GetFormula(0,9,0));
// Redo the row deletion and check the formulas again.
pUndoMgr->Redo();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A1"_ustr, m_pDoc->GetFormula(0,4,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A2"_ustr, m_pDoc->GetFormula(0,5,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"='file:///extdata.fake'#$Data.A3"_ustr, m_pDoc->GetFormula(0,6,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=COUNTA('file:///extdata.fake'#$Data.A1:A3)"_ustr, m_pDoc->GetFormula(0,7,0));
xExtDocSh->DoClose();
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, 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);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$A$5"_ustr, pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
}
// 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);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=$A$4"_ustr, pDoc->GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
}
// 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, u"Test"_ustr);
// 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 std::vector<std::vector<const char*>> aData = {
{ "=$A$4" },
{ "=$A$4" },
{ "=$A$4" },
{ "=$A$4" }
};
insertRangeData(m_pDoc, ScAddress(1,0,0), aData);
}
// Insert a new row at row 3.
ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
rFunc.InsertCells(ScRange(0,2,0,m_pDoc->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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasDeleteRows)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
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 std::vector<std::vector<const char*>> aData = {
{ "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), aData);
}
// 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);
CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", 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);
CPPUNIT_ASSERT_MESSAGE("1,10 must be a shared formula cell.", 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,m_pDoc->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);
CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", 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);
CPPUNIT_ASSERT_MESSAGE("1,8 must be a shared formula cell.", 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,m_pDoc->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);
CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", 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);
CPPUNIT_ASSERT_MESSAGE("1,7 must be a shared formula cell.", 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,m_pDoc->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);
CPPUNIT_ASSERT_MESSAGE("1,0 must be a shared formula cell.", 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);
CPPUNIT_ASSERT_MESSAGE("1,6 must be a shared formula cell.", pFC->IsShared());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedTopRow());
CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasDeleteColumns)
{
using namespace formula;
m_pDoc->InsertTab(0, u"Test"_ustr);
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
ScMarkData aMark(m_pDoc->GetSheetLimits());
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), u"=RC[-1]"_ustr);
CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,0,0)));
// Delete column B.
rFunc.DeleteCells(ScRange(1,0,0,1,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
CPPUNIT_ASSERT_EQUAL(u"#REF!"_ustr, 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);
CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), pCode->GetLen());
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(u"=#REF!1"_ustr, 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(u"=B1"_ustr, pFC->GetFormula(aCFCxt));
// Clear row 1 and move over to a formula group case.
clearRange(m_pDoc, ScRange(0,0,0,m_pDoc->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), u"=RC[-2]+RC[-1]"_ustr);
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,m_pDoc->MaxRow(),0), &aMark, DelCellCmd::CellsLeft, true);
for (SCROW i = 0; i <= 1; ++i)
{
ScAddress aPos(1,i,0);
CPPUNIT_ASSERT_EQUAL(u"#REF!"_ustr, m_pDoc->GetString(aPos));
}
pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0)); // B1
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(u"=A1+#REF!1"_ustr, pFC->GetFormula(aCFCxt));
pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0)); // B2
CPPUNIT_ASSERT(pFC);
CPPUNIT_ASSERT_EQUAL(u"=A2+#REF!2"_ustr, 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateMoveSheets)
{
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->InsertTab(1, u"Sheet2"_ustr);
m_pDoc->InsertTab(2, u"Sheet3"_ustr);
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), u"=Sheet2!RC"_ustr);
// 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(u"Sheet3"_ustr, aTabNames[0]);
CPPUNIT_ASSERT_EQUAL(u"Sheet1"_ustr, aTabNames[1]);
CPPUNIT_ASSERT_EQUAL(u"Sheet2"_ustr, 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)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula expression.", u"=Sheet2!RC"_ustr, m_pDoc->GetFormula(0,i,1));
}
// Insert a new sheet at the left end.
m_pDoc->InsertTab(0, u"Sheet4"_ustr);
// Check sheet names.
aTabNames = m_pDoc->GetAllTableNames();
CPPUNIT_ASSERT_MESSAGE("There should be at least 4 sheets.", aTabNames.size() >= 4);
CPPUNIT_ASSERT_EQUAL(u"Sheet4"_ustr, aTabNames[0]);
CPPUNIT_ASSERT_EQUAL(u"Sheet3"_ustr, aTabNames[1]);
CPPUNIT_ASSERT_EQUAL(u"Sheet1"_ustr, aTabNames[2]);
CPPUNIT_ASSERT_EQUAL(u"Sheet2"_ustr, 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)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula expression.", u"=Sheet2!RC"_ustr, m_pDoc->GetFormula(0,i,2));
}
// 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(u"Sheet3"_ustr, aTabNames[0]);
CPPUNIT_ASSERT_EQUAL(u"Sheet1"_ustr, aTabNames[1]);
CPPUNIT_ASSERT_EQUAL(u"Sheet2"_ustr, 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)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula expression.", u"=Sheet2!RC"_ustr, m_pDoc->GetFormula(0,i,1));
}
m_pDoc->DeleteTab(2);
m_pDoc->DeleteTab(1);
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateCopySheets)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // make sure auto calc is on.
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->InsertTab(1, u"Sheet2"_ustr);
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), u"=$Sheet2.A1"_ustr);
m_pDoc->SetString(ScAddress(0,1,0), u"=$Sheet2.A2"_ustr);
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);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=$Sheet2.A1"_ustr, m_pDoc->GetFormula(0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=$Sheet2.A2"_ustr, m_pDoc->GetFormula(0,1,0));
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasRefUpdateDeleteSheets)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // make sure auto calc is on.
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->InsertTab(1, u"Sheet2"_ustr);
// 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), u"=Sheet2.B2"_ustr);
m_pDoc->SetString(ScAddress(0,1,0), u"=Sheet2.B3"_ustr);
m_pDoc->SetString(ScAddress(0,2,0), u"=Sheet2.B4"_ustr);
// 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)));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B2"_ustr, m_pDoc->GetFormula(0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B3"_ustr, m_pDoc->GetFormula(0,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B4"_ustr, m_pDoc->GetFormula(0,2,0));
// Delete Sheet2.
ScDocFunc& rFunc = m_xDocShell->GetDocFunc();
rFunc.DeleteTable(1, true);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=#REF!.B2"_ustr, m_pDoc->GetFormula(0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=#REF!.B3"_ustr, m_pDoc->GetFormula(0,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=#REF!.B4"_ustr, m_pDoc->GetFormula(0,2,0));
// 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();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B2"_ustr, m_pDoc->GetFormula(0,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B3"_ustr, m_pDoc->GetFormula(0,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula", u"=Sheet2.B4"_ustr, m_pDoc->GetFormula(0,2,0));
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulasCopyPaste)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
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, u"=RC[-1]"_ustr);
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(*m_xDocShell, 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaInsertColumn)
{
m_pDoc->InsertTab(0, u"Test"_ustr);
// Set shared formula group over H2:H3.
m_pDoc->SetString(ScAddress(7,1,0), u"=G3*B3"_ustr);
m_pDoc->SetString(ScAddress(7,2,0), u"=G4*B4"_ustr);
// Insert a single column at Column F. This used to crash before fdo#74041.
m_pDoc->InsertCol(ScRange(5,0,0,5,m_pDoc->MaxRow(),0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=H3*B3"_ustr, m_pDoc->GetFormula(8,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=H4*B4"_ustr, m_pDoc->GetFormula(8,2,0));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, 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, u"Test"_ustr);
// 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), u"=RC[-1]"_ustr);
m_pDoc->SetString(ScAddress(1,1,0), u"=RC[-1]"_ustr);
m_pDoc->SetString(ScAddress(1,2,0), u"=RC[-1]"_ustr);
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 = m_xDocShell->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 { 0, 1, 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,m_pDoc->MaxCol(),m_pDoc->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), u"=RC[-1]"_ustr);
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaUpdateOnNamedRangeChange)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
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), u"=SUM(MyRange)"_ustr);
m_pDoc->SetString(ScAddress(1,1,0), u"=SUM(MyRange)"_ustr);
m_pDoc->SetString(ScAddress(1,2,0), u"=SUM(MyRange)"_ustr);
// Set single formula with no named range to B5.
m_pDoc->SetString(ScAddress(1,4,0), u"=ROW()"_ustr);
// Set shared formula with no named range to B7:B8.
m_pDoc->SetString(ScAddress(1,6,0), u"=ROW()"_ustr);
m_pDoc->SetString(ScAddress(1,7,0), u"=ROW()"_ustr);
// 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), u"=AVERAGE(MyRange)"_ustr);
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 = m_xDocShell->GetDocFunc();
typedef std::map<OUString, 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.
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(MyRange)"_ustr, m_pDoc->GetFormula(1,0,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(MyRange)"_ustr, m_pDoc->GetFormula(1,1,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=SUM(MyRange)"_ustr, m_pDoc->GetFormula(1,2,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=ROW()"_ustr, m_pDoc->GetFormula(1,4,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=ROW()"_ustr, m_pDoc->GetFormula(1,6,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=ROW()"_ustr, m_pDoc->GetFormula(1,7,0));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula!", u"=AVERAGE(MyRange)"_ustr, m_pDoc->GetFormula(2,0,0));
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaUpdateOnDBChange)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"RangeTest"_ustr);
// 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(u"MyRange"_ustr, 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), u"=SUM(MyRange)"_ustr);
// 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(*m_xDocShell);
// Change the range referenced by MyRange to A1:A4.
ScDBCollection aNewDBs(*m_pDoc);
std::unique_ptr<ScDBData> pNewData(new ScDBData(u"MyRange"_ustr, 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaAbsCellListener)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
const std::vector<std::vector<const char*>> aData = {
{ "=$A$1" },
{ "=$A$1" },
{ "=$A$1" }
};
insertRangeData(m_pDoc, ScAddress(1,0,0), aData);
// 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;
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaUnshareAreaListeners)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
const std::vector<std::vector<const char*>> aData = {
{ "=SUM(B1:B2)", "1" },
{ "=SUM(B2:B3)", "2" },
{ "=SUM(B3:B4)", "4" },
{ nullptr, "8" }
};
insertRangeData(m_pDoc, ScAddress(0,0,0), aData);
// 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 std::vector<std::vector<const char*>> aData2 = {
{ "=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, aData2);
// 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, u"=B4"_ustr);
ScDocFunc& rDocFunc = m_xDocShell->GetDocFunc();
rDocFunc.SetFormulaCell( aPos, pCell, false);
}
break;
case 1:
m_pDoc->SetString( aPos, u"=B4"_ustr); // set formula
break;
case 2:
m_pDoc->SetString( aPos, u"x"_ustr); // set string
break;
case 3:
m_pDoc->SetString( aPos, u"4096"_ustr); // 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, u"=B4"_ustr);
std::vector<ScFormulaCell*> aCells { 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 std::vector<std::vector<const char*>> aData2 = {
{ "=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, aData2);
// 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, u"=SUM(B1:C1)"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=SUM(B2:C2)"_ustr);
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, aData2);
// Add formulas in A1:A2, keep A3:A7
aPos = ScAddress(0,0,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B1+C1"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B2+C2"_ustr);
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, aData2);
// 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, u"=B2+C2"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B3+C3"_ustr);
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, aData2);
// Overwrite A3:A4, keep A5:A7
aPos = ScAddress(0,2,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B3+C3"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B4+C4"_ustr);
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, aData2);
// Keep A3, overwrite A4:A5, keep A6:A7
aPos = ScAddress(0,3,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B4+C4"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B5+C5"_ustr);
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, aData2);
// Keep A3:A4, overwrite A5:A6, keep A7
aPos = ScAddress(0,4,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B5+C5"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B6+C6"_ustr);
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, aData2);
// Keep A3:A5, overwrite A6:A7
aPos = ScAddress(0,5,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B6+C6"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B7+C7"_ustr);
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, aData2);
// Keep A3:A6, overwrite A7, add A8
aPos = ScAddress(0,6,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B7+C7"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B8+C8"_ustr);
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, aData2);
// Keep A3:A7, add A8:A9
aPos = ScAddress(0,7,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B8+C8"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=B9+C9"_ustr);
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, aData2);
// 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, u"=SUM(B8:C8)"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=SUM(B9:C9)"_ustr);
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, aData2);
// Overwrite grouping formulas in A4:A5
aPos = ScAddress(0,3,0);
std::vector<ScFormulaCell*>().swap( aCells);
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=SUM(B4:C4)"_ustr);
aCells.push_back(pCell);
aPos.IncRow();
pCell = new ScFormulaCell( *m_pDoc, aPos, u"=SUM(B5:C5)"_ustr);
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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaListenerDeleteArea)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test0"_ustr);
m_pDoc->InsertTab(1, u"Test1"_ustr);
const std::vector<std::vector<const char*>> aData0 = {
{ "", "", "=Test1.C1" },
{ "", "", "=Test1.C2" }
};
const std::vector<std::vector<const char*>> aData1 = {
{ "=Test0.A1", "=Test0.B1", "=SUM(A1:B1)" },
{ "=Test0.A2", "=Test0.B2", "=SUM(A2:B2)" },
};
insertRangeData(m_pDoc, ScAddress(0,0,0), aData0);
insertRangeData(m_pDoc, ScAddress(0,0,1), aData1);
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaUpdateOnReplacement)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
const std::vector<std::vector<const char*>> aData = {
{ "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), aData);
// 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(m_pDoc->GetSheetLimits());
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(m_xDocShell.get(), 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), u"0"_ustr);
// 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);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaDeleteTopCell)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
const std::vector<std::vector<const char*>> aData = {
{ "=SUM(B$1:B$2)", "1" },
{ "=SUM(B$1:B$2)", "2" }
};
insertRangeData( m_pDoc, ScAddress(0,0,0), aData);
// 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(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_xDocShell->GetDocFunc().DeleteCell( ScAddress(0,0,0), aMark, InsertDeleteFlags::CONTENTS, false, /*bApi=*/ true);
// 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), u"4"_ustr);
// Check result A2.
CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue( ScAddress(0,1,0)));
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaCutCopyMoveIntoRef)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
// tdf#123714 case 1
{
m_pDoc->InsertTab(0, u"Test"_ustr);
// 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(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
// Set up clip document.
ScDocument aClipDoc(SCDOCMODE_CLIP);
aClipDoc.ResetClip(m_pDoc, &aMark);
// Cut C1:C2 to clipboard.
cutToClip(*m_xDocShell, 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, u"Test"_ustr);
// 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(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
// Set up clip document.
ScDocument aClipDoc(SCDOCMODE_CLIP);
aClipDoc.ResetClip(m_pDoc, &aMark);
// Cut B1:B2 to clipboard.
cutToClip(*m_xDocShell, 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
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaCutCopyMoveWithRef)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
// 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(m_pDoc->GetSheetLimits());
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(*m_xDocShell, 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 std::u16string_view sForm[] = { u"=SUM(A1:B1)", u"=SUM(A2:B2)", u"=SUM(A1:B1)", u"=SUM(A4:B4)" };
for (SCROW i=0; i < 4; ++i)
{
OUString aFormula = m_pDoc->GetFormula( 2,i,0 );
CPPUNIT_ASSERT_EQUAL( OUString(sForm[i]), aFormula);
}
m_pDoc->DeleteTab(0);
}
// tdf#120013
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaCutCopyMoveWithinRun)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
// 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(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
// Set up clip document.
ScDocument aClipDoc(SCDOCMODE_CLIP);
aClipDoc.ResetClip(m_pDoc, &aMark);
// Cut A8:D8 to clipboard.
cutToClip(*m_xDocShell, 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
CPPUNIT_TEST_FIXTURE(TestSharedFormula, testSharedFormulaInsertShift)
{
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
m_pDoc->InsertTab(0, u"Test"_ustr);
// 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);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */