[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