[libreoffice] fix regression checks
Caolán McNamara
caolanm at fedoraproject.org
Tue Nov 11 09:30:02 UTC 2014
commit 0e560ad933eceedf2af31e07046582c2d10a1ff2
Author: Caolán McNamara <caolanm at redhat.com>
Date: Tue Nov 11 09:29:52 2014 +0000
fix regression checks
...-Adapt-sorting-unit-tests-for-new-default.patch | 248 +++
...ohei-s-comprehensive-sorting-unit-tests-f.patch | 2057 ++++++++++++++++++++
...void-broadcasting-during-cell-delete-shif.patch | 40 +
...do-80846-Broadcast-changes-before-EndUndo.patch | 80 +
...orrectly-adjust-references-in-range-names.patch | 76 +
...OW-and-COLUMN-to-be-properly-recalculated.patch | 54 +
...on-t-adjust-references-wrt-cell-position-.patch | 51 +
...5403-broadcast-changes-after-TextToColumn.patch | 35 +
libreoffice.spec | 10 +-
9 files changed, 2650 insertions(+), 1 deletions(-)
---
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 <michael.meeks at collabora.com>
+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 <muthusuba at gmail.com>
+Reviewed-by: Eike Rathke <erack at redhat.com>
+Tested-by: Eike Rathke <erack at redhat.com>
+---
+ 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 <svl/stritem.hxx>
+
+ #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<SCROW>(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 <postit.hxx>
+ #include <sortparam.hxx>
+@@ -18,7 +19,6 @@
+ #include <globalnames.hxx>
+ #include <dbdocfun.hxx>
+ #include <scitems.hxx>
+-#include <inputopt.hxx>
+ #include <editutil.hxx>
+
+ #include <sal/config.h>
+@@ -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 <michael.meeks at collabora.com>
+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 <muthusuba at gmail.com>
+Reviewed-by: Eike Rathke <erack at redhat.com>
+Tested-by: Eike Rathke <erack at redhat.com>
+---
+ 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<const SvxWeightItem*>(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<const SvxPostureItem*>(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<const SvxWeightItem*>(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<const SvxPostureItem*>(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 <postit.hxx>
++#include <sortparam.hxx>
++#include <dbdata.hxx>
++#include <patattr.hxx>
++#include <formulacell.hxx>
++#include <scopetools.hxx>
++#include <globalnames.hxx>
++#include <dbdocfun.hxx>
++#include <scitems.hxx>
++#include <inputopt.hxx>
++#include <editutil.hxx>
++
++#include <sal/config.h>
++#include <editeng/wghtitem.hxx>
++#include <editeng/postitem.hxx>
++#include <test/bootstrapfixture.hxx>
++
++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<const SvxWeightItem*>(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<const SvxPostureItem*>(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<const SvxWeightItem*>(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<const SvxPostureItem*>(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<SCROW>(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<SCROW>(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<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
++ {
++ double fVal = m_pDoc->GetValue(ScAddress(1,nRow+1,0));
++ aValCheck[nRow][0] = fVal;
++ aValCheck[static_cast<size_t>(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<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
++ {
++ size_t i = static_cast<size_t>(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4
++ CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(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<SCROW>(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<SCROW>(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<SCROW>(SAL_N_ELEMENTS(aValCheck)); ++nRow)
++ {
++ size_t i = static_cast<size_t>(m_pDoc->GetValue(ScAddress(2,nRow+1,0))); // order 1..4
++ CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(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 <kohei.yoshida at collabora.com>
+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 <erack at redhat.com>
+Tested-by: Eike Rathke <erack at redhat.com>
+---
+ 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 <kohei.yoshida at collabora.com>
+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 <caolanm at redhat.com>
+Tested-by: Caolán McNamara <caolanm at redhat.com>
+---
+ 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 <kohei.yoshida at collabora.com>
+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 <erack at redhat.com>
+Tested-by: Eike Rathke <erack at redhat.com>
+---
+ 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 <kohei.yoshida at collabora.com>
+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 <caolanm at redhat.com>
+Tested-by: Caolán McNamara <caolanm at redhat.com>
+---
+ 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<sc::FormulaGroupEntry, void>
+ 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 <kohei.yoshida at collabora.com>
+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 <markus.mohrhard at googlemail.com>
+Tested-by: Markus Mohrhard <markus.mohrhard at googlemail.com>
+---
+ 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 <erack at redhat.com>
+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 <caolanm at redhat.com>
+Tested-by: Caolán McNamara <caolanm at redhat.com>
+---
+ 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 <caolanm at redhat.com> - 1:4.3.3.2-5
+* Tue Nov 11 2014 Caolán McNamara <caolanm at redhat.com> - 1:4.3.3.2-5
- Resolves: rhbz#1161238 sync PRESOBJ_OUTLINE para depth on load
* Thu Nov 06 2014 Caolán McNamara <caolanm at redhat.com> - 1:4.3.3.2-4
More information about the scm-commits
mailing list