sc/inc/column.hxx | 27 sc/inc/sharedformula.hxx | 13 sc/qa/unit/ucalc.hxx | 8 sc/qa/unit/ucalc_sharedformula.cxx | 773 +++++++++++++++++++++++++++ sc/source/core/data/column.cxx | 4 sc/source/core/data/column3.cxx | 324 +++++++++-- sc/source/core/data/column4.cxx | 27 sc/source/core/data/table2.cxx | 2 sc/source/core/tool/sharedformula.cxx | 46 + sd/qa/unit/tiledrendering/tiledrendering.cxx | 2 10 files changed, 1151 insertions(+), 75 deletions(-)
New commits: commit 23b03b5e21d936d1fdb2096c1c57b932a1c6f9e3 Author: Andras Timar <[email protected]> AuthorDate: Wed Apr 24 22:43:49 2019 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Apr 24 22:48:15 2019 +0200 disable unreliable SdTiledRenderingTest::testPasteTextOnSlide Change-Id: I5ff8d4f1a9191aec082cacafb571e3171f464550 diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx index 04d2326cfd96..b2f6a59f43cd 100644 --- a/sd/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx @@ -2081,6 +2081,7 @@ void SdTiledRenderingTest::testTdf115783() void SdTiledRenderingTest::testPasteTextOnSlide() { +#if 0 // Load the document. comphelper::LibreOfficeKit::setActive(); SdXImpressDocument* pXImpressDocument = createDoc("paste_text_onslide.odp"); @@ -2191,6 +2192,7 @@ void SdTiledRenderingTest::testPasteTextOnSlide() CPPUNIT_ASSERT_DOUBLES_EQUAL(static_cast<long>(7430), aPos.getY(), 100); comphelper::LibreOfficeKit::setActive(false); +#endif } void SdTiledRenderingTest::testTdf115873() commit 7a4657825a258b2c83cbb23dabaa29937bb0bdbc Author: Eike Rathke <[email protected]> AuthorDate: Tue Oct 16 11:22:20 2018 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Apr 24 22:48:14 2019 +0200 Unit test deleting shared formula group top cell, tdf#119623 Change-Id: I9a497b9d9ae09c8764f62549a60c8a2a952bc9e9 Reviewed-on: https://gerrit.libreoffice.org/61822 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx index f2304ccb823e..cd1f6b06914b 100644 --- a/sc/qa/unit/ucalc.hxx +++ b/sc/qa/unit/ucalc.hxx @@ -398,6 +398,7 @@ public: void testSharedFormulaUnshareAreaListeners(); void testSharedFormulaListenerDeleteArea(); void testSharedFormulaUpdateOnReplacement(); + void testSharedFormulaDeleteTopCell(); void testSharedFormulaCutCopyMoveIntoRef(); void testSharedFormulaCutCopyMoveWithRef(); void testSharedFormulaCutCopyMoveWithinRun(); @@ -736,6 +737,7 @@ public: CPPUNIT_TEST(testSharedFormulaUnshareAreaListeners); CPPUNIT_TEST(testSharedFormulaListenerDeleteArea); CPPUNIT_TEST(testSharedFormulaUpdateOnReplacement); + CPPUNIT_TEST(testSharedFormulaDeleteTopCell); CPPUNIT_TEST(testSharedFormulaCutCopyMoveIntoRef); CPPUNIT_TEST(testSharedFormulaCutCopyMoveWithRef); CPPUNIT_TEST(testSharedFormulaCutCopyMoveWithinRun); diff --git a/sc/qa/unit/ucalc_sharedformula.cxx b/sc/qa/unit/ucalc_sharedformula.cxx index e2205ba98358..8acbb06a1c4d 100644 --- a/sc/qa/unit/ucalc_sharedformula.cxx +++ b/sc/qa/unit/ucalc_sharedformula.cxx @@ -2344,6 +2344,48 @@ void Test::testSharedFormulaUpdateOnReplacement() m_pDoc->DeleteTab(0); } +void Test::testSharedFormulaDeleteTopCell() +{ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. + + m_pDoc->InsertTab(0, "Test"); + + const char* pData[][2] = { + { "=SUM(B$1:B$2)", "1" }, + { "=SUM(B$1:B$2)", "2" } + }; + + insertRangeData( m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData)); + + // Check that A1:A2 is a formula group. + const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( ScAddress(0,0,0)); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check results A1:A2. + CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,0,0))); + CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,1,0))); + + // Delete cell A1. + ScMarkData aMark; + aMark.SelectOneTable(0); + getDocShell().GetDocFunc().DeleteCell( ScAddress(0,0,0), aMark, InsertDeleteFlags::CONTENTS, false); + // Check it's gone. + CPPUNIT_ASSERT(!m_pDoc->GetFormulaCell( ScAddress(0,0,0))); + + // Check result A2. + CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue( ScAddress(0,1,0))); + + // Replace B1 with 4. + m_pDoc->SetString( ScAddress(1,0,0), "4"); + + // Check result A2. + CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue( ScAddress(0,1,0))); + + m_pDoc->DeleteTab(0); +} + void Test::testSharedFormulaCutCopyMoveIntoRef() { commit c444aab457f1383cb94f9ab7b39ae87d37a4c817 Author: Eike Rathke <[email protected]> AuthorDate: Mon Oct 15 23:10:07 2018 +0200 Commit: Andras Timar <[email protected]> CommitDate: Wed Apr 24 22:48:14 2019 +0200 Resolves: tdf#119623 record group positions also for top cells So listeners can be re-established for remaining grouped cells if top cell is deleted (which ends listening and probably was the cause to exclude them here). Change-Id: Ic91b74c65013452d56b5cfbc132722c4314743c8 Reviewed-on: https://gerrit.libreoffice.org/61808 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx index 520bd815873c..4739eace586b 100644 --- a/sc/source/core/data/column4.cxx +++ b/sc/source/core/data/column4.cxx @@ -1522,10 +1522,12 @@ void ScColumn::EndListeningIntersectedGroups( { ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second); ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); - if (xGroup && !pFC->IsSharedTop()) + if (xGroup) { - // End listening. - pFC->EndListeningTo(rCxt); + if (!pFC->IsSharedTop()) + // End listening. + pFC->EndListeningTo(rCxt); + if (pGroupPos) // Record the position of the top cell of the group. pGroupPos->push_back(xGroup->mpTopCell->aPos); @@ -1538,10 +1540,12 @@ void ScColumn::EndListeningIntersectedGroups( { ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second); ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); - if (xGroup && !pFC->IsSharedTop()) + if (xGroup) { - // End listening. - pFC->EndListeningTo(rCxt); + if (!pFC->IsSharedTop()) + // End listening. + pFC->EndListeningTo(rCxt); + if (pGroupPos) { // Record the position of the bottom cell of the group. commit 46968a28c47b12ce4557fda5dd96641f3e0a4fa6 Author: Eike Rathke <[email protected]> AuthorDate: Sun Mar 17 19:58:12 2019 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Apr 24 22:48:14 2019 +0200 Unit tests for tdf#121002 tdf#120013 tdf#123714 tdf#123736 This is a combination of 4 commits. Add unit tests for tdf#123736 and related unshared/regrouped listening Reviewed-on: https://gerrit.libreoffice.org/69372 Tested-by: Jenkins Reviewed-by: Eike Rathke <[email protected]> (cherry picked from commit 8925bd2efca8dd07a3ac044ecbc6505bb7bfa0a6) Unit test for cut copy move into shared formula group reference, tdf#123714 Reviewed-on: https://gerrit.libreoffice.org/69453 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit f81d532648ccbbd08d3cb568eadc9f402dac67ad) Unit test for cut copy move with a group reference adjusting along, tdf#121002 Reviewed-on: https://gerrit.libreoffice.org/69532 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 92657eed57548511f26f0c22f1bbc47d554c8b5b) bcf03f5068630c18eeecf2625cdff7ec1704d292 4f3731e2236f1b8be97a6f5d21e32e678610fecb Unit test for cut copy move intersecting a formula group run, tdf#120013 Reviewed-on: https://gerrit.libreoffice.org/69568 Tested-by: Jenkins Reviewed-by: Eike Rathke <[email protected]> (cherry picked from commit 6280b5c1c62ad40b5b9780a93c7cbee9ca0260f8) ce7e1e42dba7bfbc2db6a60e4457c3610430b87e Change-Id: If48e6d75288b42b47c90f3104006b7c52982c9f6 Reviewed-on: https://gerrit.libreoffice.org/69555 Tested-by: Jenkins Reviewed-by: Christian Lohmaier <[email protected]> diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx index 74729debe9ea..f2304ccb823e 100644 --- a/sc/qa/unit/ucalc.hxx +++ b/sc/qa/unit/ucalc.hxx @@ -398,6 +398,9 @@ public: void testSharedFormulaUnshareAreaListeners(); void testSharedFormulaListenerDeleteArea(); void testSharedFormulaUpdateOnReplacement(); + void testSharedFormulaCutCopyMoveIntoRef(); + void testSharedFormulaCutCopyMoveWithRef(); + void testSharedFormulaCutCopyMoveWithinRun(); void testFormulaPosition(); void testFormulaWizardSubformula(); @@ -733,6 +736,9 @@ public: CPPUNIT_TEST(testSharedFormulaUnshareAreaListeners); CPPUNIT_TEST(testSharedFormulaListenerDeleteArea); CPPUNIT_TEST(testSharedFormulaUpdateOnReplacement); + CPPUNIT_TEST(testSharedFormulaCutCopyMoveIntoRef); + CPPUNIT_TEST(testSharedFormulaCutCopyMoveWithRef); + CPPUNIT_TEST(testSharedFormulaCutCopyMoveWithinRun); CPPUNIT_TEST(testFormulaPosition); CPPUNIT_TEST(testFormulaWizardSubformula); CPPUNIT_TEST(testMixData); diff --git a/sc/qa/unit/ucalc_sharedformula.cxx b/sc/qa/unit/ucalc_sharedformula.cxx index b0de3278653c..e2205ba98358 100644 --- a/sc/qa/unit/ucalc_sharedformula.cxx +++ b/sc/qa/unit/ucalc_sharedformula.cxx @@ -1645,6 +1645,21 @@ void Test::testSharedFormulaAbsCellListener() m_pDoc->DeleteTab(0); } +static double checkNewValuesNotification( ScDocument* pDoc, const ScAddress& rOrgPos ) +{ + ScAddress aPos(rOrgPos); + aPos.IncCol(); + pDoc->SetValues( aPos, {1024.0, 2048.0, 4096.0, 8192.0, 16384.0}); + aPos = rOrgPos; + double fVal = 0.0; + for (SCROW i=0; i < 5; ++i) + { + fVal += pDoc->GetValue(aPos); + aPos.IncRow(); + } + return fVal; +} + void Test::testSharedFormulaUnshareAreaListeners() { sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. @@ -1673,6 +1688,460 @@ void Test::testSharedFormulaUnshareAreaListeners() CPPUNIT_ASSERT_EQUAL(17.0, m_pDoc->GetValue(ScAddress(0,0,0))); CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(ScAddress(0,2,0))); + clearRange(m_pDoc, ScRange( 0,0,0, 1,3,0)); + + for (int nRun = 0; nRun < 7; ++nRun) + { + // Data in A2:C6 + const ScAddress aOrgPos(0,1,0); + const char* pData2[][3] = { + { "=SUM(B2:C2)", "1", "2" }, + { "=SUM(B3:C3)", "4", "8" }, + { "=SUM(B4:C4)", "16", "32" }, + { "=SUM(B5:C5)", "64", "128" }, + { "=SUM(B6:C6)", "256", "512" }, + }; + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Check that A2:A6 is a formula group. + pFC = m_pDoc->GetFormulaCell(aOrgPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aOrgPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength()); + + // Overwrite and thus unshare formula in A3. + // Check different code paths with different methods. + ScAddress aPos(aOrgPos); + aPos.IncRow(2); + switch (nRun) + { + case 0: + // Directly set a different formula cell, which bypasses + // ScDocument::SetString(), mimicking formula input in view. + { + ScFormulaCell* pCell = new ScFormulaCell( m_pDoc, aPos, "=B4"); + ScDocFunc& rDocFunc = getDocShell().GetDocFunc(); + rDocFunc.SetFormulaCell( aPos, pCell, false); + } + break; + case 1: + m_pDoc->SetString( aPos, "=B4"); // set formula + break; + case 2: + m_pDoc->SetString( aPos, "x"); // set string + break; + case 3: + m_pDoc->SetString( aPos, "4096"); // set number/numeric + break; + case 4: + m_pDoc->SetValue( aPos, 4096.0); // set numeric + break; + case 5: + m_pDoc->SetValues( aPos, {4096.0}); // set numeric vector + break; + case 6: + // Set formula cell vector. + { + ScFormulaCell* pCell = new ScFormulaCell( m_pDoc, aPos, "=B4"); + std::vector<ScFormulaCell*> aCells; + aCells.push_back(pCell); + m_pDoc->SetFormulaCells( aPos, aCells); + } + break; + } + + // Check that A2:A3 and A5:A6 are two formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(3); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check that listeners were set up and formulas are updated when B2:B6 + // get new values input (tdf#123736). + aPos = aOrgPos; + aPos.IncCol(); + m_pDoc->SetValues( aPos, {1024.0, 2048.0, 4096.0, 8192.0, 16384.0}); + + aPos = aOrgPos; + CPPUNIT_ASSERT_EQUAL(1026.0, m_pDoc->GetValue(aPos)); + aPos.IncRow(); + CPPUNIT_ASSERT_EQUAL(2056.0, m_pDoc->GetValue(aPos)); + aPos.IncRow(); + if (nRun != 2) // if not string + CPPUNIT_ASSERT_EQUAL(4096.0, m_pDoc->GetValue(aPos)); + aPos.IncRow(); + CPPUNIT_ASSERT_EQUAL(8320.0, m_pDoc->GetValue(aPos)); + aPos.IncRow(); + CPPUNIT_ASSERT_EQUAL(16896.0, m_pDoc->GetValue(aPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,5,0)); + } + + // Check detach/regroup combinations of overlapping when setting formula + // cell vectors. + { + // Fixed data in A3:C7, modified formula range A1:A9 + const ScAddress aOrgPos(0,2,0); + ScAddress aPos( ScAddress::UNINITIALIZED); + ScFormulaCell* pCell; + std::vector<ScFormulaCell*> aCells; + const char* pData2[][3] = { + { "=SUM(B3:C3)", "1", "2" }, + { "=SUM(B4:C4)", "4", "8" }, + { "=SUM(B5:C5)", "16", "32" }, + { "=SUM(B6:C6)", "64", "128" }, + { "=SUM(B7:C7)", "256", "512" }, + }; + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Add grouping formulas in A1:A2, keep A3:A7 + aPos = ScAddress(0,0,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B1:C1)"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B2:C2)"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check it is one formula group. + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(7), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Add formulas in A1:A2, keep A3:A7 + aPos = ScAddress(0,0,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B1+C1"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B2+C2"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Add formula in A2, overwrite A3, keep A4:A7 + aPos = ScAddress(0,1,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B2+C2"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B3+C3"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A2", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A4", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(4), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Overwrite A3:A4, keep A5:A7 + aPos = ScAddress(0,2,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B3+C3"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B4+C4"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(3), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3, overwrite A4:A5, keep A6:A7 + aPos = ScAddress(0,3,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B4+C4"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B5+C5"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", !pFC->IsSharedTop()); + aPos.IncRow(1); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A4", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A6", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3:A4, overwrite A5:A6, keep A7 + aPos = ScAddress(0,4,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B5+C5"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B6+C6"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A5", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + aPos.IncRow(2); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A7", !pFC->IsSharedTop()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3:A5, overwrite A6:A7 + aPos = ScAddress(0,5,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B6+C6"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B7+C7"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(3), pFC->GetSharedLength()); + aPos.IncRow(3); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A6", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3:A6, overwrite A7, add A8 + aPos = ScAddress(0,6,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B7+C7"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B8+C8"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(4), pFC->GetSharedLength()); + aPos.IncRow(4); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A7", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3:A7, add A8:A9 + aPos = ScAddress(0,7,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B8+C8"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=B9+C9"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check formula groups. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A3", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength()); + aPos.IncRow(5); + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A7", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(2), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Keep A3:A7, add grouping formulas in A8:A9 + aPos = ScAddress(0,7,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B8:C8)"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B9:C9)"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check it is one formula group. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(7), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + + insertRangeData(m_pDoc, aOrgPos, pData2, SAL_N_ELEMENTS(pData2)); + + // Overwrite grouping formulas in A4:A5 + aPos = ScAddress(0,3,0); + std::vector<ScFormulaCell*>().swap( aCells); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B4:C4)"); + aCells.push_back(pCell); + aPos.IncRow(); + pCell = new ScFormulaCell( m_pDoc, aPos, "=SUM(B5:C5)"); + aCells.push_back(pCell); + aPos.IncRow(-1); + m_pDoc->SetFormulaCells( aPos, aCells); + + // Check it is one formula group. + aPos = aOrgPos; + pFC = m_pDoc->GetFormulaCell(aPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_MESSAGE("A1", pFC->IsSharedTop()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared top row.", aPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shared length.", static_cast<SCROW>(5), pFC->GetSharedLength()); + + // Check notification of setting new values. + CPPUNIT_ASSERT_EQUAL(32426.0, checkNewValuesNotification( m_pDoc, aOrgPos)); + + clearRange(m_pDoc, ScRange( 0,0,0, 2,8,0)); + } + m_pDoc->DeleteTab(0); } @@ -1876,4 +2345,266 @@ void Test::testSharedFormulaUpdateOnReplacement() } +void Test::testSharedFormulaCutCopyMoveIntoRef() +{ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. + + // tdf#123714 case 1 + { + m_pDoc->InsertTab(0, "Test"); + + // Data in A1:C3 + std::vector<std::vector<const char*>> aData = { + { "=B1", "", "1" }, + { "=B2", "", "1" }, + { "=B3", "", "" } + }; + const ScAddress aOrgPos(0,0,0); + insertRangeData( m_pDoc, aOrgPos, aData); + + ScMarkData aMark; + aMark.SelectOneTable(0); + + // Set up clip document. + ScDocument aClipDoc(SCDOCMODE_CLIP); + aClipDoc.ResetClip(m_pDoc, &aMark); + // Cut C1:C2 to clipboard. + cutToClip( getDocShell(), ScRange(2,0,0, 2,1,0), &aClipDoc, false); + + // Paste to B1:B2 + ScRange aPasteRange(1,0,0, 1,1,0); + aMark.SetMarkArea(aPasteRange); + m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc); + + // Check data in A1:A2 after Paste. + ScAddress aPos(aOrgPos); + CPPUNIT_ASSERT_EQUAL_MESSAGE("A1", 1.0, m_pDoc->GetValue(aPos)); + aPos.IncRow(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("A2", 1.0, m_pDoc->GetValue(aPos)); + + m_pDoc->DeleteTab(0); + } + + // tdf#123714 case 2 + { + m_pDoc->InsertTab(0, "Test"); + + // Data in A1:C3 + std::vector<std::vector<const char*>> aData = { + { "1", "2", "=SUM(A1:B1)" }, + { "4", "8", "=SUM(A2:B2)" }, + { "16", "32", "=SUM(A3:B3)" }, + { "64", "128", "=SUM(A4:B4)" }, + }; + const ScAddress aOrgPos(0,0,0); + insertRangeData( m_pDoc, aOrgPos, aData); + + ScAddress aPos; + // Check results in C1:C4 + const double fVec0[] = { 3.0, 12.0, 48.0, 192.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec0[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + ScMarkData aMark; + aMark.SelectOneTable(0); + + // Set up clip document. + ScDocument aClipDoc(SCDOCMODE_CLIP); + aClipDoc.ResetClip(m_pDoc, &aMark); + // Cut B1:B2 to clipboard. + cutToClip( getDocShell(), ScRange(1,0,0, 1,1,0), &aClipDoc, false); + + // Check results in C1:C4 after Cut. + const double fVec1[] = { 1.0, 4.0, 48.0, 192.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec1[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Paste to B3:B4 + ScRange aPasteRange(1,2,0, 1,3,0); + aMark.SetMarkArea(aPasteRange); + m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc); + + // Check results in C1:C4 after Paste. + const double fVec2[] = { 1.0, 4.0, 18.0, 72.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec2[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Paste to B1:B2 + aPasteRange = ScRange(1,0,0, 1,1,0); + aMark.SetMarkArea(aPasteRange); + m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc); + + // Check results in C1:C4 after Paste. + const double fVec3[] = { 3.0, 12.0, 18.0, 72.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec3[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + m_pDoc->DeleteTab(0); + } +} + +// tdf#121002 +void Test::testSharedFormulaCutCopyMoveWithRef() +{ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. + + m_pDoc->InsertTab(0, "Test"); + + // Data in A1:C4 + std::vector<std::vector<const char*>> aData = { + { "", "", "=SUM(A1:B1)" }, + { "", "", "=SUM(A2:B2)" }, + { "1", "2", "=SUM(A3:B3)" }, + { "4", "8", "=SUM(A4:B4)" } + }; + const ScAddress aOrgPos(0,0,0); + insertRangeData( m_pDoc, aOrgPos, aData); + + ScMarkData aMark; + aMark.SelectOneTable(0); + + ScAddress aPos( ScAddress::UNINITIALIZED); + + // Check results in C1:C4 + const double fVec0[] = { 0.0, 0.0, 3.0, 12.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec0[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Set up clip document. + ScDocument aClipDoc(SCDOCMODE_CLIP); + aClipDoc.ResetClip(m_pDoc, &aMark); + // Cut A3:B3 to clipboard. + cutToClip( getDocShell(), ScRange(0,2,0, 1,2,0), &aClipDoc, false); + + // Check results in C1:C4 after Cut. + const double fVec1[] = { 0.0, 0.0, 0.0, 12.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec1[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Paste to A1:B1 + ScRange aPasteRange(0,0,0, 1,0,0); + aMark.SetMarkArea(aPasteRange); + m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc); + + // Check results in C1:C4 after Paste. + const double fVec2[] = { 3.0, 0.0, 3.0, 12.0 }; + aPos = ScAddress(2,0,0); + for (SCROW i=0; i < 4; ++i) + { + CPPUNIT_ASSERT_EQUAL( fVec2[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Check formulas in C1:C4 after Paste. + const OUStringLiteral sForm[] = { "=SUM(A1:B1)", "=SUM(A2:B2)", "=SUM(A1:B1)", "=SUM(A4:B4)" }; + for (SCROW i=0; i < 4; ++i) + { + OUString aFormula; + m_pDoc->GetFormula( 2,i,0, aFormula); + CPPUNIT_ASSERT_EQUAL( OUString(sForm[i]), aFormula); + } + + m_pDoc->DeleteTab(0); +} + +// tdf#120013 +void Test::testSharedFormulaCutCopyMoveWithinRun() +{ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc. + + m_pDoc->InsertTab(0, "Test"); + + // Data in C3:E9 + const std::vector<std::vector<const char*>> aData = { + { "2200", "", "=SUM(C$3:C3)-SUM(D$3:D3)" }, + { "", "", "=SUM(C$3:C4)-SUM(D$3:D4)" }, + { "", "1900", "=SUM(C$3:C5)-SUM(D$3:D5)" }, + { "", "", "=SUM(C$3:C6)-SUM(D$3:D6)" }, + { "1600", "", "=SUM(C$3:C7)-SUM(D$3:D7)" }, + { "", "1000", "=SUM(C$3:C8)-SUM(D$3:D8)" }, + { "", "", "=SUM(C$3:C9)-SUM(D$3:D9)" } + }; + const ScAddress aOrgPos(2,2,0); + insertRangeData( m_pDoc, aOrgPos, aData); + + // Check that E3:E9 is a formula group. + const ScAddress aFormulaPos(4,2,0); + const ScFormulaCell* pFC = m_pDoc->GetFormulaCell( aFormulaPos); + CPPUNIT_ASSERT(pFC); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula top row.", aFormulaPos.Row(), pFC->GetSharedTopRow()); + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Shared formula length.", static_cast<SCROW>(7), pFC->GetSharedLength()); + + ScAddress aPos( ScAddress::UNINITIALIZED); + + // Check results in E3:E9 + const double fVec0[] = { 2200.0, 2200.0, 300.0, 300.0, 1900.0, 900.0, 900.0 }; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec0), aData.size()); + aPos = aFormulaPos; + for (size_t i=0; i < SAL_N_ELEMENTS(fVec0); ++i) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9", fVec0[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + ScMarkData aMark; + aMark.SelectOneTable(0); + + // Set up clip document. + ScDocument aClipDoc(SCDOCMODE_CLIP); + aClipDoc.ResetClip(m_pDoc, &aMark); + // Cut A8:D8 to clipboard. + cutToClip( getDocShell(), ScRange(0,7,0, 3,7,0), &aClipDoc, false); + + // Check results in E3:E9 after Cut. + const double fVec1[] = { 2200.0, 2200.0, 300.0, 300.0, 1900.0, 1900.0, 1900.0 }; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec1), aData.size()); + aPos = aFormulaPos; + for (size_t i=0; i < SAL_N_ELEMENTS(fVec1); ++i) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9 after Cut.", fVec1[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + // Paste to A4:D4 + ScRange aPasteRange(0,3,0, 3,3,0); + aMark.SetMarkArea(aPasteRange); + m_pDoc->CopyFromClip( aPasteRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc); + + // Check results in E3:E9 after Paste. + const double fVec2[] = { 2200.0, 1200.0, -700.0, -700.0, 900.0, 900.0, 900.0 }; + CPPUNIT_ASSERT_EQUAL_MESSAGE( "Number of checks mismatch.", SAL_N_ELEMENTS(fVec2), aData.size()); + aPos = aFormulaPos; + for (size_t i=0; i < SAL_N_ELEMENTS(fVec2); ++i) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE( "E3:E9 after Paste.", fVec2[i], m_pDoc->GetValue(aPos)); + aPos.IncRow(); + } + + m_pDoc->DeleteTab(0); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit af7516c81be02a3500a85a3c66923bb18afcd628 Author: Eike Rathke <[email protected]> AuthorDate: Sat Mar 9 01:20:46 2019 +0100 Commit: Andras Timar <[email protected]> CommitDate: Wed Apr 24 22:48:14 2019 +0200 Resolves: tdf#120013 tdf#123714 tdf#123736 shared formula group split This is a combination of 7 commits. Resolves: tdf#123714 tdf#123736 all split formula groups; tdf#120013 related Add all split shared formula groups to regrouping and mark for listening, even if the references had not be to adjusted. This partly also resolves tdf#120013 but there's more to that, a remaining partial group is not updated. Reviewed-on: https://gerrit.libreoffice.org/68951 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 7fdc5df36f5b50e0629405a47ff3d5765fcfeb93) Resolves: tdf#120013 tdf#123714 split-off group or single cell needs listening Reviewed-on: https://gerrit.libreoffice.org/69066 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 5c27a048658afcd2f78ef4d7e6c7128554ed3f4c) Resolves: tdf#123736 re-establish listeners for unshared formula groups ... when replacing one of their formula cells, with another formula cell or any other cell, passing through DetachFormulaCell() Reviewed-on: https://gerrit.libreoffice.org/69221 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 1634a6d926c6cfd8fe92be1f3ae6083d2fb80f5e) In case of sc::NoListening only SetNeedsListeningGroup(), tdf#123736 follow-up Reviewed-on: https://gerrit.libreoffice.org/69303 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 6160025b27e97841321be29863bb1efd8c194a5f) Related: tdf#123736 re-establish listeners also for vector unsharing ... via DetachFormulaCells() Reviewed-on: https://gerrit.libreoffice.org/69320 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit 564d0d145cf9c164ea9c717b4b2113fd971fa0af) Reget position_type as the block or type may have changed, tdf#123736 related Reviewed-on: https://gerrit.libreoffice.org/69358 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit e5de84e73ffbaa1a45ab787750f5997582bbfa49) Listening when grouping in ScColumn::AttachNewFormulaCells(), (tdf#123736) Not directly related to tdf#123736 but similar approach. Setting a vector with ScColumn::SetFormulaCells() is currently only done for Undo documents, but implementation provided listening as only single cell listening for not-undo/clip documents, which wouldn't work if actually used in grouping context. The upcoming unit tests will use SetFormulaCells() for checks. Reviewed-on: https://gerrit.libreoffice.org/69371 Reviewed-by: Eike Rathke <[email protected]> Tested-by: Jenkins (cherry picked from commit de024e572dd7a588f82b84c68daa2051ec6b20e9) c511f618e9dde2288491c01cfcd889970fd6e4e5 d527307ef5278d87345c6dbfab9d05cb490dfe6c 3e5a5334dfe11002b526f942463626b62efbc340 a57308495a06e0df612eb1610b5f387d6b60ce08 2faf3e74d95cb1f3088f20cbb7de7ba965a6de21 990e6a5d6e1efcf70a2661b3a9a39c37d9e4c2e6 Change-Id: If6d1fef7e545017232a1b7e29b4d60dd58775e88 Reviewed-on: https://gerrit.libreoffice.org/69554 Tested-by: Jenkins Reviewed-by: Dennis Francis <[email protected]> Reviewed-by: Katarina Behrens <[email protected]> diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx index a8d6e49a7ca4..5576153cbaed 100644 --- a/sc/inc/column.hxx +++ b/sc/inc/column.hxx @@ -636,13 +636,23 @@ public: /** * Detach a formula cell that's about to be deleted, or removed from * document storage (if that ever happens). + * + * @param rNewSharedRows collects possible new shared row ranges (top and + * bottom of shared or remaining single twice) resulting from + * unsharing to reestablish listeners on. */ - void DetachFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell ); + void DetachFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, + std::vector<SCROW>& rNewSharedRows ); + + /** Re-establish listeners on unshared formula groups */ + void StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows ); - void DetachFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength ); + void DetachFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength, + std::vector<SCROW>* pNewSharedRows ); void AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ); - void DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ); + void DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, + std::vector<SCROW>* pNewSharedRows ); /** * Regroup formula cells for the entire column. @@ -692,18 +702,23 @@ public: bool ReservePatternCount( SCSIZE nReserve ); private: - sc::CellStoreType::iterator GetPositionToInsert( SCROW nRow ); - sc::CellStoreType::iterator GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow ); + sc::CellStoreType::iterator GetPositionToInsert( SCROW nRow, std::vector<SCROW>& rNewSharedRows ); + sc::CellStoreType::iterator GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow, + std::vector<SCROW>& rNewSharedRows ); void AttachNewFormulaCell( const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell, + const std::vector<SCROW>& rNewSharedRows, bool bJoin = true, sc::StartListeningType eListenType = sc::SingleCellListening ); void AttachNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, + const std::vector<SCROW>& rNewSharedRows, bool bJoin = true, sc::StartListeningType eListenType = sc::SingleCellListening ); - void AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength ); + void AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength, + std::vector<SCROW>& rNewSharedRows ); + void BroadcastNewCell( SCROW nRow ); bool UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow, sc::CellStoreType::iterator& itr ); diff --git a/sc/inc/sharedformula.hxx b/sc/inc/sharedformula.hxx index f4c8ab018532..b7c9577b2dfa 100644 --- a/sc/inc/sharedformula.hxx +++ b/sc/inc/sharedformula.hxx @@ -57,6 +57,9 @@ public: } } + /** Get shared formula top cell from position, if any, else nullptr. */ + static const ScFormulaCell* getSharedTopFormulaCell(const CellStoreType::position_type& aPos); + /** * Split existing shared formula range at specified position. The cell at * specified position becomes the top cell of the lower shared formula @@ -65,8 +68,11 @@ public: * * @param aPos position of cell to examine. * @param pCxt context to be used, if any, may be nullptr. + * + * @return TRUE if there indeed was a split, else FALSE (e.g. split + * position was top or bottom cell or no formula group). */ - static void splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt); + static bool splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt); /** * Split existing shared formula ranges at specified row positions. @@ -75,8 +81,11 @@ public: * @param rBounds row positions at which to split existing shared formula * ranges. Note that this method will directly modify this * parameter to sort and remove duplicates. + * + * @return TRUE if there indeed was a split, else FALSE (e.g. split + * positions were only top or bottom cells or no formula group). */ - static void splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds); + static bool splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds); /** * See if two specified adjacent formula cells can be merged, and if they diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx index 0436dd3f3173..fcee215f1580 100644 --- a/sc/source/core/data/column.cxx +++ b/sc/source/core/data/column.cxx @@ -2441,7 +2441,7 @@ bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc } // Do the actual splitting. - sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds); + const bool bSplit = sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds); // Collect all formula groups. std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries(); @@ -2449,7 +2449,7 @@ bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc // Process all collected formula groups. UpdateRefOnNonCopy aHandler(nCol, nTab, &rCxt, pUndoDoc); aHandler = std::for_each(aGroups.begin(), aGroups.end(), aHandler); - if (aHandler.isUpdated()) + if (bSplit || aHandler.isUpdated()) rCxt.maRegroupCols.set(nTab, nCol); return aHandler.isUpdated(); diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx index b0950987e20c..74440216b915 100644 --- a/sc/source/core/data/column3.cxx +++ b/sc/source/core/data/column3.cxx @@ -262,9 +262,9 @@ void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize, std::vector<ScAddress>* CellStorageModified(); } -sc::CellStoreType::iterator ScColumn::GetPositionToInsert( SCROW nRow ) +sc::CellStoreType::iterator ScColumn::GetPositionToInsert( SCROW nRow, std::vector<SCROW>& rNewSharedRows ) { - return GetPositionToInsert(maCells.begin(), nRow); + return GetPositionToInsert(maCells.begin(), nRow, rNewSharedRows); } void ScColumn::JoinNewFormulaCell( @@ -288,15 +288,74 @@ void ScColumn::JoinNewFormulaCell( } void ScColumn::DetachFormulaCell( - const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell ) + const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, std::vector<SCROW>& rNewSharedRows ) { if (!GetDoc()->IsClipOrUndo()) + { +#if USE_FORMULA_GROUP_LISTENER + if (rCell.IsShared() && rCell.GetSharedLength() > 1) + { + // Record new spans (shared or remaining single) that will result + // from unsharing to reestablish listeners. + // Same cases as in unshareFormulaCell(). + // XXX NOTE: this is not part of unshareFormulaCell() because that + // is called in other contexts as well, for which passing and + // determining the rows vector would be superfluous. If that was + // needed, move it there. + const SCROW nSharedTopRow = rCell.GetSharedTopRow(); + const SCROW nSharedLength = rCell.GetSharedLength(); + if (rCell.aPos.Row() == nSharedTopRow) + { + // Top cell. + // Next row will be new shared top or single cell. + rNewSharedRows.push_back( nSharedTopRow + 1); + rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1); + } + else if (rCell.aPos.Row() == nSharedTopRow + nSharedLength - 1) + { + // Bottom cell. + // Current shared top row will be new shared top again or + // single cell. + rNewSharedRows.push_back( nSharedTopRow); + rNewSharedRows.push_back( rCell.aPos.Row() - 1); + } + else + { + // Some mid cell. + // Current shared top row will be new shared top again or + // single cell, plus a new shared top below or single cell. + rNewSharedRows.push_back( nSharedTopRow); + rNewSharedRows.push_back( rCell.aPos.Row() - 1); + rNewSharedRows.push_back( rCell.aPos.Row() + 1); + rNewSharedRows.push_back( nSharedTopRow + nSharedLength - 1); + } + } +#endif + // Have the dying formula cell stop listening. + // If in a shared formula group this ends the group listening. rCell.EndListeningTo(GetDoc()); + } sc::SharedFormulaUtil::unshareFormulaCell(aPos, rCell); } +void ScColumn::StartListeningUnshared( const std::vector<SCROW>& rNewSharedRows ) +{ + assert(rNewSharedRows.empty() || rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4); + ScDocument* pDoc = GetDoc(); + if (!rNewSharedRows.empty() /* && !pDoc->IsDelayedFormulaGrouping() N/A in backport */) + { + std::shared_ptr<sc::ColumnBlockPositionSet> pPosSet(new sc::ColumnBlockPositionSet(*pDoc)); + sc::StartListeningContext aStartCxt(*pDoc, pPosSet); + sc::EndListeningContext aEndCxt(*pDoc, pPosSet); + if (rNewSharedRows.size() >= 2) + StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[0], rNewSharedRows[1]); + if (rNewSharedRows.size() >= 4) + StartListeningFormulaCells(aStartCxt, aEndCxt, rNewSharedRows[2], rNewSharedRows[3]); + } +} + namespace { class AttachFormulaCellsHandler @@ -334,14 +393,59 @@ public: } void ScColumn::DetachFormulaCells( - const sc::CellStoreType::position_type& aPos, size_t nLength ) + const sc::CellStoreType::position_type& aPos, size_t nLength, std::vector<SCROW>* pNewSharedRows ) { + const size_t nRow = aPos.first->position + aPos.second; + const size_t nNextTopRow = nRow + nLength; // start row of next formula group. + + bool bLowerSplitOff = false; + if (pNewSharedRows && !GetDoc()->IsClipOrUndo()) + { + const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos); + if (pFC) + { + const SCROW nTopRow = pFC->GetSharedTopRow(); + const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; + // nTopRow <= nRow <= nBotRow, because otherwise pFC would not exist. + if (nTopRow < static_cast<SCROW>(nRow)) + { + // Upper part will be split off. + pNewSharedRows->push_back(nTopRow); + pNewSharedRows->push_back(nRow - 1); + } + if (static_cast<SCROW>(nNextTopRow) <= nBotRow) + { + // Lower part will be split off. + pNewSharedRows->push_back(nNextTopRow); + pNewSharedRows->push_back(nBotRow); + bLowerSplitOff = true; + } + } + } + // Split formula grouping at the top and bottom boundaries. sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr); - size_t nRow = aPos.first->position + aPos.second; - size_t nNextTopRow = nRow + nLength; // start row of next formula group. - if (ValidRow(nNextTopRow)) + + if (nLength > 0 && ValidRow(nNextTopRow)) { + if (pNewSharedRows && !bLowerSplitOff && !GetDoc()->IsClipOrUndo()) + { + sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow-1); + const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2); + if (pFC) + { + const SCROW nTopRow = pFC->GetSharedTopRow(); + const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; + // nRow < nTopRow < nNextTopRow <= nBotRow + if (static_cast<SCROW>(nNextTopRow) <= nBotRow) + { + // Lower part will be split off. + pNewSharedRows->push_back(nNextTopRow); + pNewSharedRows->push_back(nBotRow); + } + } + } + sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nNextTopRow); sc::SharedFormulaUtil::splitFormulaCellGroup(aPos2, nullptr); } @@ -372,15 +476,59 @@ void ScColumn::AttachFormulaCells( sc::StartListeningContext& rCxt, SCROW nRow1, sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc); } -void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2 ) +void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, + std::vector<SCROW>* pNewSharedRows ) { sc::CellStoreType::position_type aPos = maCells.position(nRow1); sc::CellStoreType::iterator it = aPos.first; + bool bLowerSplitOff = false; + if (pNewSharedRows && !GetDoc()->IsClipOrUndo()) + { + const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos); + if (pFC) + { + const SCROW nTopRow = pFC->GetSharedTopRow(); + const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; + // nTopRow <= nRow1 <= nBotRow, because otherwise pFC would not exist. + if (nTopRow < nRow1) + { + // Upper part will be split off. + pNewSharedRows->push_back(nTopRow); + pNewSharedRows->push_back(nRow1 - 1); + } + if (nRow2 < nBotRow) + { + // Lower part will be split off. + pNewSharedRows->push_back(nRow2 + 1); + pNewSharedRows->push_back(nBotRow); + bLowerSplitOff = true; + } + } + } + // Split formula grouping at the top and bottom boundaries. sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt); if (ValidRow(nRow2+1)) { + if (pNewSharedRows && !bLowerSplitOff && !GetDoc()->IsClipOrUndo()) + { + sc::CellStoreType::position_type aPos2 = maCells.position(aPos.first, nRow2); + const ScFormulaCell* pFC = sc::SharedFormulaUtil::getSharedTopFormulaCell(aPos2); + if (pFC) + { + const SCROW nTopRow = pFC->GetSharedTopRow(); + const SCROW nBotRow = nTopRow + pFC->GetSharedLength() - 1; + // nRow1 < nTopRow <= nRow2 < nBotRow + if (nRow2 < nBotRow) + { + // Lower part will be split off. + pNewSharedRows->push_back(nRow2 + 1); + pNewSharedRows->push_back(nBotRow); + } + } + } + aPos = maCells.position(it, nRow2+1); sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, &rCxt); } @@ -392,7 +540,8 @@ void ScColumn::DetachFormulaCells( sc::EndListeningContext& rCxt, SCROW nRow1, S sc::ProcessFormula(it, maCells, nRow1, nRow2, aFunc); } -sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow ) +sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreType::iterator& it, SCROW nRow, + std::vector<SCROW>& rNewSharedRows ) { // See if we are overwriting an existing formula cell. sc::CellStoreType::position_type aPos = maCells.position(it, nRow); @@ -400,7 +549,7 @@ sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreTy if (itRet->type == sc::element_type_formula) { ScFormulaCell& rCell = *sc::formula_block::at(*itRet->data, aPos.second); - DetachFormulaCell(aPos, rCell); + DetachFormulaCell(aPos, rCell, rNewSharedRows); } return itRet; @@ -408,13 +557,15 @@ sc::CellStoreType::iterator ScColumn::GetPositionToInsert( const sc::CellStoreTy void ScColumn::AttachNewFormulaCell( const sc::CellStoreType::iterator& itPos, SCROW nRow, ScFormulaCell& rCell, + const std::vector<SCROW>& rNewSharedRows, bool bJoin, sc::StartListeningType eListenType ) { - AttachNewFormulaCell(maCells.position(itPos, nRow), rCell, bJoin, eListenType); + AttachNewFormulaCell(maCells.position(itPos, nRow), rCell, rNewSharedRows, bJoin, eListenType); } void ScColumn::AttachNewFormulaCell( const sc::CellStoreType::position_type& aPos, ScFormulaCell& rCell, + const std::vector<SCROW>& rNewSharedRows, bool bJoin, sc::StartListeningType eListenType ) { if (bJoin) @@ -437,24 +588,50 @@ void ScColumn::AttachNewFormulaCell( std::shared_ptr<sc::ColumnBlockPositionSet> pPosSet(new sc::ColumnBlockPositionSet(*pDocument)); sc::StartListeningContext aStartCxt(*pDocument, pPosSet); sc::EndListeningContext aEndCxt(*pDocument, pPosSet); - SCROW nRow = aPos.first->position + aPos.second; - StartListeningFormulaCells(aStartCxt, aEndCxt, nRow, nRow); + SCROW nStartRow, nEndRow; + nStartRow = nEndRow = aPos.first->position + aPos.second; + for (const SCROW nR : rNewSharedRows) + { + if (nStartRow > nR) + nStartRow = nR; + if (nEndRow < nR) + nEndRow = nR; + } + StartListeningFormulaCells(aStartCxt, aEndCxt, nStartRow, nEndRow); } break; case sc::SingleCellListening: rCell.StartListeningTo(pDocument); + StartListeningUnshared( rNewSharedRows); break; case sc::NoListening: default: - ; - + if (!rNewSharedRows.empty()) + { + assert(rNewSharedRows.size() == 2 || rNewSharedRows.size() == 4); + // Calling SetNeedsListeningGroup() with a top row sets it to + // all affected formula cells of that group. + const ScFormulaCell* pFC = GetFormulaCell( rNewSharedRows[0]); + assert(pFC); // that *is* supposed to be a top row + if (pFC && !pFC->NeedsListening()) + SetNeedsListeningGroup( rNewSharedRows[0]); + if (rNewSharedRows.size() > 2) + { + pFC = GetFormulaCell( rNewSharedRows[2]); + assert(pFC); // that *is* supposed to be a top row + if (pFC && !pFC->NeedsListening()) + SetNeedsListeningGroup( rNewSharedRows[2]); + } + } + break; } if (!pDocument->IsCalcingAfterLoad()) rCell.SetDirty(); } -void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength ) +void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aPos, size_t nLength, + std::vector<SCROW>& rNewSharedRows ) { // Make sure the whole length consists of formula cells. if (aPos.first->type != sc::element_type_formula) @@ -465,26 +642,61 @@ void ScColumn::AttachNewFormulaCells( const sc::CellStoreType::position_type& aP return; // Join the top and bottom cells only. - ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second); - JoinNewFormulaCell(aPos, *pCell); + ScFormulaCell* pCell1 = sc::formula_block::at(*aPos.first->data, aPos.second); + JoinNewFormulaCell(aPos, *pCell1); sc::CellStoreType::position_type aPosLast = aPos; aPosLast.second += nLength - 1; - pCell = sc::formula_block::at(*aPosLast.first->data, aPosLast.second); - JoinNewFormulaCell(aPosLast, *pCell); + ScFormulaCell* pCell2 = sc::formula_block::at(*aPosLast.first->data, aPosLast.second); + JoinNewFormulaCell(aPosLast, *pCell2); ScDocument* pDocument = GetDoc(); if (!pDocument->IsClipOrUndo() && !pDocument->IsInsertingFromOtherDoc()) { + const bool bShared = pCell1->IsShared() || pCell2->IsShared(); + if (bShared) + { + const SCROW nTopRow = (pCell1->IsShared() ? pCell1->GetSharedTopRow() : pCell1->aPos.Row()); + const SCROW nBotRow = (pCell2->IsShared() ? + pCell2->GetSharedTopRow() + pCell2->GetSharedLength() - 1 : pCell2->aPos.Row()); + if (rNewSharedRows.empty()) + { + rNewSharedRows.push_back( nTopRow); + rNewSharedRows.push_back( nBotRow); + } + else if (rNewSharedRows.size() == 2) + { + // Combine into one span. + if (rNewSharedRows[0] > nTopRow) + rNewSharedRows[0] = nTopRow; + if (rNewSharedRows[1] < nBotRow) + rNewSharedRows[1] = nBotRow; + } + else if (rNewSharedRows.size() == 4) + { + // Merge into one span. + // The original two spans are ordered from top to bottom. + std::vector<SCROW> aRows(2); + aRows[0] = std::min( rNewSharedRows[0], nTopRow); + aRows[1] = std::max( rNewSharedRows[3], nBotRow); + rNewSharedRows.swap( aRows); + } + else + { + assert(!"rNewSharedRows?"); + } + } + StartListeningUnshared( rNewSharedRows); + sc::StartListeningContext aCxt(*pDocument); ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second); ScFormulaCell** ppEnd = pp + nLength; for (; pp != ppEnd; ++pp) { - pCell = *pp; - pCell->StartListeningTo(aCxt); + if (!bShared) + (*pp)->StartListeningTo(aCxt); if (!pDocument->IsCalcingAfterLoad()) - pCell->SetDirty(); + (*pp)->SetDirty(); } } } @@ -1523,7 +1735,7 @@ public: // Stop all formula cells in the destination range first. sc::CellStoreType::position_type aPos = rDestCells.position(mrBlockPos.miCellPos, mnRowOffset); - mrDestColumn.DetachFormulaCells(aPos, maNewCells.size()); + mrDestColumn.DetachFormulaCells(aPos, maNewCells.size(), nullptr); // Move the new cells to the destination range. sc::CellStoreType::iterator& itDestPos = mrBlockPos.miCellPos; @@ -1882,24 +2094,30 @@ bool ScColumn::SetString( SCROW nRow, SCTAB nTabP, const OUString& rString, void ScColumn::SetEditText( SCROW nRow, EditTextObject* pEditText ) { pEditText->NormalizeString(GetDoc()->GetSharedStringPool()); - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); maCells.set(it, nRow, pEditText); maCellTextAttrs.set(nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + BroadcastNewCell(nRow); } void ScColumn::SetEditText( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, EditTextObject* pEditText ) { pEditText->NormalizeString(GetDoc()->GetSharedStringPool()); - rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + std::vector<SCROW> aNewSharedRows; + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows); rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, pEditText); rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + BroadcastNewCell(nRow); } @@ -1939,7 +2157,8 @@ void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::Form { ScAddress aPos(nCol, nRow, nTab); - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rArray, eGram); sal_uInt32 nCellFormat = GetNumberFormat(GetDoc()->GetNonThreadedContext(), nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) @@ -1949,14 +2168,15 @@ void ScColumn::SetFormula( SCROW nRow, const ScTokenArray& rArray, formula::Form CellStorageModified(); - AttachNewFormulaCell(it, nRow, *pCell); + AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); } void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ) { ScAddress aPos(nCol, nRow, nTab); - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), aPos, rFormula, eGram); sal_uInt32 nCellFormat = GetNumberFormat(GetDoc()->GetNonThreadedContext(), nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0) @@ -1966,14 +2186,15 @@ void ScColumn::SetFormula( SCROW nRow, const OUString& rFormula, formula::Formul CellStorageModified(); - AttachNewFormulaCell(it, nRow, *pCell); + AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); } ScFormulaCell* ScColumn::SetFormulaCell( SCROW nRow, ScFormulaCell* pCell, sc::StartListeningType eListenType, bool bInheritNumFormatIfNeeded ) { - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); sal_uInt32 nCellFormat = GetNumberFormat(GetDoc()->GetNonThreadedContext(), nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded ) pCell->SetNeedNumberFormat(true); @@ -1982,7 +2203,8 @@ ScFormulaCell* ScColumn::SetFormulaCell( CellStorageModified(); - AttachNewFormulaCell(it, nRow, *pCell, true, eListenType); + AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows, true, eListenType); + return pCell; } @@ -1991,7 +2213,8 @@ void ScColumn::SetFormulaCell( sc::StartListeningType eListenType, bool bInheritNumFormatIfNeeded ) { - rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + std::vector<SCROW> aNewSharedRows; + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows); sal_uInt32 nCellFormat = GetNumberFormat(GetDoc()->GetNonThreadedContext(), nRow); if( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && bInheritNumFormatIfNeeded ) pCell->SetNeedNumberFormat(true); @@ -2001,7 +2224,7 @@ void ScColumn::SetFormulaCell( CellStorageModified(); - AttachNewFormulaCell(rBlockPos.miCellPos, nRow, *pCell, true, eListenType); + AttachNewFormulaCell(rBlockPos.miCellPos, nRow, *pCell, aNewSharedRows, true, eListenType); } bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells ) @@ -2016,7 +2239,8 @@ bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells sc::CellStoreType::position_type aPos = maCells.position(nRow); // Detach all formula cells that will be overwritten. - DetachFormulaCells(aPos, rCells.size()); + std::vector<SCROW> aNewSharedRows; + DetachFormulaCells(aPos, rCells.size(), &aNewSharedRows); if (!GetDoc()->IsClipOrUndo()) { @@ -2036,7 +2260,10 @@ bool ScColumn::SetFormulaCells( SCROW nRow, std::vector<ScFormulaCell*>& rCells CellStorageModified(); - AttachNewFormulaCells(aPos, rCells.size()); + // Reget position_type as the type may have changed to formula, block and + // block size changed, ... + aPos = maCells.position(nRow); + AttachNewFormulaCells(aPos, rCells.size(), aNewSharedRows); return true; } @@ -2473,13 +2700,14 @@ void ScColumn::SetError( SCROW nRow, const FormulaError nError) ScFormulaCell* pCell = new ScFormulaCell(GetDoc(), ScAddress(nCol, nRow, nTab)); pCell->SetErrCode(nError); - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); it = maCells.set(it, nRow, pCell); maCellTextAttrs.set(nRow, sc::CellTextAttr()); CellStorageModified(); - AttachNewFormulaCell(it, nRow, *pCell); + AttachNewFormulaCell(it, nRow, *pCell, aNewSharedRows); } void ScColumn::SetRawString( SCROW nRow, const OUString& rStr ) @@ -2499,12 +2727,15 @@ void ScColumn::SetRawString( SCROW nRow, const svl::SharedString& rStr ) if (!ValidRow(nRow)) return; - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); maCells.set(it, nRow, rStr); maCellTextAttrs.set(nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + BroadcastNewCell(nRow); } @@ -2514,13 +2745,16 @@ void ScColumn::SetRawString( if (!ValidRow(nRow)) return; - rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + std::vector<SCROW> aNewSharedRows; + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows); rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, rStr); rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + if (bBroadcast) BroadcastNewCell(nRow); } @@ -2530,12 +2764,15 @@ void ScColumn::SetValue( SCROW nRow, double fVal ) if (!ValidRow(nRow)) return; - sc::CellStoreType::iterator it = GetPositionToInsert(nRow); + std::vector<SCROW> aNewSharedRows; + sc::CellStoreType::iterator it = GetPositionToInsert(nRow, aNewSharedRows); maCells.set(it, nRow, fVal); maCellTextAttrs.set(nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + BroadcastNewCell(nRow); } @@ -2545,13 +2782,16 @@ void ScColumn::SetValue( if (!ValidRow(nRow)) return; - rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow); + std::vector<SCROW> aNewSharedRows; + rBlockPos.miCellPos = GetPositionToInsert(rBlockPos.miCellPos, nRow, aNewSharedRows); rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow, fVal); rBlockPos.miCellTextAttrPos = maCellTextAttrs.set( rBlockPos.miCellTextAttrPos, nRow, sc::CellTextAttr()); CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + if (bBroadcast) BroadcastNewCell(nRow); } diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx index 59d8923dded1..520bd815873c 100644 --- a/sc/source/core/data/column4.cxx +++ b/sc/source/core/data/column4.cxx @@ -316,7 +316,8 @@ void ScColumn::SetValues( SCROW nRow, const std::vector<double>& rVals ) return; sc::CellStoreType::position_type aPos = maCells.position(nRow); - DetachFormulaCells(aPos, rVals.size()); + std::vector<SCROW> aNewSharedRows; + DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows); maCells.set(nRow, rVals.begin(), rVals.end()); std::vector<sc::CellTextAttr> aDefaults(rVals.size()); @@ -324,6 +325,8 @@ void ScColumn::SetValues( SCROW nRow, const std::vector<double>& rVals ) CellStorageModified(); + StartListeningUnshared( aNewSharedRows); + std::vector<SCROW> aRows; aRows.reserve(rVals.size()); for (SCROW i = nRow; i <= nLastRow; ++i) @@ -343,7 +346,7 @@ void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rD return; sc::CellStoreType::position_type aPos = maCells.position(nRow); - DetachFormulaCells(aPos, nLen); + DetachFormulaCells(aPos, nLen, nullptr); rDest.transferFrom(*this, nRow, nLen); @@ -368,7 +371,7 @@ void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc ) return; sc::CellStoreType::position_type aPos = maCells.position(nRow); - DetachFormulaCells(aPos, rSrc.size()); + DetachFormulaCells(aPos, rSrc.size(), nullptr); rSrc.copyTo(*this, nRow); @@ -444,7 +447,7 @@ void ScColumn::ConvertFormulaToValue( // No formula cells encountered. return; - DetachFormulaCells(rCxt, nRow1, nRow2); + DetachFormulaCells(rCxt, nRow1, nRow2, nullptr); // Undo storage to hold static values which will get swapped to the cell storage later. sc::CellValues aUndoCells; diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx index ed6046dea2a0..715ed11dbc49 100644 --- a/sc/source/core/data/table2.cxx +++ b/sc/source/core/data/table2.cxx @@ -1079,7 +1079,7 @@ void ScTable::DetachFormulaCells( sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) { for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) - aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2); + aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2, nullptr); } void ScTable::SetDirtyFromClip( diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx index 4b688654077c..c91e512fee5e 100644 --- a/sc/source/core/tool/sharedformula.cxx +++ b/sc/source/core/tool/sharedformula.cxx @@ -16,28 +16,44 @@ namespace sc { -void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt) +const ScFormulaCell* SharedFormulaUtil::getSharedTopFormulaCell(const CellStoreType::position_type& aPos) +{ + if (aPos.first->type != sc::element_type_formula) + // Not a formula cell block. + return nullptr; + + sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data); + std::advance(it, aPos.second); + const ScFormulaCell* pCell = *it; + if (!pCell->IsShared()) + // Not a shared formula. + return nullptr; + + return pCell->GetCellGroup()->mpTopCell; +} + +bool SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos, sc::EndListeningContext* pCxt) { SCROW nRow = aPos.first->position + aPos.second; if (aPos.first->type != sc::element_type_formula) // Not a formula cell block. - return; + return false; if (aPos.second == 0) // Split position coincides with the block border. Nothing to do. - return; + return false; sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data); std::advance(it, aPos.second); ScFormulaCell& rTop = **it; if (!rTop.IsShared()) // Not a shared formula. - return; + return false; if (nRow == rTop.GetSharedTopRow()) // Already the top cell of a shared group. - return; + return false; ScFormulaCellGroupRef xGroup = rTop.GetCellGroup(); @@ -70,6 +86,9 @@ void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type else rPrevTop.EndListeningTo( rPrevTop.GetDocument(), nullptr, ScAddress( ScAddress::UNINITIALIZED)); rPrevTop.SetNeedsListening(true); + + // The new group or remaining single cell needs a new listening. + rTop.SetNeedsListening(true); } #endif @@ -96,12 +115,14 @@ void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type ScFormulaCell& rCell = **it; rCell.SetCellGroup(xGroup2); } + + return true; } -void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds) +bool SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds) { if (rBounds.empty()) - return; + return false; // Sort and remove duplicates. std::sort(rBounds.begin(), rBounds.end()); @@ -112,9 +133,9 @@ void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vecto SCROW nRow = *it; CellStoreType::position_type aPos = rCells.position(nRow); if (aPos.first == rCells.end()) - return; + return false; - splitFormulaCellGroup(aPos, nullptr); + bool bSplit = splitFormulaCellGroup(aPos, nullptr); std::vector<SCROW>::iterator itEnd = rBounds.end(); for (++it; it != itEnd; ++it) { @@ -123,10 +144,11 @@ void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vecto { aPos = rCells.position(aPos.first, nRow); if (aPos.first == rCells.end()) - return; - splitFormulaCellGroup(aPos, nullptr); + return bSplit; + bSplit |= splitFormulaCellGroup(aPos, nullptr); } } + return bSplit; } bool SharedFormulaUtil::joinFormulaCells( @@ -234,9 +256,9 @@ void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& a { // Move the top cell to the next formula cell down. ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1); - --xGroup->mnLength; xGroup->mpTopCell = &rNext; } + --xGroup->mnLength; } else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1) { _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
