From 0e560ad933eceedf2af31e07046582c2d10a1ff2 Mon Sep 17 00:00:00 2001 From: Caolán McNamara Date: Nov 11 2014 09:29:52 +0000 Subject: fix regression checks --- diff --git a/0001-Adapt-sorting-unit-tests-for-new-default.patch b/0001-Adapt-sorting-unit-tests-for-new-default.patch new file mode 100644 index 0000000..7875402 --- /dev/null +++ b/0001-Adapt-sorting-unit-tests-for-new-default.patch @@ -0,0 +1,248 @@ +From 77c6ce66696a997269b9fe4dfed1dc2e51ecd00e Mon Sep 17 00:00:00 2001 +From: Michael Meeks +Date: Fri, 10 Oct 2014 13:00:35 +0100 +Subject: [PATCH] Adapt sorting unit tests for new default. + +Change-Id: I9885e2712753390f0597233c404ab80c0ad2b537 +Reviewed-on: https://gerrit.libreoffice.org/11904 +Reviewed-by: Muthu Subramanian K +Reviewed-by: Eike Rathke +Tested-by: Eike Rathke +--- + sc/Library_scqahelper.mk | 2 +- + sc/qa/unit/filters-test.cxx | 9 +++++++ + sc/qa/unit/helper/sorthelper.hxx | 55 ++++++++++++++++++++++++++++++++++++++++ + sc/qa/unit/ucalc_sort.cxx | 26 ++++++++++++++----- + 4 files changed, 85 insertions(+), 7 deletions(-) + create mode 100644 sc/qa/unit/helper/sorthelper.hxx + +diff --git a/sc/Library_scqahelper.mk b/sc/Library_scqahelper.mk +index 351b115..912d5f8 100644 +--- a/sc/Library_scqahelper.mk ++++ b/sc/Library_scqahelper.mk +@@ -46,7 +46,7 @@ $(eval $(call gb_Library_use_libraries,scqahelper,\ + svl \ + svt \ + svx \ +- svxcore \ ++ svxcore \ + test \ + tl \ + unotest \ +diff --git a/sc/qa/unit/filters-test.cxx b/sc/qa/unit/filters-test.cxx +index 0ea9f85..596f3fa 100644 +--- a/sc/qa/unit/filters-test.cxx ++++ b/sc/qa/unit/filters-test.cxx +@@ -21,6 +21,7 @@ + #include + + #include "helper/qahelper.hxx" ++#include "helper/sorthelper.hxx" + + #include "docsh.hxx" + #include "postit.hxx" +@@ -549,6 +550,13 @@ void ScFiltersTest::testEnhancedProtectionXLSX() + + void ScFiltersTest::testSortWithSharedFormulasODS() + { ++#if 0 ++ // This guy is a nightmare - he requires a ton of internal / ++ // private API from sc - that has a huge knock-on effect on ++ // filters-test linking etc. etc. - urgh ... surely we should ++ // test this just in ucalc - review appreciated Eike ... ++ SortRefUpdateSetter aUpdateSet; ++ + ScDocShellRef xDocSh = loadDoc("shared-formula/sort-crash.", ODS, true); + CPPUNIT_ASSERT(xDocSh.Is()); + ScDocument* pDoc = xDocSh->GetDocument(); +@@ -594,6 +602,7 @@ void ScFiltersTest::testSortWithSharedFormulasODS() + CPPUNIT_ASSERT_EQUAL(static_cast(15), pFC->GetSharedLength()); + + xDocSh->DoClose(); ++#endif + } + + ScFiltersTest::ScFiltersTest() +diff --git a/sc/qa/unit/helper/sorthelper.hxx b/sc/qa/unit/helper/sorthelper.hxx +new file mode 100644 +index 0000000..e82b8c2 +--- /dev/null ++++ b/sc/qa/unit/helper/sorthelper.hxx +@@ -0,0 +1,55 @@ ++/* -*- 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/. ++ */ ++ ++#ifndef INCLUDED_SC_QA_SORT_HELPER_QAHELPER_HXX ++#define INCLUDED_SC_QA_SORT_HELPER_QAHELPER_HXX ++ ++// Unfortunately requires linkage to sc/ internals so ++// can't live in qahelper itself. ++#include "inputopt.hxx" ++ ++/** ++ * Temporarily set the sorting type. ++ */ ++class SortTypeSetter { ++ bool mbSortRefUpdate; ++public: ++ SortTypeSetter(bool bSortRefUpdate) ++ { ++ mbSortRefUpdate = changeTo(bSortRefUpdate); ++ } ++ bool changeTo(bool bSortRefUpdate) ++ { ++ ScInputOptions aInputOptions = SC_MOD()->GetInputOptions(); ++ bool bRet = aInputOptions.GetSortRefUpdate(); ++ aInputOptions.SetSortRefUpdate(bSortRefUpdate); ++ SC_MOD()->SetInputOptions(aInputOptions); ++ return bRet; ++ } ++ virtual ~SortTypeSetter() ++ { ++ changeTo(mbSortRefUpdate); ++ } ++}; ++ ++class SortRefNoUpdateSetter : private SortTypeSetter ++{ ++public: ++ SortRefNoUpdateSetter() : SortTypeSetter(false) {} ++}; ++ ++class SortRefUpdateSetter : private SortTypeSetter ++{ ++public: ++ SortRefUpdateSetter() : SortTypeSetter(true) {} ++}; ++ ++#endif ++ ++/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +diff --git a/sc/qa/unit/ucalc_sort.cxx b/sc/qa/unit/ucalc_sort.cxx +index f81a394..ce6b9b3 100644 +--- a/sc/qa/unit/ucalc_sort.cxx ++++ b/sc/qa/unit/ucalc_sort.cxx +@@ -8,6 +8,7 @@ + */ + + #include "ucalc.hxx" ++#include "helper/sorthelper.hxx" + + #include + #include +@@ -18,7 +19,6 @@ + #include + #include + #include +-#include + #include + + #include +@@ -117,6 +117,8 @@ void Test::testSort() + + void Test::testSortHorizontal() + { ++ SortRefUpdateSetter aUpdateSet; ++ + ScFormulaOptions aOptions; + aOptions.SetFormulaSepArg(";"); + aOptions.SetFormulaSepArrayCol(";"); +@@ -361,6 +363,8 @@ void Test::testSortSingleRow() + // if cells in the sort are referenced by formulas + void Test::testSortWithFormulaRefs() + { ++ SortRefUpdateSetter aUpdateSet; ++ + m_pDoc->InsertTab(0, "List1"); + m_pDoc->InsertTab(1, "List2"); + +@@ -460,6 +464,8 @@ void Test::testSortWithStrings() + + void Test::testSortInFormulaGroup() + { ++ SortRefUpdateSetter aUpdateSet; ++ + static struct { + SCCOL nCol; + SCROW nRow; +@@ -691,6 +697,8 @@ void Test::testSortWithCellFormats() + + void Test::testSortRefUpdate() + { ++ SortTypeSetter aSortTypeSet(true); ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); + +@@ -811,8 +819,7 @@ void Test::testSortRefUpdate() + m_pDoc->SetString(ScAddress(2,1+i,0), "=RC[-2]"); + + // Turn off reference update on sort. +- ScInputOptions aInputOption = SC_MOD()->GetInputOptions(); +- aInputOption.SetSortRefUpdate(false); ++ aSortTypeSet.changeTo(false); + + bSorted = aFunc.Sort(0, aSortData, true, true, true); + CPPUNIT_ASSERT(bSorted); +@@ -837,14 +844,13 @@ void Test::testSortRefUpdate() + CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); // column C + } + +- // Turn it back on. +- aInputOption.SetSortRefUpdate(true); +- + m_pDoc->DeleteTab(0); + } + + void Test::testSortRefUpdate2() + { ++ SortRefUpdateSetter aUpdateSet; ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); + +@@ -932,6 +938,8 @@ void Test::testSortRefUpdate2() + + void Test::testSortRefUpdate3() + { ++ SortRefUpdateSetter aUpdateSet; ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + m_pDoc->InsertTab(0, "Sort"); + +@@ -1020,6 +1028,8 @@ void Test::testSortRefUpdate3() + // testRefInterne.ods + void Test::testSortRefUpdate4() + { ++ SortRefUpdateSetter aUpdateSet; ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + m_pDoc->InsertTab(0, "Sort"); + m_pDoc->InsertTab(1, "Lesson1"); +@@ -1217,6 +1227,8 @@ void Test::testSortRefUpdate4() + * before midnight, ermm.. */ + void Test::testSortRefUpdate5() + { ++ SortRefUpdateSetter aUpdateSet; ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + m_pDoc->InsertTab(0, "Sort"); + +@@ -1388,6 +1400,8 @@ void Test::testSortOutOfPlaceResult() + + void Test::testSortPartialFormulaGroup() + { ++ SortRefUpdateSetter aUpdateSet; ++ + sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. + FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); + +-- +1.9.3 + diff --git a/0001-Back-port-Kohei-s-comprehensive-sorting-unit-tests-f.patch b/0001-Back-port-Kohei-s-comprehensive-sorting-unit-tests-f.patch new file mode 100644 index 0000000..2620af9 --- /dev/null +++ b/0001-Back-port-Kohei-s-comprehensive-sorting-unit-tests-f.patch @@ -0,0 +1,2057 @@ +From f4dabc071f0eaf0e22db9b23c573a380b5e480ff Mon Sep 17 00:00:00 2001 +From: Michael Meeks +Date: Thu, 9 Oct 2014 17:30:42 +0100 +Subject: [PATCH] Back-port Kohei's comprehensive sorting unit tests from + master. + +As of master commit fab88063281761dbdac7ea550072660fe0f8c863. + +Change-Id: I45ee0021c727281ba5590ceead6d8c5848e25169 +Reviewed-on: https://gerrit.libreoffice.org/11903 +Reviewed-by: Muthu Subramanian K +Reviewed-by: Eike Rathke +Tested-by: Eike Rathke +--- + sc/CppunitTest_sc_ucalc.mk | 1 + + sc/qa/unit/ucalc.cxx | 491 --------------- + sc/qa/unit/ucalc.hxx | 21 +- + sc/qa/unit/ucalc_sort.cxx | 1464 ++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1485 insertions(+), 492 deletions(-) + create mode 100644 sc/qa/unit/ucalc_sort.cxx + +diff --git a/sc/CppunitTest_sc_ucalc.mk b/sc/CppunitTest_sc_ucalc.mk +index 9b3c498..2054f19 100644 +--- a/sc/CppunitTest_sc_ucalc.mk ++++ b/sc/CppunitTest_sc_ucalc.mk +@@ -17,6 +17,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,sc_ucalc, \ + sc/qa/unit/ucalc_formula \ + sc/qa/unit/ucalc_pivottable \ + sc/qa/unit/ucalc_sharedformula \ ++ sc/qa/unit/ucalc_sort \ + )) + + $(eval $(call gb_CppunitTest_use_library_objects,sc_ucalc, \ +diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx +index 8e19f2f..2bb0889 100644 +--- a/sc/qa/unit/ucalc.cxx ++++ b/sc/qa/unit/ucalc.cxx +@@ -4724,497 +4724,6 @@ void Test::testFindAreaPosColRight() + m_pDoc->DeleteTab(0); + } + +-// regression test fo fdo#53814, sorting doens't work as expected +-// if cells in the sort are referenced by formulas +-void Test::testSortWithFormulaRefs() +-{ +- m_pDoc->InsertTab(0, "List1"); +- m_pDoc->InsertTab(1, "List2"); +- +- const char* aFormulaData[6] = { +- "=IF($List1.A2<>\"\";$List1.A2;\"\")", +- "=IF($List1.A3<>\"\";$List1.A3;\"\")", +- "=IF($List1.A4<>\"\";$List1.A4;\"\")", +- "=IF($List1.A5<>\"\";$List1.A5;\"\")", +- "=IF($List1.A6<>\"\";$List1.A6;\"\")", +- "=IF($List1.A7<>\"\";$List1.A7;\"\")", +- }; +- +- const char* aTextData[4] = { +- "bob", +- "tim", +- "brian", +- "larry", +- }; +- +- const char* aResults[6] = { +- "bob", +- "brian", +- "larry", +- "tim", +- "", +- "", +- }; +- +- // Insert data to sort in A2:A5 on the 1st sheet. +- for (SCROW i = 1; i <= 4; ++i) +- m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) ); +- +- // Insert forumulas in A1:A6 on the 2nd sheet. +- for (size_t i = 0; i < SAL_N_ELEMENTS(aFormulaData); ++i) +- m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) ); +- +- // Sort data in A2:A8 on the 1st sheet. No column header. +- ScSortParam aSortData; +- aSortData.nCol1 = 0; +- aSortData.nCol2 = 0; +- aSortData.nRow1 = 1; +- aSortData.nRow2 = 7; +- aSortData.maKeyState[0].bDoSort = true; +- aSortData.maKeyState[0].nField = 0; +- +- m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); +- +- for (size_t i = 0; i < SAL_N_ELEMENTS(aResults); ++i) +- { +- OUString sResult = m_pDoc->GetString(0, i + 1, 0); +- CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult ); +- } +- m_pDoc->DeleteTab(1); +- m_pDoc->DeleteTab(0); +-} +- +-void Test::testSortWithStrings() +-{ +- m_pDoc->InsertTab(0, "Test"); +- +- ScFieldEditEngine& rEE = m_pDoc->GetEditEngine(); +- rEE.SetText("Val1"); +- m_pDoc->SetString(ScAddress(1,1,0), "Header"); +- m_pDoc->SetString(ScAddress(1,2,0), "Val2"); +- m_pDoc->SetEditText(ScAddress(1,3,0), rEE.CreateTextObject()); +- +- CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0))); +- +- ScSortParam aParam; +- aParam.nCol1 = 1; +- aParam.nCol2 = 1; +- aParam.nRow1 = 1; +- aParam.nRow2 = 3; +- aParam.bHasHeader = true; +- aParam.maKeyState[0].bDoSort = true; +- aParam.maKeyState[0].bAscending = true; +- aParam.maKeyState[0].nField = 1; +- +- m_pDoc->Sort(0, aParam, false, true, NULL, NULL); +- +- CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,2,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,3,0))); +- +- aParam.maKeyState[0].bAscending = false; +- +- m_pDoc->Sort(0, aParam, false, true, NULL, NULL); +- +- CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0))); +- +- m_pDoc->DeleteTab(0); +-} +- +-void Test::testSort() +-{ +- m_pDoc->InsertTab(0, "test1"); +- +- ScRange aDataRange; +- ScAddress aPos(0,0,0); +- { +- const char* aData[][2] = { +- { "2", "4" }, +- { "4", "1" }, +- { "1", "2" }, +- { "1", "23" }, +- }; +- +- clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0)); +- aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); +- CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos); +- } +- +- // Insert note in cell B2. +- ScAddress rAddr(1, 1, 0); +- ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr); +- pNote->SetText(rAddr, "Hello"); +- pNote->SetAuthor("Jim Bob"); +- +- ScSortParam aSortData; +- aSortData.nCol1 = 1; +- aSortData.nCol2 = 1; +- aSortData.nRow1 = 0; +- aSortData.nRow2 = 2; +- aSortData.maKeyState[0].bDoSort = true; +- aSortData.maKeyState[0].nField = 1; +- aSortData.maKeyState[0].bAscending = true; +- +- m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); +- +- double nVal = m_pDoc->GetValue(1,0,0); +- ASSERT_DOUBLES_EQUAL(nVal, 1.0); +- +- // check that note is also moved after sorting +- pNote = m_pDoc->GetNote(1, 0, 0); +- CPPUNIT_ASSERT(pNote); +- +- clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10. +- { +- // 0 = empty cell +- const char* aData[][1] = { +- { "Title" }, +- { 0 }, +- { 0 }, +- { "12" }, +- { "b" }, +- { "1" }, +- { "9" }, +- { "123" } +- }; +- +- aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); +- CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos); +- } +- +- aSortData.nCol1 = aDataRange.aStart.Col(); +- aSortData.nCol2 = aDataRange.aEnd.Col(); +- aSortData.nRow1 = aDataRange.aStart.Row(); +- aSortData.nRow2 = aDataRange.aEnd.Row(); +- aSortData.bHasHeader = true; +- aSortData.maKeyState[0].nField = 0; +- m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); +- +- // Title should stay at the top, numbers should be sorted numerically, +- // numbers always come before strings, and empty cells always occur at the +- // end. +- CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos)); +- aPos.IncRow(); +- CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos)); +- +- m_pDoc->DeleteTab(0); +-} +- +-void Test::testSortHorizontal() +-{ +- ScFormulaOptions aOptions; +- aOptions.SetFormulaSepArg(";"); +- aOptions.SetFormulaSepArrayCol(";"); +- aOptions.SetFormulaSepArrayRow("|"); +- getDocShell().SetFormulaOptions(aOptions); +- +- sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); +- m_pDoc->InsertTab(0, "Sort"); +- +- // Test case from fdo#78079. +- +- // 0 = empty cell +- const char* aData[][4] = { +- { "table", "has UNIQUE", "Publish to EC2", "flag" }, +- { "w2gi.mobilehit", "Yes", "No", "=CONCATENATE(B2;\"-\";C2)" }, +- { "w2gi.visitors", "No", "No", "=CONCATENATE(B3;\"-\";C3)" }, +- { "w2gi.pagedimension", "Yes", "Yes", "=CONCATENATE(B4;\"-\";C4)" }, +- }; +- +- // Insert raw data into A1:D4. +- ScRange aDataRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData, SAL_N_ELEMENTS(aData)); +- CPPUNIT_ASSERT_EQUAL(OUString("A1:D4"), aDataRange.Format(SCA_VALID)); +- +- // Check the formula values. +- CPPUNIT_ASSERT_EQUAL(OUString("Yes-No"), m_pDoc->GetString(ScAddress(3,1,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("No-No"), m_pDoc->GetString(ScAddress(3,2,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Yes-Yes"), m_pDoc->GetString(ScAddress(3,3,0))); +- +- // Define A1:D4 as sheet-local anonymous database range. +- m_pDoc->SetAnonymousDBData( +- 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 3, 3)); +- +- // Sort A1:D4 horizontally, ascending by row 1. +- ScDBDocFunc aFunc(getDocShell()); +- +- ScSortParam aSortData; +- aSortData.nCol1 = 0; +- aSortData.nCol2 = 3; +- aSortData.nRow1 = 0; +- aSortData.nRow2 = 3; +- aSortData.bHasHeader = true; +- aSortData.bByRow = false; // Sort by column (in horizontal direction). +- aSortData.bIncludePattern = true; +- aSortData.maKeyState[0].bDoSort = true; +- aSortData.maKeyState[0].nField = 0; +- aSortData.maKeyState[0].bAscending = true; +- bool bSorted = aFunc.Sort(0, aSortData, true, true, true); +- CPPUNIT_ASSERT(bSorted); +- +- { +- // Expected output table content. 0 = empty cell +- const char* aOutputCheck[][4] = { +- { "table", "flag", "has UNIQUE", "Publish to EC2" }, +- { "w2gi.mobilehit", "Yes-No", "Yes", "No" }, +- { "w2gi.visitors", "No-No", "No", "No" }, +- { "w2gi.pagedimension", "Yes-Yes", "Yes", "Yes" }, +- }; +- +- bool bSuccess = checkOutput<4>(m_pDoc, aDataRange, aOutputCheck, "Sorted by column with formula"); +- CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess); +- } +- +- if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "CONCATENATE(C2;\"-\";D2)")) +- CPPUNIT_FAIL("Wrong formula!"); +- if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "CONCATENATE(C3;\"-\";D3)")) +- CPPUNIT_FAIL("Wrong formula!"); +- if (!checkFormula(*m_pDoc, ScAddress(1,3,0), "CONCATENATE(C4;\"-\";D4)")) +- CPPUNIT_FAIL("Wrong formula!"); +- +- m_pDoc->DeleteTab(0); +-} +- +-void Test::testSortInFormulaGroup() +-{ +- static struct { +- SCCOL nCol; +- SCROW nRow; +- const char *pData; +- } aEntries[] = { +- { 0, 0, "3" }, { 1, 0, "=A1" }, +- { 0, 1, "1" }, { 1, 1, "=A2" }, +- { 0, 2, "20" }, { 1, 2, "=A3" }, +- { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups +- { 0, 4, "2" }, { 1, 4, "=A5+1" }, +- { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguious group +- { 0, 6, "100" }, { 1, 6, "=A7" }, +- { 0, 7, "102" }, { 1, 7, "=A8" }, +- { 0, 8, "104" }, { 1, 8, "=A9" }, +- { 0, 9, "103" }, { 1, 9, "=A10" }, +- }; +- +- m_pDoc->InsertTab(0, "sorttest"); +- +- for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i ) +- m_pDoc->SetString( aEntries[i].nCol, aEntries[i].nRow, 0, +- OUString::createFromAscii( aEntries[i].pData) ); +- +- ScSortParam aSortData; +- aSortData.nCol1 = 0; +- aSortData.nCol2 = 1; +- aSortData.nRow1 = 0; +- aSortData.nRow2 = 9; +- aSortData.maKeyState[0].bDoSort = true; +- aSortData.maKeyState[0].nField = 0; +- aSortData.maKeyState[0].bAscending = true; +- +- m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); +- +- static struct { +- SCCOL nCol; +- SCROW nRow; +- double fValue; +- } aResults[] = { +- { 0, 0, 1.0 }, { 1, 0, 1.0 }, +- { 0, 1, 2.0 }, { 1, 1, 3.0 }, +- { 0, 2, 3.0 }, { 1, 2, 3.0 }, +- { 0, 3, 10.0 }, { 1, 3, 11.0 }, +- { 0, 4, 20.0 }, { 1, 4, 20.0 }, +- { 0, 5, 100.0 }, { 1, 5, 100.0 }, +- { 0, 6, 101.0 }, { 1, 6, 101.0 }, +- { 0, 7, 102.0 }, { 1, 7, 102.0 }, +- { 0, 8, 103.0 }, { 1, 8, 103.0 }, +- { 0, 9, 104.0 }, { 1, 9, 104.0 }, +- }; +- +- for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i ) +- { +- double val = m_pDoc->GetValue( aEntries[i].nCol, aEntries[i].nRow, 0 ); +-// fprintf(stderr, "value at %d %d is %g = %g\n", +-// (int)aResults[i].nRow, (int)aResults[i].nCol, +-// val, aResults[i].fValue); +- CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.", +- rtl::math::approxEqual(val, aResults[i].fValue)); +- } +- +- m_pDoc->DeleteTab( 0 ); +-} +- +-void Test::testSortWithCellFormats() +-{ +- struct +- { +- bool isBold( const ScPatternAttr* pPat ) const +- { +- if (!pPat) +- { +- cerr << "Pattern is NULL!" << endl; +- return false; +- } +- +- const SfxPoolItem* pItem = NULL; +- if (!pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem)) +- { +- cerr << "Pattern does not have a font weight item, but it should." << endl; +- return false; +- } +- +- if (static_cast(pItem)->GetEnumValue() != WEIGHT_BOLD) +- { +- cerr << "Font weight should be bold." << endl; +- return false; +- } +- +- return true; +- } +- +- bool isItalic( const ScPatternAttr* pPat ) const +- { +- if (!pPat) +- { +- cerr << "Pattern is NULL!" << endl; +- return false; +- } +- +- const SfxPoolItem* pItem = NULL; +- if (!pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem)) +- { +- cerr << "Pattern does not have a font posture item, but it should." << endl; +- return false; +- } +- +- if (static_cast(pItem)->GetEnumValue() != ITALIC_NORMAL) +- { +- cerr << "Italic should be applied.." << endl; +- return false; +- } +- +- return true; +- } +- +- bool isNormal( const ScPatternAttr* pPat ) const +- { +- if (!pPat) +- { +- cerr << "Pattern is NULL!" << endl; +- return false; +- } +- +- const SfxPoolItem* pItem = NULL; +- if (pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT)) +- { +- // Check if the font weight is applied. +- if (static_cast(pItem)->GetEnumValue() == WEIGHT_BOLD) +- { +- cerr << "This cell is bold, but shouldn't." << endl; +- return false; +- } +- } +- +- if (pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE)) +- { +- // Check if the italics is applied. +- if (static_cast(pItem)->GetEnumValue() == ITALIC_NORMAL) +- { +- cerr << "This cell is bold, but shouldn't." << endl; +- return false; +- } +- } +- +- return true; +- } +- +- } aCheck; +- +- m_pDoc->InsertTab(0, "Test"); +- +- // Insert some values into A1:A4. +- m_pDoc->SetString(ScAddress(0,0,0), "Header"); +- m_pDoc->SetString(ScAddress(0,1,0), "Normal"); +- m_pDoc->SetString(ScAddress(0,2,0), "Bold"); +- m_pDoc->SetString(ScAddress(0,3,0), "Italic"); +- +- // Set A3 bold and A4 italic. +- const ScPatternAttr* pPat = m_pDoc->GetPattern(ScAddress(0,2,0)); +- CPPUNIT_ASSERT(pPat); +- { +- ScPatternAttr aNewPat(*pPat); +- SfxItemSet& rSet = aNewPat.GetItemSet(); +- rSet.Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT)); +- m_pDoc->ApplyPattern(0, 2, 0, aNewPat); +- +- // Make sure it's really in. +- bool bGood = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,2,0))); +- CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood); +- } +- +- pPat = m_pDoc->GetPattern(ScAddress(0,3,0)); +- CPPUNIT_ASSERT(pPat); +- { +- ScPatternAttr aNewPat(*pPat); +- SfxItemSet& rSet = aNewPat.GetItemSet(); +- rSet.Put(SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE)); +- m_pDoc->ApplyPattern(0, 3, 0, aNewPat); +- +- bool bGood = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,3,0))); +- CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood); +- } +- +- // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run. +- m_pDoc->SetAnonymousDBData( +- 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 3)); +- +- // Sort A1:A4 ascending with cell formats. +- ScDBDocFunc aFunc(getDocShell()); +- +- ScSortParam aSortData; +- aSortData.nCol1 = 0; +- aSortData.nCol2 = 0; +- aSortData.nRow1 = 0; +- aSortData.nRow2 = 3; +- aSortData.bHasHeader = true; +- aSortData.bIncludePattern = true; +- aSortData.maKeyState[0].bDoSort = true; +- aSortData.maKeyState[0].nField = 0; +- aSortData.maKeyState[0].bAscending = true; +- bool bSorted = aFunc.Sort(0, aSortData, true, false, true); +- CPPUNIT_ASSERT(bSorted); +- +- // Check the sort result. +- CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Bold"), m_pDoc->GetString(ScAddress(0,1,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Italic"), m_pDoc->GetString(ScAddress(0,2,0))); +- CPPUNIT_ASSERT_EQUAL(OUString("Normal"), m_pDoc->GetString(ScAddress(0,3,0))); +- +- // A2 should be bold now. +- bool bBold = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,1,0))); +- CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold); +- +- // and A3 should be italic. +- bool bItalic = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,2,0))); +- CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic); +- +- // A4 should have neither bold nor italic. +- bool bNormal = aCheck.isNormal(m_pDoc->GetPattern(ScAddress(0,3,0))); +- CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal); +- +- m_pDoc->DeleteTab(0); +-} +- + void Test::testShiftCells() + { + m_pDoc->InsertTab(0, "foo"); +diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx +index 39862ee..a1688f7 100644 +--- a/sc/qa/unit/ucalc.hxx ++++ b/sc/qa/unit/ucalc.hxx +@@ -338,13 +338,23 @@ public: + + void testFindAreaPosVertical(); + void testFindAreaPosColRight(); ++ void testShiftCells(); ++ + void testSort(); + void testSortHorizontal(); ++ void testSortHorizontalWholeColumn(); ++ void testSortSingleRow(); + void testSortWithFormulaRefs(); + void testSortWithStrings(); + void testSortInFormulaGroup(); + void testSortWithCellFormats(); +- void testShiftCells(); ++ void testSortRefUpdate(); ++ void testSortRefUpdate2(); ++ void testSortRefUpdate3(); ++ void testSortRefUpdate4(); ++ void testSortRefUpdate5(); ++ void testSortOutOfPlaceResult(); ++ void testSortPartialFormulaGroup(); + + void testNoteBasic(); + void testNoteDeleteRow(); +@@ -507,10 +517,19 @@ public: + CPPUNIT_TEST(testFindAreaPosColRight); + CPPUNIT_TEST(testSort); + CPPUNIT_TEST(testSortHorizontal); ++ CPPUNIT_TEST(testSortHorizontalWholeColumn); ++ CPPUNIT_TEST(testSortSingleRow); + CPPUNIT_TEST(testSortWithFormulaRefs); + CPPUNIT_TEST(testSortWithStrings); + CPPUNIT_TEST(testSortInFormulaGroup); + CPPUNIT_TEST(testSortWithCellFormats); ++ CPPUNIT_TEST(testSortRefUpdate); ++ CPPUNIT_TEST(testSortRefUpdate2); ++ CPPUNIT_TEST(testSortRefUpdate3); ++ CPPUNIT_TEST(testSortRefUpdate4); ++ CPPUNIT_TEST(testSortRefUpdate5); ++ CPPUNIT_TEST(testSortOutOfPlaceResult); ++ CPPUNIT_TEST(testSortPartialFormulaGroup); + CPPUNIT_TEST(testShiftCells); + CPPUNIT_TEST(testNoteBasic); + CPPUNIT_TEST(testNoteDeleteRow); +diff --git a/sc/qa/unit/ucalc_sort.cxx b/sc/qa/unit/ucalc_sort.cxx +new file mode 100644 +index 0000000..f81a394 +--- /dev/null ++++ b/sc/qa/unit/ucalc_sort.cxx +@@ -0,0 +1,1464 @@ ++/* -*- 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 "ucalc.hxx" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++void Test::testSort() ++{ ++ m_pDoc->InsertTab(0, "test1"); ++ ++ ScRange aDataRange; ++ ScAddress aPos(0,0,0); ++ { ++ const char* aData[][2] = { ++ { "2", "4" }, ++ { "4", "1" }, ++ { "1", "2" }, ++ { "1", "23" }, ++ }; ++ ++ clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0)); ++ aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos); ++ } ++ ++ // Insert note in cell B2. ++ ScAddress rAddr(1, 1, 0); ++ ScPostIt* pNote = m_pDoc->GetOrCreateNote(rAddr); ++ pNote->SetText(rAddr, "Hello"); ++ pNote->SetAuthor("Jim Bob"); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 1; ++ aSortData.nCol2 = 1; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 2; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 1; ++ aSortData.maKeyState[0].bAscending = true; ++ ++ m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); ++ ++ double nVal = m_pDoc->GetValue(1,0,0); ++ ASSERT_DOUBLES_EQUAL(nVal, 1.0); ++ ++ // check that note is also moved after sorting ++ pNote = m_pDoc->GetNote(1, 0, 0); ++ CPPUNIT_ASSERT(pNote); ++ ++ clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10. ++ { ++ // 0 = empty cell ++ const char* aData[][1] = { ++ { "Title" }, ++ { 0 }, ++ { 0 }, ++ { "12" }, ++ { "b" }, ++ { "1" }, ++ { "9" }, ++ { "123" } ++ }; ++ ++ aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos); ++ } ++ ++ aSortData.nCol1 = aDataRange.aStart.Col(); ++ aSortData.nCol2 = aDataRange.aEnd.Col(); ++ aSortData.nRow1 = aDataRange.aStart.Row(); ++ aSortData.nRow2 = aDataRange.aEnd.Row(); ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].nField = 0; ++ m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); ++ ++ // Title should stay at the top, numbers should be sorted numerically, ++ // numbers always come before strings, and empty cells always occur at the ++ // end. ++ CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos)); ++ aPos.IncRow(); ++ CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos)); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortHorizontal() ++{ ++ ScFormulaOptions aOptions; ++ aOptions.SetFormulaSepArg(";"); ++ aOptions.SetFormulaSepArrayCol(";"); ++ aOptions.SetFormulaSepArrayRow("|"); ++ getDocShell().SetFormulaOptions(aOptions); ++ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ // Test case from fdo#78079. ++ ++ // 0 = empty cell ++ const char* aData[][4] = { ++ { "table", "has UNIQUE", "Publish to EC2", "flag" }, ++ { "w2gi.mobilehit", "Yes", "No", "=CONCATENATE(B2;\"-\";C2)" }, ++ { "w2gi.visitors", "No", "No", "=CONCATENATE(B3;\"-\";C3)" }, ++ { "w2gi.pagedimension", "Yes", "Yes", "=CONCATENATE(B4;\"-\";C4)" }, ++ }; ++ ++ // Insert raw data into A1:D4. ++ ScRange aDataRange = insertRangeData(m_pDoc, ScAddress(0,0,0), aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_EQUAL(OUString("A1:D4"), aDataRange.Format(SCA_VALID)); ++ ++ // Check the formula values. ++ CPPUNIT_ASSERT_EQUAL(OUString("Yes-No"), m_pDoc->GetString(ScAddress(3,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("No-No"), m_pDoc->GetString(ScAddress(3,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Yes-Yes"), m_pDoc->GetString(ScAddress(3,3,0))); ++ ++ // Define A1:D4 as sheet-local anonymous database range. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 3, 3)); ++ ++ // Sort A1:D4 horizontally, ascending by row 1. ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 3; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 3; ++ aSortData.bHasHeader = true; ++ aSortData.bByRow = false; // Sort by column (in horizontal direction). ++ aSortData.bIncludePattern = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ { ++ // Expected output table content. 0 = empty cell ++ const char* aOutputCheck[][4] = { ++ { "table", "flag", "has UNIQUE", "Publish to EC2" }, ++ { "w2gi.mobilehit", "Yes-No", "Yes", "No" }, ++ { "w2gi.visitors", "No-No", "No", "No" }, ++ { "w2gi.pagedimension", "Yes-Yes", "Yes", "Yes" }, ++ }; ++ ++ bool bSuccess = checkOutput<4>(m_pDoc, aDataRange, aOutputCheck, "Sorted by column with formula"); ++ CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess); ++ } ++ ++ if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "CONCATENATE(C2;\"-\";D2)")) ++ CPPUNIT_FAIL("Wrong formula!"); ++ if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "CONCATENATE(C3;\"-\";D3)")) ++ CPPUNIT_FAIL("Wrong formula!"); ++ if (!checkFormula(*m_pDoc, ScAddress(1,3,0), "CONCATENATE(C4;\"-\";D4)")) ++ CPPUNIT_FAIL("Wrong formula!"); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortHorizontalWholeColumn() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ // 0 = empty cell ++ const char* aData[][5] = { ++ { "4", "2", "47", "a", "9" } ++ }; ++ ++ // Insert row data to C1:G1. ++ ScRange aSortRange = insertRangeData(m_pDoc, ScAddress(2,0,0), aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc->GetString(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(6,0,0))); ++ ++ // Extend the sort range to whole column. ++ aSortRange.aEnd.SetRow(MAXROW); ++ ++ SCCOL nCol1 = aSortRange.aStart.Col(); ++ SCCOL nCol2 = aSortRange.aEnd.Col(); ++ SCROW nRow1 = aSortRange.aStart.Row(); ++ SCROW nRow2 = aSortRange.aEnd.Row(); ++ ++ // Define C:G as sheet-local anonymous database range. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, nCol1, nRow1, nCol2, nRow2, false, false)); ++ ++ // Sort C:G horizontally ascending by row 1. ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = nCol1; ++ aSortData.nCol2 = nCol2; ++ aSortData.nRow1 = nRow1; ++ aSortData.nRow2 = nRow2; ++ aSortData.bHasHeader = false; ++ aSortData.bByRow = false; // Sort by column (in horizontal direction). ++ aSortData.bIncludePattern = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sort result. ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc->GetString(ScAddress(6,0,0))); ++ ++ // Undo and check. ++ ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ CPPUNIT_ASSERT(pUndoMgr); ++ ++ pUndoMgr->Undo(); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc->GetString(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(6,0,0))); ++ ++ // Redo and check. ++ pUndoMgr->Redo(); ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("a"), m_pDoc->GetString(ScAddress(6,0,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortSingleRow() ++{ ++ // This test case is from fdo#80462. ++ ++ m_pDoc->InsertTab(0, "Test"); ++ ++ // Sort range consists of only one row. ++ m_pDoc->SetString(ScAddress(0,0,0), "X"); ++ m_pDoc->SetString(ScAddress(1,0,0), "Y"); ++ ++ // Define A1:B1 as sheet-local anonymous database range. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 1, 0)); ++ ++ // Sort A1:B1 horizontally, ascending by row 1. ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 1; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 0; ++ aSortData.bHasHeader = true; ++ aSortData.bByRow = true; ++ aSortData.bIncludePattern = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ ++ // Do the sorting. This should not crash. ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Another test case - single row horizontal sort with header column. ++ clearSheet(m_pDoc, 0); ++ ++ // A1:G1 ++ m_pDoc->SetString(ScAddress(0,0,0), "Header"); ++ m_pDoc->SetValue(ScAddress(1,0,0), 1.0); ++ m_pDoc->SetValue(ScAddress(2,0,0), 10.0); ++ m_pDoc->SetValue(ScAddress(3,0,0), 3.0); ++ m_pDoc->SetValue(ScAddress(4,0,0), 9.0); ++ m_pDoc->SetValue(ScAddress(5,0,0), 12.0); ++ m_pDoc->SetValue(ScAddress(6,0,0), 2.0); ++ ++ // Define A1:G1 as sheet-local anonymous database range. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 6, 0, false, true)); ++ ++ // Update the sort data. ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 6; ++ aSortData.bByRow = false; ++ bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the result. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(6,0,0))); ++ ++ // Undo and check. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ CPPUNIT_ASSERT(pUndoMgr); ++ pUndoMgr->Undo(); ++ ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0))); ++ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(6,0,0))); ++ ++ // Redo and check. ++ pUndoMgr->Redo(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(1,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(4,0,0))); ++ CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(5,0,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(6,0,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++// regression test fo fdo#53814, sorting doens't work as expected ++// if cells in the sort are referenced by formulas ++void Test::testSortWithFormulaRefs() ++{ ++ m_pDoc->InsertTab(0, "List1"); ++ m_pDoc->InsertTab(1, "List2"); ++ ++ const char* aFormulaData[6] = { ++ "=IF($List1.A2<>\"\";$List1.A2;\"\")", ++ "=IF($List1.A3<>\"\";$List1.A3;\"\")", ++ "=IF($List1.A4<>\"\";$List1.A4;\"\")", ++ "=IF($List1.A5<>\"\";$List1.A5;\"\")", ++ "=IF($List1.A6<>\"\";$List1.A6;\"\")", ++ "=IF($List1.A7<>\"\";$List1.A7;\"\")", ++ }; ++ ++ const char* aTextData[4] = { ++ "bob", ++ "tim", ++ "brian", ++ "larry", ++ }; ++ ++ const char* aResults[6] = { ++ "bob", ++ "brian", ++ "larry", ++ "tim", ++ "", ++ "", ++ }; ++ ++ // Insert data to sort in A2:A5 on the 1st sheet. ++ for (SCROW i = 1; i <= 4; ++i) ++ m_pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) ); ++ ++ // Insert forumulas in A1:A6 on the 2nd sheet. ++ for (size_t i = 0; i < SAL_N_ELEMENTS(aFormulaData); ++i) ++ m_pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) ); ++ ++ // Sort data in A2:A8 on the 1st sheet. No column header. ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 0; ++ aSortData.nRow1 = 1; ++ aSortData.nRow2 = 7; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ ++ m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); ++ ++ for (size_t i = 0; i < SAL_N_ELEMENTS(aResults); ++i) ++ { ++ OUString sResult = m_pDoc->GetString(0, i + 1, 0); ++ CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[i] ), sResult ); ++ } ++ m_pDoc->DeleteTab(1); ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortWithStrings() ++{ ++ m_pDoc->InsertTab(0, "Test"); ++ ++ ScFieldEditEngine& rEE = m_pDoc->GetEditEngine(); ++ rEE.SetText("Val1"); ++ m_pDoc->SetString(ScAddress(1,1,0), "Header"); ++ m_pDoc->SetString(ScAddress(1,2,0), "Val2"); ++ m_pDoc->SetEditText(ScAddress(1,3,0), rEE.CreateTextObject()); ++ ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0))); ++ ++ ScSortParam aParam; ++ aParam.nCol1 = 1; ++ aParam.nCol2 = 1; ++ aParam.nRow1 = 1; ++ aParam.nRow2 = 3; ++ aParam.bHasHeader = true; ++ aParam.maKeyState[0].bDoSort = true; ++ aParam.maKeyState[0].bAscending = true; ++ aParam.maKeyState[0].nField = 1; ++ ++ m_pDoc->Sort(0, aParam, false, true, NULL, NULL); ++ ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,3,0))); ++ ++ aParam.maKeyState[0].bAscending = false; ++ ++ m_pDoc->Sort(0, aParam, false, true, NULL, NULL); ++ ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val2"), m_pDoc->GetString(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Val1"), m_pDoc->GetString(ScAddress(1,3,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortInFormulaGroup() ++{ ++ static struct { ++ SCCOL nCol; ++ SCROW nRow; ++ const char *pData; ++ } aEntries[] = { ++ { 0, 0, "3" }, { 1, 0, "=A1" }, ++ { 0, 1, "1" }, { 1, 1, "=A2" }, ++ { 0, 2, "20" }, { 1, 2, "=A3" }, ++ { 0, 3, "10" }, { 1, 3, "=A4+1" }, // swap across groups ++ { 0, 4, "2" }, { 1, 4, "=A5+1" }, ++ { 0, 5, "101" }, { 1, 5, "=A6" }, // swap inside contiguious group ++ { 0, 6, "100" }, { 1, 6, "=A7" }, ++ { 0, 7, "102" }, { 1, 7, "=A8" }, ++ { 0, 8, "104" }, { 1, 8, "=A9" }, ++ { 0, 9, "103" }, { 1, 9, "=A10" }, ++ }; ++ ++ m_pDoc->InsertTab(0, "sorttest"); ++ ++ for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i ) ++ m_pDoc->SetString( aEntries[i].nCol, aEntries[i].nRow, 0, ++ OUString::createFromAscii( aEntries[i].pData) ); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 1; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 9; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ ++ m_pDoc->Sort(0, aSortData, false, true, NULL, NULL); ++ ++ static struct { ++ SCCOL nCol; ++ SCROW nRow; ++ double fValue; ++ } aResults[] = { ++ { 0, 0, 1.0 }, { 1, 0, 1.0 }, ++ { 0, 1, 2.0 }, { 1, 1, 3.0 }, ++ { 0, 2, 3.0 }, { 1, 2, 3.0 }, ++ { 0, 3, 10.0 }, { 1, 3, 11.0 }, ++ { 0, 4, 20.0 }, { 1, 4, 20.0 }, ++ { 0, 5, 100.0 }, { 1, 5, 100.0 }, ++ { 0, 6, 101.0 }, { 1, 6, 101.0 }, ++ { 0, 7, 102.0 }, { 1, 7, 102.0 }, ++ { 0, 8, 103.0 }, { 1, 8, 103.0 }, ++ { 0, 9, 104.0 }, { 1, 9, 104.0 }, ++ }; ++ ++ for ( SCROW i = 0; i < (SCROW) SAL_N_ELEMENTS( aEntries ); ++i ) ++ { ++ double val = m_pDoc->GetValue( aEntries[i].nCol, aEntries[i].nRow, 0 ); ++// fprintf(stderr, "value at %d %d is %g = %g\n", ++// (int)aResults[i].nRow, (int)aResults[i].nCol, ++// val, aResults[i].fValue); ++ CPPUNIT_ASSERT_MESSAGE("Mis-matching value after sort.", ++ rtl::math::approxEqual(val, aResults[i].fValue)); ++ } ++ ++ m_pDoc->DeleteTab( 0 ); ++} ++ ++void Test::testSortWithCellFormats() ++{ ++ struct ++ { ++ bool isBold( const ScPatternAttr* pPat ) const ++ { ++ if (!pPat) ++ { ++ cerr << "Pattern is NULL!" << endl; ++ return false; ++ } ++ ++ const SfxPoolItem* pItem = NULL; ++ if (!pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem)) ++ { ++ cerr << "Pattern does not have a font weight item, but it should." << endl; ++ return false; ++ } ++ ++ CPPUNIT_ASSERT(pItem); ++ ++ if (static_cast(pItem)->GetEnumValue() != WEIGHT_BOLD) ++ { ++ cerr << "Font weight should be bold." << endl; ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool isItalic( const ScPatternAttr* pPat ) const ++ { ++ if (!pPat) ++ { ++ cerr << "Pattern is NULL!" << endl; ++ return false; ++ } ++ ++ const SfxPoolItem* pItem = NULL; ++ if (!pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem)) ++ { ++ cerr << "Pattern does not have a font posture item, but it should." << endl; ++ return false; ++ } ++ ++ CPPUNIT_ASSERT(pItem); ++ ++ if (static_cast(pItem)->GetEnumValue() != ITALIC_NORMAL) ++ { ++ cerr << "Italic should be applied.." << endl; ++ return false; ++ } ++ ++ return true; ++ } ++ ++ bool isNormal( const ScPatternAttr* pPat ) const ++ { ++ if (!pPat) ++ { ++ cerr << "Pattern is NULL!" << endl; ++ return false; ++ } ++ ++ const SfxPoolItem* pItem = NULL; ++ if (pPat->GetItemSet().HasItem(ATTR_FONT_WEIGHT, &pItem)) ++ { ++ // Check if the font weight is applied. ++ if (static_cast(pItem)->GetEnumValue() == WEIGHT_BOLD) ++ { ++ cerr << "This cell is bold, but shouldn't." << endl; ++ return false; ++ } ++ } ++ ++ if (pPat->GetItemSet().HasItem(ATTR_FONT_POSTURE, &pItem)) ++ { ++ // Check if the italics is applied. ++ if (static_cast(pItem)->GetEnumValue() == ITALIC_NORMAL) ++ { ++ cerr << "This cell is bold, but shouldn't." << endl; ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ } aCheck; ++ ++ m_pDoc->InsertTab(0, "Test"); ++ ++ // Insert some values into A1:A4. ++ m_pDoc->SetString(ScAddress(0,0,0), "Header"); ++ m_pDoc->SetString(ScAddress(0,1,0), "Normal"); ++ m_pDoc->SetString(ScAddress(0,2,0), "Bold"); ++ m_pDoc->SetString(ScAddress(0,3,0), "Italic"); ++ ++ // Set A3 bold and A4 italic. ++ const ScPatternAttr* pPat = m_pDoc->GetPattern(ScAddress(0,2,0)); ++ CPPUNIT_ASSERT(pPat); ++ { ++ ScPatternAttr aNewPat(*pPat); ++ SfxItemSet& rSet = aNewPat.GetItemSet(); ++ rSet.Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT)); ++ m_pDoc->ApplyPattern(0, 2, 0, aNewPat); ++ ++ // Make sure it's really in. ++ bool bGood = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_MESSAGE("A3 is not bold but it should.", bGood); ++ } ++ ++ pPat = m_pDoc->GetPattern(ScAddress(0,3,0)); ++ CPPUNIT_ASSERT(pPat); ++ { ++ ScPatternAttr aNewPat(*pPat); ++ SfxItemSet& rSet = aNewPat.GetItemSet(); ++ rSet.Put(SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE)); ++ m_pDoc->ApplyPattern(0, 3, 0, aNewPat); ++ ++ bool bGood = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_MESSAGE("A4 is not italic but it should.", bGood); ++ } ++ ++ // Define A1:A4 as sheet-local anonymous database range, else sort wouldn't run. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 3)); ++ ++ // Sort A1:A4 ascending with cell formats. ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 0; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 3; ++ aSortData.bHasHeader = true; ++ aSortData.bIncludePattern = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, false, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sort result. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Bold"), m_pDoc->GetString(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Italic"), m_pDoc->GetString(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Normal"), m_pDoc->GetString(ScAddress(0,3,0))); ++ ++ // A2 should be bold now. ++ bool bBold = aCheck.isBold(m_pDoc->GetPattern(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_MESSAGE("A2 should be bold after the sort.", bBold); ++ ++ // and A3 should be italic. ++ bool bItalic = aCheck.isItalic(m_pDoc->GetPattern(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_MESSAGE("A3 should be italic.", bItalic); ++ ++ // A4 should have neither bold nor italic. ++ bool bNormal = aCheck.isNormal(m_pDoc->GetPattern(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_MESSAGE("A4 should be neither bold nor italic.", bNormal); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortRefUpdate() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); ++ ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ // Set values to sort in column A. ++ m_pDoc->SetString(ScAddress(0,0,0), "Header"); ++ ++ double aValues[] = { 4.0, 36.0, 14.0, 29.0, 98.0, 78.0, 0.0, 99.0, 1.0 }; ++ size_t nCount = SAL_N_ELEMENTS(aValues); ++ for (size_t i = 0; i < nCount; ++i) ++ m_pDoc->SetValue(ScAddress(0,i+1,0), aValues[i]); ++ ++ // Set formulas to reference these values in column C. ++ m_pDoc->SetString(ScAddress(2,0,0), "Formula"); ++ for (size_t i = 0; i < nCount; ++i) ++ m_pDoc->SetString(ScAddress(2,1+i,0), "=RC[-2]"); ++ ++ // Check the values in column C. ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aValues[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); ++ } ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Define A1:A10 as sheet-local anonymous database range, else sort wouldn't run. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 9)); ++ ++ // Sort A1:A10 (with a header row). ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 0; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 9; ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ double aSorted[] = { 0.0, 1.0, 4.0, 14.0, 29.0, 36.0, 78.0, 98.0, 99.0 }; ++ ++ // Check the sort result. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aSorted[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0))); ++ } ++ ++ // Sorting should not alter the values in column C. ++ m_pDoc->CalcAll(); // just in case... ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aValues[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); ++ } ++ ++ // C2 should now point to A4. ++ if (!checkFormula(*m_pDoc, ScAddress(2,1,0), "R[2]C[-2]")) ++ CPPUNIT_FAIL("Wrong formula in C2!"); ++ ++ // Undo the sort. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ pUndoMgr->Undo(); ++ ++ // Check the undo result. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aValues[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0))); ++ } ++ ++ // Values in column C should still be unaltered. ++ m_pDoc->CalcAll(); // just in case... ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aValues[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); ++ } ++ ++ // C2 should now point to A2. ++ if (!checkFormula(*m_pDoc, ScAddress(2,1,0), "RC[-2]")) ++ CPPUNIT_FAIL("Wrong formula in C2!"); ++ ++ // Redo. ++ pUndoMgr->Redo(); ++ ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aSorted[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0))); ++ } ++ ++ // Sorting should not alter the values in column C. ++ m_pDoc->CalcAll(); // just in case... ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aValues[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); ++ } ++ ++ // C2 should now point to A4. ++ if (!checkFormula(*m_pDoc, ScAddress(2,1,0), "R[2]C[-2]")) ++ CPPUNIT_FAIL("Wrong formula in C2!"); ++ ++ // Undo again. ++ pUndoMgr->Undo(); ++ ++ // Formulas in column C should all be "RC[-2]" again. ++ for (size_t i = 0; i < nCount; ++i) ++ m_pDoc->SetString(ScAddress(2,1+i,0), "=RC[-2]"); ++ ++ // Turn off reference update on sort. ++ ScInputOptions aInputOption = SC_MOD()->GetInputOptions(); ++ aInputOption.SetSortRefUpdate(false); ++ ++ bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sort result again. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aSorted[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(0,i+1,0))); ++ } ++ ++ // Formulas in column C should all remain "RC[-2]". ++ for (size_t i = 0; i < nCount; ++i) ++ m_pDoc->SetString(ScAddress(2,1+i,0), "=RC[-2]"); ++ ++ // The values in column C should now be the same as sorted values in column A. ++ m_pDoc->CalcAll(); // just in case... ++ for (size_t i = 0; i < nCount; ++i) ++ { ++ double fCheck = aSorted[i]; ++ CPPUNIT_ASSERT_EQUAL(fCheck, m_pDoc->GetValue(ScAddress(2,i+1,0))); // column C ++ } ++ ++ // Turn it back on. ++ aInputOption.SetSortRefUpdate(true); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortRefUpdate2() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); ++ ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ // Set up the sheet. ++ const char* aData[][2] = { ++ { "F1", "F2" }, ++ { "9", "=RC[-1]" }, ++ { "2", "=RC[-1]" }, ++ { "6", "=RC[-1]" }, ++ { "4", "=RC[-1]" }, ++ { 0, 0 } // terminator ++ }; ++ ++ for (SCROW i = 0; aData[i][0]; ++i) ++ { ++ m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0])); ++ m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1])); ++ } ++ ++ // Check the values in B2:B5. ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,4,0))); ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Define A1:B5 as sheet-local anonymous database range, else sort wouldn't run. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 1, 4)); ++ ++ // Sort A1:B5 by column A (with a row header). ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 1; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 4; ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sort result in column A. ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ ++ // and column B. ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,4,0))); ++ ++ // Formulas in column B should still point to their respective left neighbor cell. ++ for (SCROW i = 1; i <= 4; ++i) ++ { ++ if (!checkFormula(*m_pDoc, ScAddress(1,i,0), "RC[-1]")) ++ CPPUNIT_FAIL("Wrong formula!"); ++ } ++ ++ // Undo and check the result in column B. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ pUndoMgr->Undo(); ++ ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,4,0))); ++ ++ // and redo. ++ pUndoMgr->Redo(); ++ ++ CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0))); ++ CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(1,4,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortRefUpdate3() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ const char* pData[] = { ++ "Header", ++ "1", ++ "=A2+10", ++ "2", ++ "=A4+10", ++ "=A2+A4", ++ 0 // terminator ++ }; ++ ++ for (SCROW i = 0; pData[i]; ++i) ++ m_pDoc->SetString(ScAddress(0,i,0), OUString::createFromAscii(pData[i])); ++ ++ // Check the initial values. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Sort A1:A6. ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 5)); ++ ++ // Sort A1:A6 by column A (with a row header). ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 0; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 5; ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sorted values. ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ // Make sure the formula cells have been adjusted correctly. ++ if (!checkFormula(*m_pDoc, ScAddress(0,3,0), "A2+A3")) ++ CPPUNIT_FAIL("Wrong formula in A4."); ++ if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "A2+10")) ++ CPPUNIT_FAIL("Wrong formula in A5."); ++ if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "A3+10")) ++ CPPUNIT_FAIL("Wrong formula in A6."); ++ ++ // Undo and check the result. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ pUndoMgr->Undo(); ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ // Redo and check the result. ++ pUndoMgr->Redo(); ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++// Derived from fdo#79441 https://bugs.freedesktop.org/attachment.cgi?id=100144 ++// testRefInterne.ods ++void Test::testSortRefUpdate4() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ m_pDoc->InsertTab(0, "Sort"); ++ m_pDoc->InsertTab(1, "Lesson1"); ++ m_pDoc->InsertTab(2, "Lesson2"); ++ ++ ScRange aLesson1Range; ++ { ++ const char* aData[][2] = { ++ { "Name", "Note" }, ++ { "Student1", "1" }, ++ { "Student2", "2" }, ++ { "Student3", "3" }, ++ { "Student4", "4" }, ++ { "Student5", "5" }, ++ }; ++ ++ SCTAB nTab = 1; ++ ScAddress aPos(0,0,nTab); ++ clearRange(m_pDoc, ScRange(0, 0, nTab, 1, SAL_N_ELEMENTS(aData), nTab)); ++ aLesson1Range = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aLesson1Range.aStart == aPos); ++ } ++ ++ ScRange aLesson2Range; ++ { ++ const char* aData[][2] = { ++ { "Name", "Note" }, ++ { "=Lesson1.A2", "3" }, ++ { "=Lesson1.A3", "4" }, ++ { "=Lesson1.A4", "9" }, ++ { "=Lesson1.A5", "6" }, ++ { "=Lesson1.A6", "3" }, ++ }; ++ ++ SCTAB nTab = 2; ++ ScAddress aPos(0,0,nTab); ++ clearRange(m_pDoc, ScRange(0, 0, nTab, 1, SAL_N_ELEMENTS(aData), nTab)); ++ aLesson2Range = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aLesson2Range.aStart == aPos); ++ } ++ ++ ScRange aSortRange; ++ { ++ const char* aData[][4] = { ++ { "Name", "Lesson1", "Lesson2", "Average" }, ++ { "=Lesson1.A2", "=Lesson1.B2", "=Lesson2.B2", "=AVERAGE(B2:C2)" }, ++ { "=Lesson1.A3", "=Lesson1.B3", "=Lesson2.B3", "=AVERAGE(B3:C3)" }, ++ { "=Lesson1.A4", "=Lesson1.B4", "=Lesson2.B4", "=AVERAGE(B4:C4)" }, ++ { "=Lesson1.A5", "=Lesson1.B5", "=Lesson2.B5", "=AVERAGE(B5:C5)" }, ++ { "=Lesson1.A6", "=Lesson1.B6", "=Lesson2.B6", "=AVERAGE(B6:C6)" }, ++ }; ++ ++ SCTAB nTab = 0; ++ ScAddress aPos(0,0,nTab); ++ clearRange(m_pDoc, ScRange(0, 0, nTab, 1, SAL_N_ELEMENTS(aData), nTab)); ++ aSortRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aSortRange.aStart == aPos); ++ } ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Sort A1:D6 by column D (Average, with a row header). ++ { ++ ScSortParam aSortData; ++ aSortData.nCol1 = aSortRange.aStart.Col(); ++ aSortData.nCol2 = aSortRange.aEnd.Col(); ++ aSortData.nRow1 = aSortRange.aStart.Row(); ++ aSortData.nRow2 = aSortRange.aEnd.Row(); ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].bDoSort = true; // sort on ++ aSortData.maKeyState[0].nField = 3; // Average ++ aSortData.maKeyState[0].bAscending = false; // descending ++ ++ m_pDoc->SetAnonymousDBData( 0, new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(), ++ aSortData.nCol1, aSortData.nRow1, aSortData.nCol2, aSortData.nRow2)); ++ ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sorted values. ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc->GetString(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc->GetString(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc->GetString(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc->GetString(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc->GetString(ScAddress(0,5,0))); ++ CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0))); ++ ++ // Make sure the formula cells have been adjusted correctly. ++ const char* aCheck[][4] = { ++ // Name Lesson1 Lesson2 Average ++ { "Lesson1.A4", "Lesson1.B4", "Lesson2.B4", "AVERAGE(B2:C2)" }, ++ { "Lesson1.A5", "Lesson1.B5", "Lesson2.B5", "AVERAGE(B3:C3)" }, ++ { "Lesson1.A6", "Lesson1.B6", "Lesson2.B6", "AVERAGE(B4:C4)" }, ++ { "Lesson1.A3", "Lesson1.B3", "Lesson2.B3", "AVERAGE(B5:C5)" }, ++ { "Lesson1.A2", "Lesson1.B2", "Lesson2.B2", "AVERAGE(B6:C6)" }, ++ }; ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aCheck)); ++nRow) ++ { ++ for (SCCOL nCol=0; nCol < 4; ++nCol) ++ { ++ if (!checkFormula(*m_pDoc, ScAddress(nCol,nRow+1,0), aCheck[nRow][nCol])) ++ CPPUNIT_FAIL(OString("Wrong formula in " + OString('A'+nCol) + OString::number(nRow+2) + ".").getStr()); ++ } ++ } ++ ++ // Undo and check the result. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ pUndoMgr->Undo(); ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,5,0))); ++ ++ // Redo and check the result. ++ pUndoMgr->Redo(); ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0))); ++ } ++ ++ // Sort A2:AMJ6 by column A (Name, without header). ++ { ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = MAXCOL; ++ aSortData.nRow1 = aSortRange.aStart.Row()+1; ++ aSortData.nRow2 = aSortRange.aEnd.Row(); ++ aSortData.bHasHeader = false; ++ aSortData.maKeyState[0].bDoSort = true; // sort on ++ aSortData.maKeyState[0].nField = 0; // Name ++ aSortData.maKeyState[0].bAscending = false; // descending ++ ++ m_pDoc->SetAnonymousDBData( 0, new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(), ++ aSortData.nCol1, aSortData.nRow1, aSortData.nCol2, aSortData.nRow2)); ++ ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sorted values. ++ m_pDoc->CalcAll(); ++ CPPUNIT_ASSERT_EQUAL(OUString("Name"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student5"), m_pDoc->GetString(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student4"), m_pDoc->GetString(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student3"), m_pDoc->GetString(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student2"), m_pDoc->GetString(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(OUString("Student1"), m_pDoc->GetString(ScAddress(0,5,0))); ++ CPPUNIT_ASSERT_EQUAL( 4.0, m_pDoc->GetValue(ScAddress(3,1,0))); ++ CPPUNIT_ASSERT_EQUAL( 5.0, m_pDoc->GetValue(ScAddress(3,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 6.0, m_pDoc->GetValue(ScAddress(3,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(3,4,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(3,5,0))); ++ ++ // Make sure the formula cells have been adjusted correctly. ++ const char* aCheck[][4] = { ++ // Name Lesson1 Lesson2 Average ++ { "Lesson1.A6", "Lesson1.B6", "Lesson2.B6", "AVERAGE(B2:C2)" }, ++ { "Lesson1.A5", "Lesson1.B5", "Lesson2.B5", "AVERAGE(B3:C3)" }, ++ { "Lesson1.A4", "Lesson1.B4", "Lesson2.B4", "AVERAGE(B4:C4)" }, ++ { "Lesson1.A3", "Lesson1.B3", "Lesson2.B3", "AVERAGE(B5:C5)" }, ++ { "Lesson1.A2", "Lesson1.B2", "Lesson2.B2", "AVERAGE(B6:C6)" }, ++ }; ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aCheck)); ++nRow) ++ { ++ for (SCCOL nCol=0; nCol < 4; ++nCol) ++ { ++ if (!checkFormula(*m_pDoc, ScAddress(nCol,nRow+1,0), aCheck[nRow][nCol])) ++ CPPUNIT_FAIL(OString("Wrong formula in " + OString('A'+nCol) + OString::number(nRow+2) + ".").getStr()); ++ } ++ } ++ } ++ ++ m_pDoc->DeleteTab(2); ++ m_pDoc->DeleteTab(1); ++ m_pDoc->DeleteTab(0); ++} ++ ++// Make sure the refupdate works also with volatile cells, see fdo#83067 ++/* FIXME: this test is not roll-over-midnight safe and will fail then! We may ++ * want to have something different, but due to the nature of volatile ++ * functions it's not that easy to come up with something reproducible staying ++ * stable over sorts.. ;-) Check for time and don't run test a few seconds ++ * before midnight, ermm.. */ ++void Test::testSortRefUpdate5() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ double aValCheck[][3] = { ++ // Result, Unsorted order, Sorted result. ++ { 0, 4, 0 }, ++ { 0, 1, 0 }, ++ { 0, 3, 0 }, ++ { 0, 2, 0 }, ++ }; ++ ScRange aSortRange; ++ { ++ const char* aData[][3] = { ++ { "Date", "Volatile", "Order" }, ++ { "1999-05-05", "=TODAY()-$A2", "4" }, ++ { "1994-10-18", "=TODAY()-$A3", "1" }, ++ { "1996-06-30", "=TODAY()-$A4", "3" }, ++ { "1995-11-21", "=TODAY()-$A5", "2" }, ++ }; ++ ++ SCTAB nTab = 0; ++ ScAddress aPos(0,0,nTab); ++ clearRange(m_pDoc, ScRange(0, 0, nTab, 2, SAL_N_ELEMENTS(aData), nTab)); ++ aSortRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData)); ++ CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aSortRange.aStart == aPos); ++ ++ // Actual results and expected sorted results. ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aValCheck)); ++nRow) ++ { ++ double fVal = m_pDoc->GetValue(ScAddress(1,nRow+1,0)); ++ aValCheck[nRow][0] = fVal; ++ aValCheck[static_cast(aValCheck[nRow][1])-1][2] = fVal; ++ } ++ } ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Sort A1:B5. ++ m_pDoc->SetAnonymousDBData( 0, new ScDBData( STR_DB_LOCAL_NONAME, aSortRange.aStart.Tab(), ++ aSortRange.aStart.Col(), aSortRange.aStart.Row(), aSortRange.aEnd.Col(), aSortRange.aEnd.Row())); ++ ++ // Sort by column A. ++ ScSortParam aSortData; ++ aSortData.nCol1 = aSortRange.aStart.Col(); ++ aSortData.nCol2 = aSortRange.aEnd.Col(); ++ aSortData.nRow1 = aSortRange.aStart.Row(); ++ aSortData.nRow2 = aSortRange.aEnd.Row(); ++ aSortData.bHasHeader = true; ++ aSortData.maKeyState[0].bDoSort = true; // sort on ++ aSortData.maKeyState[0].nField = 0; // Date ++ aSortData.maKeyState[0].bAscending = true; // ascending ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Check the sorted values. ++ m_pDoc->CalcAll(); ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aValCheck)); ++nRow) ++ { ++ size_t i = static_cast(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4 ++ CPPUNIT_ASSERT_EQUAL( static_cast(nRow+1), i); ++ CPPUNIT_ASSERT_EQUAL( aValCheck[i-1][2], m_pDoc->GetValue(ScAddress(1,nRow+1,0))); ++ } ++ ++ // Make sure the formula cells have been adjusted correctly. ++ const char* aFormulaCheck[] = { ++ // Volatile ++ "TODAY()-$A2", ++ "TODAY()-$A3", ++ "TODAY()-$A4", ++ "TODAY()-$A5", ++ }; ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aFormulaCheck)); ++nRow) ++ { ++ if (!checkFormula(*m_pDoc, ScAddress(1,nRow+1,0), aFormulaCheck[nRow])) ++ CPPUNIT_FAIL(OString("Wrong formula in B" + OString::number(nRow+2) + ".").getStr()); ++ } ++ ++ // Undo and check the result. ++ SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager(); ++ pUndoMgr->Undo(); ++ m_pDoc->CalcAll(); ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aValCheck)); ++nRow) ++ { ++ CPPUNIT_ASSERT_EQUAL( aValCheck[nRow][0], m_pDoc->GetValue(ScAddress(1,nRow+1,0))); ++ CPPUNIT_ASSERT_EQUAL( aValCheck[nRow][1], m_pDoc->GetValue(ScAddress(2,nRow+1,0))); ++ } ++ ++ // Redo and check the result. ++ pUndoMgr->Redo(); ++ m_pDoc->CalcAll(); ++ for (SCROW nRow=0; nRow < static_cast(SAL_N_ELEMENTS(aValCheck)); ++nRow) ++ { ++ size_t i = static_cast(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4 ++ CPPUNIT_ASSERT_EQUAL( static_cast(nRow+1), i); ++ CPPUNIT_ASSERT_EQUAL( aValCheck[i-1][2], m_pDoc->GetValue(ScAddress(1,nRow+1,0))); ++ } ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortOutOfPlaceResult() ++{ ++ m_pDoc->InsertTab(0, "Sort"); ++ m_pDoc->InsertTab(1, "Result"); ++ ++ const char* pData[] = { ++ "Header", ++ "1", ++ "23", ++ "2", ++ "9", ++ "-2", ++ 0 // terminator ++ }; ++ ++ // source data in A1:A6. ++ for (SCROW i = 0; pData[i]; ++i) ++ m_pDoc->SetString(ScAddress(0,i,0), OUString::createFromAscii(pData[i])); ++ ++ // Check the initial values. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Sort A1:A6, and set the result to C2:C7 ++ m_pDoc->SetAnonymousDBData( ++ 0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 5)); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 0; ++ aSortData.nCol2 = 0; ++ aSortData.nRow1 = 0; ++ aSortData.nRow2 = 5; ++ aSortData.bHasHeader = true; ++ aSortData.bInplace = false; ++ aSortData.nDestTab = 1; ++ aSortData.nDestCol = 2; ++ aSortData.nDestRow = 1; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ // Source data still intact. ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(0,0,0))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ // Sort result in C2:C7 on sheet "Result". ++ CPPUNIT_ASSERT_EQUAL(OUString("Header"), m_pDoc->GetString(ScAddress(2,1,1))); ++ CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(2,2,1))); ++ CPPUNIT_ASSERT_EQUAL( 1.0, m_pDoc->GetValue(ScAddress(2,3,1))); ++ CPPUNIT_ASSERT_EQUAL( 2.0, m_pDoc->GetValue(ScAddress(2,4,1))); ++ CPPUNIT_ASSERT_EQUAL( 9.0, m_pDoc->GetValue(ScAddress(2,5,1))); ++ CPPUNIT_ASSERT_EQUAL(23.0, m_pDoc->GetValue(ScAddress(2,6,1))); ++ ++ m_pDoc->DeleteTab(1); ++ m_pDoc->DeleteTab(0); ++} ++ ++void Test::testSortPartialFormulaGroup() ++{ ++ sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on. ++ FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1); ++ ++ m_pDoc->InsertTab(0, "Sort"); ++ ++ // Set up the sheet. ++ const char* aData[][2] = { ++ { "F1", "F2" }, ++ { "43", "=RC[-1]" }, ++ { "50", "=RC[-1]" }, ++ { "8", "=RC[-1]" }, ++ { "47", "=RC[-1]" }, ++ { "28", "=RC[-1]" }, ++ { 0, 0 } // terminator ++ }; ++ ++ // A1:B6. ++ for (SCROW i = 0; aData[i][0]; ++i) ++ { ++ m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0])); ++ m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1])); ++ } ++ ++ // Check the initial condition. ++ for (SCROW i = 1; i <= 5; ++i) ++ // A2:A6 should equal B2:B6. ++ CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(ScAddress(0,i,0)), m_pDoc->GetValue(ScAddress(1,i,0))); ++ ++ const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0)); ++ CPPUNIT_ASSERT(pFC); ++ CPPUNIT_ASSERT_MESSAGE("This formula cell should be the first in a group.", pFC->IsSharedTop()); ++ CPPUNIT_ASSERT_MESSAGE("Incorrect formula group length.", pFC->GetSharedLength() == 5); ++ ++ ScDBDocFunc aFunc(getDocShell()); ++ ++ // Sort only B2:B4. This caused crash at one point (c.f. fdo#81617). ++ ++ m_pDoc->SetAnonymousDBData(0, new ScDBData(STR_DB_LOCAL_NONAME, 0, 1, 1, 1, 3)); ++ ++ ScSortParam aSortData; ++ aSortData.nCol1 = 1; ++ aSortData.nCol2 = 1; ++ aSortData.nRow1 = 1; ++ aSortData.nRow2 = 3; ++ aSortData.bHasHeader = false; ++ aSortData.bInplace = true; ++ aSortData.maKeyState[0].bDoSort = true; ++ aSortData.maKeyState[0].nField = 0; ++ aSortData.maKeyState[0].bAscending = true; ++ bool bSorted = aFunc.Sort(0, aSortData, true, true, true); ++ CPPUNIT_ASSERT(bSorted); ++ ++ m_pDoc->CalcAll(); // just in case... ++ ++ // Check the cell values after the partial sort. ++ ++ // Column A ++ CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(0,1,0))); ++ CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(ScAddress(0,2,0))); ++ CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc->GetValue(ScAddress(0,3,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(0,4,0))); ++ CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0))); ++ ++ // Column B ++ CPPUNIT_ASSERT_EQUAL( 8.0, m_pDoc->GetValue(ScAddress(1,1,0))); ++ CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(1,2,0))); ++ CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(ScAddress(1,3,0))); ++ CPPUNIT_ASSERT_EQUAL(47.0, m_pDoc->GetValue(ScAddress(1,4,0))); ++ CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(1,5,0))); ++ ++ m_pDoc->DeleteTab(0); ++} ++ ++/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +-- +1.9.3 + diff --git a/0001-fdo-80284-Avoid-broadcasting-during-cell-delete-shif.patch b/0001-fdo-80284-Avoid-broadcasting-during-cell-delete-shif.patch new file mode 100644 index 0000000..b9d160f --- /dev/null +++ b/0001-fdo-80284-Avoid-broadcasting-during-cell-delete-shif.patch @@ -0,0 +1,40 @@ +From b35c51d15189835bd388411f9ab4baefacd7b460 Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida +Date: Fri, 17 Oct 2014 21:48:31 -0400 +Subject: [PATCH] fdo#80284: Avoid broadcasting during cell delete & shift. + +Broadcasting it here and marking formula cells dirty prevents them +from being entered into the formula tree at the end. They get marked +"postponed dirty" during reference update, and are supposed to be +set dirty at the end. + +Change-Id: I65977300ee4ee26b6166d170acd2145abcbbf288 +(cherry picked from commit 7fef943114b9184e69c8c714bf158116b8d9caf7) +Reviewed-on: https://gerrit.libreoffice.org/12014 +Reviewed-by: Eike Rathke +Tested-by: Eike Rathke +--- + sc/source/core/data/table2.cxx | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx +index 9abd954..9b4fd02 100644 +--- a/sc/source/core/data/table2.cxx ++++ b/sc/source/core/data/table2.cxx +@@ -391,11 +391,8 @@ void ScTable::DeleteCol( + } + } + +- { // scope for bulk broadcast +- ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM()); +- for (SCSIZE i = 0; i < nSize; i++) +- aCol[nStartCol + i].DeleteArea(nStartRow, nEndRow, IDF_ALL); +- } ++ for (SCSIZE i = 0; i < nSize; i++) ++ aCol[nStartCol + i].DeleteArea(nStartRow, nEndRow, IDF_ALL, false); + + if ((nStartRow == 0) && (nEndRow == MAXROW)) + { +-- +1.9.3 + diff --git a/0001-fdo-80846-Broadcast-changes-before-EndUndo.patch b/0001-fdo-80846-Broadcast-changes-before-EndUndo.patch new file mode 100644 index 0000000..90fdd8c --- /dev/null +++ b/0001-fdo-80846-Broadcast-changes-before-EndUndo.patch @@ -0,0 +1,80 @@ +From eb5f25984307cd9e63e9cc88cbdb09228d66b097 Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida +Date: Sat, 11 Oct 2014 14:18:10 -0400 +Subject: [PATCH] fdo#80846: Broadcast changes before EndUndo(). +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +EndUndo() calls PostDataChanged(), which renders the recalculated formula +cells. Not broadcasting before EndUndo causes some dependent formula +cells to not get recalculated. + +This one unfortunately is not currently unit-testable as this behavior +depends on the presence of ScTabViewShell.... + +Change-Id: I86288608b7f2627cda7c74be27a18029832775ef +(cherry picked from commit 424bfaa773e58d6b609ac7f64907db4b542d1315) +Reviewed-on: https://gerrit.libreoffice.org/11927 +Reviewed-by: Caolán McNamara +Tested-by: Caolán McNamara +--- + sc/source/ui/undo/undoblk3.cxx | 23 +++++++++-------------- + 1 file changed, 9 insertions(+), 14 deletions(-) + +diff --git a/sc/source/ui/undo/undoblk3.cxx b/sc/source/ui/undo/undoblk3.cxx +index 03edabf..6784134 100644 +--- a/sc/source/ui/undo/undoblk3.cxx ++++ b/sc/source/ui/undo/undoblk3.cxx +@@ -170,12 +170,20 @@ void ScUndoDeleteContents::DoChange( const bool bUndo ) + SetChangeTrack(); + } + ++ if (nFlags & IDF_CONTENTS) ++ { ++ // Broadcast only when the content changes. fdo#74687 ++ if (mpDataSpans) ++ BroadcastChanges(*mpDataSpans); ++ else ++ BroadcastChanges(aRange); ++ } ++ + ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell(); + if ( !( (pViewShell) && pViewShell->AdjustRowHeight( + aRange.aStart.Row(), aRange.aEnd.Row() ) ) ) + /*A*/ pDocShell->PostPaint( aRange, PAINT_GRID | PAINT_EXTRAS, nExtFlags ); + +- pDocShell->PostDataChanged(); + if (pViewShell) + pViewShell->CellContentChanged(); + +@@ -188,15 +196,6 @@ void ScUndoDeleteContents::Undo() + DoChange( true ); + EndUndo(); + +- if (nFlags & IDF_CONTENTS) +- { +- // Broadcast only when the content changes. fdo#74687 +- if (mpDataSpans) +- BroadcastChanges(*mpDataSpans); +- else +- BroadcastChanges(aRange); +- } +- + HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, aRange); + } + +@@ -206,10 +205,6 @@ void ScUndoDeleteContents::Redo() + DoChange( false ); + EndRedo(); + +- if (nFlags & IDF_CONTENTS) +- // Broadcast only when the content changes. fdo#74687 +- BroadcastChanges(aRange); +- + HelperNotifyChanges::NotifyIfChangesListeners(*pDocShell, aRange); + } + +-- +1.9.3 + diff --git a/0001-fdo-82047-Correctly-adjust-references-in-range-names.patch b/0001-fdo-82047-Correctly-adjust-references-in-range-names.patch new file mode 100644 index 0000000..de103d0 --- /dev/null +++ b/0001-fdo-82047-Correctly-adjust-references-in-range-names.patch @@ -0,0 +1,76 @@ +From ab5ff775b5b197a11a76a5e91859c31421ff559f Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida +Date: Sat, 18 Oct 2014 20:22:53 -0400 +Subject: [PATCH] fdo#82047: Correctly adjust references in range names on row + deletion. + +Change-Id: Iac924b0b6932863f7f9cc088f996e0b07c340d2c +(cherry picked from commit 281847613bd3ae472523822f4be9c21cc353867e) +Reviewed-on: https://gerrit.libreoffice.org/12025 +Reviewed-by: Eike Rathke +Tested-by: Eike Rathke +--- + sc/source/core/tool/token.cxx | 47 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx +index 916a88e..84c8ccc 100644 +--- a/sc/source/core/tool/token.cxx ++++ b/sc/source/core/tool/token.cxx +@@ -3170,6 +3170,53 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceInName( + if (adjustDoubleRefInName(rRef, rCxt, rPos)) + aRes.mbReferenceModified = true; + } ++ else if (rCxt.mnRowDelta < 0) ++ { ++ // row(s) deleted. ++ if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel()) ++ // Don't modify relative references in names. ++ break; ++ ++ if (aAbs.aStart.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.aEnd.Col()) ++ // column range of the reference is not entirely in the deleted column range. ++ break; ++ ++ ScRange aDeleted = rCxt.maRange; ++ aDeleted.aStart.IncRow(rCxt.mnRowDelta); ++ aDeleted.aEnd.SetRow(aDeleted.aStart.Row()-rCxt.mnRowDelta-1); ++ ++ if (aAbs.aEnd.Row() < aDeleted.aStart.Row() || aDeleted.aEnd.Row() < aAbs.aStart.Row()) ++ // reference range doesn't intersect with the deleted range. ++ break; ++ ++ if (aDeleted.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= aDeleted.aEnd.Row()) ++ { ++ // This reference is entirely deleted. ++ rRef.Ref1.SetRowDeleted(true); ++ rRef.Ref2.SetRowDeleted(true); ++ aRes.mbReferenceModified = true; ++ break; ++ } ++ ++ if (aAbs.aStart.Row() < aDeleted.aStart.Row()) ++ { ++ if (aDeleted.aEnd.Row() < aAbs.aEnd.Row()) ++ // Deleted in the middle. Make the reference shorter. ++ rRef.Ref2.IncRow(rCxt.mnRowDelta); ++ else ++ // Deleted at tail end. Cut off the lower part. ++ rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1); ++ } ++ else ++ { ++ // Deleted at the top. Cut the top off and shift up. ++ rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1); ++ rRef.Ref1.IncRow(rCxt.mnRowDelta); ++ rRef.Ref2.IncRow(rCxt.mnRowDelta); ++ } ++ ++ aRes.mbReferenceModified = true; ++ } + else if (rCxt.maRange.Intersects(aAbs)) + { + if (rCxt.mnColDelta && rCxt.maRange.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= rCxt.maRange.aEnd.Row()) +-- +1.9.3 + diff --git a/0001-fdo-83901-ROW-and-COLUMN-to-be-properly-recalculated.patch b/0001-fdo-83901-ROW-and-COLUMN-to-be-properly-recalculated.patch new file mode 100644 index 0000000..cbf245d --- /dev/null +++ b/0001-fdo-83901-ROW-and-COLUMN-to-be-properly-recalculated.patch @@ -0,0 +1,54 @@ +From 4b2f0915f9f3bff7d2476ec41a272e5263fbb312 Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida +Date: Sun, 12 Oct 2014 10:18:09 -0400 +Subject: [PATCH] fdo#83901: ROW() and COLUMN() to be properly recalculated on + cell move. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For cases where ROW or COLUMN references another cell that has shifted. + +Change-Id: Ic4bef8672dab811ceff6886d9af0388306a66485 +(cherry picked from commit 0b29a16d1dcffd75e49bd7ad3da867b0d0ebfa38) +Reviewed-on: https://gerrit.libreoffice.org/11934 +Reviewed-by: Caolán McNamara +Tested-by: Caolán McNamara +--- + sc/source/core/data/column.cxx | 6 ++++++ + sc/source/core/data/formulacell.cxx | 2 +- + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx +index 1676e1b..42da658 100644 +--- a/sc/source/core/data/column.cxx ++++ b/sc/source/core/data/column.cxx +@@ -2096,6 +2096,12 @@ class UpdateRefOnNonCopy : std::unary_function + if (pCode->IsRecalcModeOnRefMove()) + aRes.mbValueChanged = true; + } ++ else if (aRes.mbReferenceModified && pCode->IsRecalcModeOnRefMove()) ++ { ++ // The cell itself hasn't shifted. But it may have ROW or COLUMN ++ // referencing another cell that has. ++ aRes.mbValueChanged = true; ++ } + + if (aRes.mbNameModified) + recompileTokenArray(*pTop); +diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx +index 2059aee..96eb323 100644 +--- a/sc/source/core/data/formulacell.cxx ++++ b/sc/source/core/data/formulacell.cxx +@@ -2758,7 +2758,7 @@ bool ScFormulaCell::UpdateReferenceOnShift( + + if (bOnRefMove) + // Cell may reference itself, e.g. ocColumn, ocRow without parameter +- bOnRefMove = (bValChanged || (aPos != aOldPos)); ++ bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified); + + bool bNewListening = false; + bool bInDeleteUndo = false; +-- +1.9.3 + diff --git a/0001-fdo-85215-Don-t-adjust-references-wrt-cell-position-.patch b/0001-fdo-85215-Don-t-adjust-references-wrt-cell-position-.patch new file mode 100644 index 0000000..fd286e7 --- /dev/null +++ b/0001-fdo-85215-Don-t-adjust-references-wrt-cell-position-.patch @@ -0,0 +1,51 @@ +From 81e4dbe1adf196ee20f1a4bfbc50b54abfc79f4b Mon Sep 17 00:00:00 2001 +From: Kohei Yoshida +Date: Sun, 26 Oct 2014 14:43:14 -0700 +Subject: [PATCH] fdo#85215: Don't adjust references wrt cell position when + disabled. + +Change-Id: Ie1a12cc189bcb66fad59ea9901ac0dc95bb68788 +(cherry picked from commit 10fc138307afb4b39baddb0d56eb8e986e5d29ea) +Reviewed-on: https://gerrit.libreoffice.org/12106 +Reviewed-by: Markus Mohrhard +Tested-by: Markus Mohrhard +--- + sc/source/core/data/table3.cxx | 7 +++++-- + sc/source/ui/undo/undosort.cxx | 3 +-- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx +index ce17bb9..05285d8 100644 +--- a/sc/source/core/data/table3.cxx ++++ b/sc/source/core/data/table3.cxx +@@ -856,8 +856,11 @@ void ScTable::SortReorderByRow( + ScAddress aOldPos = rCell.maCell.mpFormula->aPos; + + ScFormulaCell* pNew = rCell.maCell.mpFormula->Clone( aCellPos, SC_CLONECELL_DEFAULT); +- pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula); +- pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos); ++ if (pArray->IsUpdateRefs()) ++ { ++ pNew->CopyAllBroadcasters(*rCell.maCell.mpFormula); ++ pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos); ++ } + + sc::CellStoreType::iterator itBlk = rCellStore.push_back(pNew); + } +diff --git a/sc/source/ui/undo/undosort.cxx b/sc/source/ui/undo/undosort.cxx +index 36156fe..4a00707 100644 +--- a/sc/source/ui/undo/undosort.cxx ++++ b/sc/source/ui/undo/undosort.cxx +@@ -46,8 +46,7 @@ void UndoSort::Execute( bool bUndo ) + + ScUndoUtil::MarkSimpleBlock(pDocShell, maParam.maSortRange); + +- pDocShell->PostPaint(maParam.maSortRange, PAINT_GRID); +- pDocShell->PostDataChanged(); ++ rDoc.SetDirty(maParam.maSortRange); + if (!aParam.mbUpdateRefs) + rDoc.BroadcastCells(aParam.maSortRange, SC_HINT_DATACHANGED); + } +-- +1.9.3 + diff --git a/0001-fdo-85403-broadcast-changes-after-TextToColumn.patch b/0001-fdo-85403-broadcast-changes-after-TextToColumn.patch new file mode 100644 index 0000000..b7d6e99 --- /dev/null +++ b/0001-fdo-85403-broadcast-changes-after-TextToColumn.patch @@ -0,0 +1,35 @@ +From e963f4ee14fdda118a54745dcfca46e6244f0999 Mon Sep 17 00:00:00 2001 +From: Eike Rathke +Date: Mon, 3 Nov 2014 14:52:27 +0100 +Subject: [PATCH] fdo#85403 broadcast changes after TextToColumn +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Regression introduced with 3d869cda8db03820dea8c4ba463eb155d05e933b for +fdo#74014 + +Change-Id: Ie8ca1e7c15609aaf80b4ecbb6ccffc30a3f79f0a +(cherry picked from commit 99cfc0f8a321c3fd3ef1a49d669ebc5744dbf606) +Reviewed-on: https://gerrit.libreoffice.org/12216 +Reviewed-by: Caolán McNamara +Tested-by: Caolán McNamara +--- + sc/source/ui/view/cellsh2.cxx | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx +index 9a0fc853..7b7189c 100644 +--- a/sc/source/ui/view/cellsh2.cxx ++++ b/sc/source/ui/view/cellsh2.cxx +@@ -976,6 +976,7 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq ) + pDlg->SaveParameters(); + aImport.SetExtOptions( aOptions ); + aImport.SetApi( false ); ++ aImport.SetImportBroadcast( true ); + aStream.Seek( 0 ); + aImport.ImportStream( aStream, OUString(), FORMAT_STRING ); + +-- +1.9.3 + diff --git a/libreoffice.spec b/libreoffice.spec index 0e50b27..8f3b118 100644 --- a/libreoffice.spec +++ b/libreoffice.spec @@ -347,6 +347,14 @@ Patch49: 0001-fdo-60712-Inherits-cell-styles-in-inserting-rows-col.patch Patch50: 0001-implement-toggling-off-removeable-master-elements-wi.patch Patch51: 0001-Resolves-fdo-78151-change-style-on-toggling-bullets-.patch Patch52: 0001-Resolves-rhbz-1161238-sync-PRESOBJ_OUTLINE-para-dept.patch +Patch53: 0001-fdo-83901-ROW-and-COLUMN-to-be-properly-recalculated.patch +Patch54: 0001-fdo-80846-Broadcast-changes-before-EndUndo.patch +Patch55: 0001-Back-port-Kohei-s-comprehensive-sorting-unit-tests-f.patch +Patch56: 0001-Adapt-sorting-unit-tests-for-new-default.patch +Patch57: 0001-fdo-80284-Avoid-broadcasting-during-cell-delete-shif.patch +Patch58: 0001-fdo-82047-Correctly-adjust-references-in-range-names.patch +Patch59: 0001-fdo-85215-Don-t-adjust-references-wrt-cell-position-.patch +Patch60: 0001-fdo-85403-broadcast-changes-after-TextToColumn.patch %define instdir %{_libdir} %define baseinstdir %{instdir}/libreoffice @@ -2313,7 +2321,7 @@ update-desktop-database %{_datadir}/applications &> /dev/null || : %endif %changelog -* Mon Nov 10 2014 Caolán McNamara - 1:4.3.3.2-5 +* Tue Nov 11 2014 Caolán McNamara - 1:4.3.3.2-5 - Resolves: rhbz#1161238 sync PRESOBJ_OUTLINE para depth on load * Thu Nov 06 2014 Caolán McNamara - 1:4.3.3.2-4