[Libreoffice-commits] core.git: 2 commits - sc/inc sc/qa sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Wed Apr 30 22:18:32 PDT 2014


 sc/inc/address.hxx                  |   13 --
 sc/inc/column.hxx                   |   20 ++++
 sc/inc/refhint.hxx                  |   20 ++++
 sc/inc/table.hxx                    |    1 
 sc/inc/tokenarray.hxx               |   20 ++++
 sc/inc/types.hxx                    |   15 +++
 sc/qa/unit/helper/qahelper.hxx      |   37 ++++++++
 sc/qa/unit/ucalc.cxx                |   74 ++++++++++++++++
 sc/qa/unit/ucalc.hxx                |    2 
 sc/qa/unit/ucalc_pivottable.cxx     |   33 -------
 sc/source/core/data/column.cxx      |   60 -------------
 sc/source/core/data/column4.cxx     |  129 +++++++++++++++++++++++++++++
 sc/source/core/data/formulacell.cxx |   24 ++++-
 sc/source/core/data/table3.cxx      |   95 ++++++++++++++++-----
 sc/source/core/tool/refhint.cxx     |   25 +++++
 sc/source/core/tool/token.cxx       |  160 +++++++++++++++++++++++++++++++++---
 16 files changed, 581 insertions(+), 147 deletions(-)

New commits:
commit 3c4fb52d8fc89fe43983991ed2339295b2e0ef8c
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Thu May 1 01:15:02 2014 -0400

    fdo#78079: Re-work sort by column to get it to do the right thing.
    
    Also fixed reference update problem.
    
    Change-Id: I06e6115ef969a011fdd5c92d5eb1927fb7ae789b

diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx
index e8f4c93..f856f3d 100644
--- a/sc/inc/address.hxx
+++ b/sc/inc/address.hxx
@@ -26,6 +26,7 @@
 
 #include <limits>
 #include "scdllapi.h"
+#include <types.hxx>
 #include <formula/grammar.hxx>
 
 #include <com/sun/star/uno/Sequence.hxx>
@@ -38,18 +39,6 @@ namespace com { namespace sun { namespace star {
 
 class ScDocument;
 
-// The typedefs
-typedef sal_Int32 SCROW;
-typedef sal_Int16 SCCOL;
-typedef sal_Int16 SCTAB;
-typedef sal_Int32 SCCOLROW;     ///< a type capable of holding either SCCOL or SCROW
-
-// temporarily signed typedefs
-typedef sal_Int32 SCsROW;
-typedef sal_Int16 SCsCOL;
-typedef sal_Int16 SCsTAB;
-typedef sal_Int32 SCsCOLROW;
-
 /** size_t typedef to be able to find places where code was changed from USHORT
     to size_t and is used to read/write from/to streams. */
 typedef size_t SCSIZE;
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 0e13ec9..526c8e3 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -186,7 +186,7 @@ public:
 
     void        Delete( SCROW nRow );
     void        FreeAll();
-    void        SwapCell( SCROW nRow, ScColumn& rCol);
+    void Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern );
 
     bool        HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const;
     bool    HasAttribSelection( const ScMarkData& rMark, sal_uInt16 nMask ) const;
@@ -480,6 +480,7 @@ public:
     void BroadcastRecalcOnRefMove();
     void BroadcastRefMoved( const sc::RefMovedHint& rHint );
     void TransferListeners( ScColumn& rDestCol, SCROW nRow1, SCROW nRow2, SCROW nRowDelta );
+    void CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 );
 
     void CompileDBFormula( sc::CompileFormulaContext& rCxt );
     void CompileDBFormula( sc::CompileFormulaContext& rCxt, bool bCreateFormulaString );
@@ -576,6 +577,23 @@ public:
      */
     void RegroupFormulaCells();
 
+    /**
+     * Reset column position of formula cells within specified row range.
+     * Reference positions are also adjusted to reflect the new position so
+     * that the formula cells still reference the same cells or ranges after
+     * the the position change.  The position of a formula cell before the
+     * call is interpreted as the old position of that cell.
+     *
+     * Caller needs to ensure that no formula groups cross the top and bottom
+     * row boundaries.
+     *
+     * @param nRow1 top row boundary
+     * @param nRow2 bottom row boundary
+     */
+    void ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2 );
+
+    void SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange );
+
     void TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest );
     void TransferCellValuesFrom( SCROW nRow, sc::CellValues& rSrc );
     void CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc );
diff --git a/sc/inc/refhint.hxx b/sc/inc/refhint.hxx
index b9ddf04..9ded863 100644
--- a/sc/inc/refhint.hxx
+++ b/sc/inc/refhint.hxx
@@ -18,7 +18,7 @@ namespace sc {
 class RefHint : public SfxSimpleHint
 {
 public:
-    enum Type { Moved };
+    enum Type { Moved, ColumnReordered };
 
 private:
     Type meType;
@@ -55,6 +55,24 @@ public:
     const ScAddress& getDelta() const;
 };
 
+class RefColReorderHint : public RefHint
+{
+    const sc::ColReorderMapType& mrColMap;
+    SCTAB mnTab;
+    SCROW mnRow1;
+    SCROW mnRow2;
+
+public:
+    RefColReorderHint( const sc::ColReorderMapType& rColMap, SCTAB nTab, SCROW nRow1, SCROW nRow2 );
+    virtual ~RefColReorderHint();
+
+    const sc::ColReorderMapType& getColMap() const;
+
+    SCTAB getTab() const;
+    SCROW getStartRow() const;
+    SCROW getEndRow() const;
+};
+
 }
 
 #endif
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index a8bef6e..16680dc 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1007,7 +1007,6 @@ private:
                                 // use the global sort parameter:
     bool        IsSorted(SCCOLROW nStart, SCCOLROW nEnd) const;
     void        DecoladeRow( ScSortInfoArray*, SCROW nRow1, SCROW nRow2 );
-    void        SwapCol(SCCOL nCol1, SCCOL nCol2);
     short CompareCell(
         sal_uInt16 nSort,
         ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index 7244790..c7ff3dc 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -157,6 +157,18 @@ public:
         const ScAddress& rPos, const ScRange& rMovedRange, const ScAddress& rDelta );
 
     /**
+     * Move reference positions in response to column reordering.  A range
+     * reference gets moved only when the whole range fits in a single column.
+     *
+     * @param rPos position of this formula cell
+     * @param nTab sheet where columns are reordered.
+     * @param nRow1 top row of reordered range.
+     * @param nRow2 bottom row of reordered range.
+     * @param rColMap old-to-new column mapping.
+     */
+    void MoveReference( const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColReorderMapType& rColMap );
+
+    /**
      * Adjust all references in named expression. In named expression, we only
      * update absolute positions, and leave relative positions intact.
      *
@@ -184,6 +196,11 @@ public:
     sc::RefUpdateResult AdjustReferenceOnMovedTab( sc::RefUpdateMoveTabContext& rCxt, const ScAddress& rOldPos );
 
     /**
+     * Adjust all internal references on base position change.
+     */
+    void AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos );
+
+    /**
      * Clear sheet deleted flag from internal reference tokens if the sheet
      * index falls within specified range.  Note that when a reference is on a
      * sheet that's been deleted, its referenced sheet index retains the
@@ -198,6 +215,9 @@ public:
     void CheckRelativeReferenceBounds(
         const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const;
 
+    void CheckRelativeReferenceBounds(
+        const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const;
+
     /**
      * Create a string representation of formula token array without modifying
      * the internal state of the token array.
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 045f550..216a58d 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -13,9 +13,22 @@
 #include "sal/types.h"
 
 #include <boost/intrusive_ptr.hpp>
+#include <boost/unordered_map.hpp>
 
 class ScMatrix;
 
+// The typedefs
+typedef sal_Int32 SCROW;
+typedef sal_Int16 SCCOL;
+typedef sal_Int16 SCTAB;
+typedef sal_Int32 SCCOLROW;     ///< a type capable of holding either SCCOL or SCROW
+
+// temporarily signed typedefs
+typedef sal_Int32 SCsROW;
+typedef sal_Int16 SCsCOL;
+typedef sal_Int16 SCsTAB;
+typedef sal_Int32 SCsCOLROW;
+
 typedef ::boost::intrusive_ptr<ScMatrix>        ScMatrixRef;
 typedef ::boost::intrusive_ptr<const ScMatrix>  ScConstMatrixRef;
 
@@ -85,6 +98,8 @@ struct RangeMatrix
     bool isRangeValid() const;
 };
 
+typedef boost::unordered_map<SCCOL,SCCOL> ColReorderMapType;
+
 }
 
 #endif
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index a243598..208c046 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -836,66 +836,6 @@ const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition&
     return &sc::celltextattr_block::at(*aPos.first->data, aPos.second);
 }
 
-namespace {
-
-/**
- * Adjust references in formula cell with respect to column-wise relocation.
- */
-void updateRefInFormulaCell( ScDocument* pDoc, ScFormulaCell& rCell, SCCOL nCol, SCTAB nTab, SCCOL nColDiff )
-{
-    rCell.aPos.SetCol(nCol);
-    sc::RefUpdateContext aCxt(*pDoc);
-    aCxt.meMode = URM_MOVE;
-    aCxt.maRange = ScRange(ScAddress(nCol, 0, nTab), ScAddress(nCol, MAXROW, nTab));
-    aCxt.mnColDelta = nColDiff;
-    rCell.UpdateReference(aCxt);
-}
-
-}
-
-void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol)
-{
-    sc::CellStoreType::position_type aPos1 = maCells.position(nRow);
-    sc::CellStoreType::position_type aPos2 = rCol.maCells.position(nRow);
-
-    if (aPos1.first->type == sc::element_type_formula)
-    {
-        ScFormulaCell& rCell = *sc::formula_block::at(*aPos1.first->data, aPos1.second);
-        updateRefInFormulaCell(pDocument, rCell, rCol.nCol, nTab, rCol.nCol - nCol);
-        sc::SharedFormulaUtil::unshareFormulaCell(aPos1, rCell);
-    }
-
-    if (aPos2.first->type == sc::element_type_formula)
-    {
-        ScFormulaCell& rCell = *sc::formula_block::at(*aPos2.first->data, aPos2.second);
-        updateRefInFormulaCell(pDocument, rCell, nCol, nTab, nCol - rCol.nCol);
-        sc::SharedFormulaUtil::unshareFormulaCell(aPos2, rCell);
-    }
-
-    maCells.swap(nRow, nRow, rCol.maCells, nRow);
-    maCellTextAttrs.swap(nRow, nRow, rCol.maCellTextAttrs, nRow);
-    maCellNotes.swap(nRow, nRow, rCol.maCellNotes, nRow);
-
-    aPos1 = maCells.position(nRow);
-    aPos2 = rCol.maCells.position(nRow);
-
-    if (aPos1.first->type == sc::element_type_formula)
-    {
-        ScFormulaCell& rCell = *sc::formula_block::at(*aPos1.first->data, aPos1.second);
-        JoinNewFormulaCell(aPos1, rCell);
-    }
-
-    if (aPos2.first->type == sc::element_type_formula)
-    {
-        ScFormulaCell& rCell = *sc::formula_block::at(*aPos2.first->data, aPos2.second);
-        rCol.JoinNewFormulaCell(aPos2, rCell);
-    }
-
-    CellStorageModified();
-    rCol.CellStorageModified();
-}
-
-
 bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
 {
     if (IsEmpty())
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index b955344..cf9cd1e 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -28,6 +28,7 @@
 #include <globalnames.hxx>
 #include <scitems.hxx>
 #include <cellform.hxx>
+#include <sharedformula.hxx>
 
 #include <svl/sharedstringpool.hxx>
 
@@ -830,4 +831,132 @@ void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
         CellStorageModified();
 }
 
+void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
+{
+    maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
+    maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
+    maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
+    maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
+
+    if (bPattern)
+    {
+        for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+        {
+            const ScPatternAttr* pPat1 = GetPattern(nRow);
+            const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
+            if (pPat1 != pPat2)
+            {
+                SetPattern(nRow, *pPat2, true);
+                rOther.SetPattern(nRow, *pPat1, true);
+            }
+        }
+    }
+
+    CellStorageModified();
+    rOther.CellStorageModified();
+}
+
+namespace {
+
+class FormulaColPosSetter
+{
+    SCCOL mnCol;
+public:
+    FormulaColPosSetter( SCCOL nCol ) : mnCol(nCol) {}
+
+    void operator() ( size_t nRow, ScFormulaCell* pCell )
+    {
+        if (!pCell->IsShared() || pCell->IsSharedTop())
+        {
+            // Ensure that the references still point to the same locations
+            // after the position change.
+            ScAddress aOldPos = pCell->aPos;
+            pCell->aPos.SetCol(mnCol);
+            pCell->aPos.SetRow(nRow);
+            pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
+        }
+        else
+        {
+            pCell->aPos.SetCol(mnCol);
+            pCell->aPos.SetRow(nRow);
+        }
+    }
+};
+
+}
+
+void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2 )
+{
+    FormulaColPosSetter aFunc(nCol);
+    sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+namespace {
+
+class RelativeRefBoundChecker
+{
+    std::vector<SCROW> maBounds;
+    ScRange maBoundRange;
+
+public:
+    RelativeRefBoundChecker( const ScRange& rBoundRange ) :
+        maBoundRange(rBoundRange) {}
+
+    void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
+    {
+        if (!pCell->IsSharedTop())
+            return;
+
+        pCell->GetCode()->CheckRelativeReferenceBounds(
+            pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
+    }
+
+    void swapBounds( std::vector<SCROW>& rBounds )
+    {
+        rBounds.swap(maBounds);
+    }
+};
+
+}
+
+void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
+{
+    std::vector<SCROW> aBounds;
+
+    // Cut at row boundaries first.
+    aBounds.push_back(rBoundRange.aStart.Row());
+    aBounds.push_back(rBoundRange.aEnd.Row()+1);
+    sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
+
+    RelativeRefBoundChecker aFunc(rBoundRange);
+    sc::ProcessFormula(
+        maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
+    aFunc.swapBounds(aBounds);
+    sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
+}
+
+namespace {
+
+class ListenerCollector
+{
+    std::vector<SvtListener*>& mrListeners;
+public:
+    ListenerCollector( std::vector<SvtListener*>& rListener ) :
+        mrListeners(rListener) {}
+
+    void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
+    {
+        SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
+        std::copy(rLis.begin(), rLis.end(), std::back_inserter(mrListeners));
+    }
+};
+
+}
+
+void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
+{
+    ListenerCollector aFunc(rListeners);
+    sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index aa52450..d7cef22 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1898,12 +1898,28 @@ void ScFormulaCell::Notify( const SfxHint& rHint )
     {
         const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
 
-        if (rRefHint.getType() == sc::RefHint::Moved)
+        switch (rRefHint.getType())
         {
-            // One of the references has moved.
+            case sc::RefHint::Moved:
+            {
+                // One of the references has moved.
 
-            const sc::RefMovedHint& rRefMoved = static_cast<const sc::RefMovedHint&>(rRefHint);
-            pCode->MoveReference(aPos, rRefMoved.getRange(), rRefMoved.getDelta());
+                const sc::RefMovedHint& rRefMoved = static_cast<const sc::RefMovedHint&>(rRefHint);
+                if (!IsShared() || IsSharedTop())
+                    pCode->MoveReference(aPos, rRefMoved.getRange(), rRefMoved.getDelta());
+            }
+            break;
+            case sc::RefHint::ColumnReordered:
+            {
+                const sc::RefColReorderHint& rRefColReorder =
+                    static_cast<const sc::RefColReorderHint&>(rRefHint);
+                if (!IsShared() || IsSharedTop())
+                    pCode->MoveReference(
+                        aPos, rRefColReorder.getTab(), rRefColReorder.getStartRow(), rRefColReorder.getEndRow(), rRefColReorder.getColMap());
+            }
+            break;
+            default:
+                ;
         }
 
         return;
diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx
index 2cf6c1b..f32d559 100644
--- a/sc/source/core/data/table3.cxx
+++ b/sc/source/core/data/table3.cxx
@@ -60,6 +60,7 @@
 #include <fstalgorithm.hxx>
 #include <listenercontext.hxx>
 #include <sharedformula.hxx>
+#include <refhint.hxx>
 
 #include "svl/sharedstringpool.hxx"
 
@@ -257,6 +258,7 @@ private:
     SCCOLROW        mnLastIndex; /// index of last non-empty cell position.
     sal_uInt16      nUsedSorts;
 
+    std::vector<SCCOLROW> maOldIndices;
     bool mbKeepQuery;
 
 public:
@@ -274,6 +276,9 @@ public:
                 ppInfo[j] = new ScSortInfo;
             pppInfo[nSort] = ppInfo;
         }
+
+        for (size_t i = 0; i < nCount; ++i)
+            maOldIndices.push_back(i+nStart);
     }
 
     ~ScSortInfoArray()
@@ -310,6 +315,8 @@ public:
             ppInfo[n2] = pTmp;
         }
 
+        std::swap(maOldIndices[n1], maOldIndices[n2]);
+
         if (mpRows)
         {
             // Swap rows in data table.
@@ -324,6 +331,8 @@ public:
     SCCOLROW GetLast() const { return mnLastIndex; }
     SCSIZE      GetCount() const { return nCount; }
 
+    const std::vector<SCCOLROW>& GetOldIndices() const { return maOldIndices; }
+
     RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
     {
         mpRows.reset(new RowsType);
@@ -520,6 +529,22 @@ void ScTable::DestroySortCollator()
     }
 }
 
+namespace {
+
+class ColReorderNotifier : std::unary_function<SvtListener*, void>
+{
+    sc::RefColReorderHint maHint;
+public:
+    ColReorderNotifier( const sc::ColReorderMapType& rColMap, SCTAB nTab, SCROW nRow1, SCROW nRow2 ) :
+        maHint(rColMap, nTab, nRow1, nRow2) {}
+
+    void operator() ( SvtListener* p )
+    {
+        p->Notify(maHint);
+    }
+};
+
+}
 
 void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
 {
@@ -531,20 +556,28 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
 
     size_t nCount = pArray->GetCount();
     SCCOLROW nStart = pArray->GetStart();
+    SCCOLROW nLast = pArray->GetLast();
     ScSortInfo** ppInfo = pArray->GetFirstArray();
 
     std::vector<ScSortInfo*> aTable(nCount);
+
     SCSIZE nPos;
     for ( nPos = 0; nPos < nCount; nPos++ )
         aTable[ppInfo[nPos]->nOrg - nStart] = ppInfo[nPos];
 
+    // Cut formula grouping at row and reference boundaries before the reordering.
+    ScRange aSortRange(nStart, aSortParam.nRow1, nTab, nLast, aSortParam.nRow2, nTab);
+    for (SCCOL nCol = nStart; nCol <= nLast; ++nCol)
+        aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
+
     SCCOLROW nDest = nStart;
     for ( nPos = 0; nPos < nCount; nPos++, nDest++ )
     {
         SCCOLROW nOrg = ppInfo[nPos]->nOrg;
         if ( nDest != nOrg )
         {
-            SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) );
+            aCol[nDest].Swap(aCol[nOrg], aSortParam.nRow1, aSortParam.nRow2, aSortParam.bIncludePattern);
+
             // neue Position des weggeswapten eintragen
             ScSortInfo* p = ppInfo[nPos];
             p->nOrg = nDest;
@@ -556,6 +589,44 @@ void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress* pProgress )
         if(pProgress)
             pProgress->SetStateOnPercent( nPos );
     }
+
+    // Reset formula cell positions which became out-of-sync after column reordering.
+    for (SCCOL nCol = nStart; nCol <= nLast; ++nCol)
+        aCol[nCol].ResetFormulaCellPositions(aSortParam.nRow1, aSortParam.nRow2);
+
+    // Set up column reorder map (for later broadcasting of reference updates).
+    sc::ColReorderMapType aColMap;
+    const std::vector<SCCOLROW>& rOldIndices = pArray->GetOldIndices();
+    for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
+    {
+        SCCOL nNew = i + nStart;
+        SCROW nOld = rOldIndices[i];
+        aColMap.insert(sc::ColReorderMapType::value_type(nOld, nNew));
+    }
+
+    // Collect all listeners within sorted range ahead of time.
+    std::vector<SvtListener*> aListeners;
+    for (SCCOL nCol = nStart; nCol <= nLast; ++nCol)
+        aCol[nCol].CollectListeners(aListeners, aSortParam.nRow1, aSortParam.nRow2);
+
+    // Remove any duplicate listener entries and notify all listeners
+    // afterward.  We must ensure that we notify each unique listener only
+    // once.
+    std::sort(aListeners.begin(), aListeners.end());
+    aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
+    ColReorderNotifier aFunc(aColMap, nTab, aSortParam.nRow1, aSortParam.nRow2);
+    std::for_each(aListeners.begin(), aListeners.end(), aFunc);
+
+    // Re-join formulas at row boundaries now that all the references have
+    // been adjusted for column reordering.
+    for (SCCOL nCol = nStart; nCol <= nLast; ++nCol)
+    {
+        sc::CellStoreType& rCells = aCol[nCol].maCells;
+        sc::CellStoreType::position_type aPos = rCells.position(aSortParam.nRow1);
+        sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+        aPos = rCells.position(aPos.first, aSortParam.nRow2+1);
+        sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+    }
 }
 
 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, ScProgress* pProgress )
@@ -913,28 +984,6 @@ void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi )
     }
 }
 
-void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2)
-{
-    SCROW nRowStart = aSortParam.nRow1;
-    SCROW nRowEnd = aSortParam.nRow2;
-    for (SCROW nRow = nRowStart; nRow <= nRowEnd; nRow++)
-    {
-        aCol[nCol1].SwapCell(nRow, aCol[nCol2]);
-        if (aSortParam.bIncludePattern)
-        {
-            const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow);
-            const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow);
-            if (pPat1 != pPat2)
-            {
-                pDocument->GetPool()->Put(*pPat1);
-                SetPattern(nCol1, nRow, *pPat2, true);
-                SetPattern(nCol2, nRow, *pPat1, true);
-                pDocument->GetPool()->Remove(*pPat1);
-            }
-        }
-    }
-}
-
 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
 {
     short nRes;
diff --git a/sc/source/core/tool/refhint.cxx b/sc/source/core/tool/refhint.cxx
index eb07b4fe..0e70b4f 100644
--- a/sc/source/core/tool/refhint.cxx
+++ b/sc/source/core/tool/refhint.cxx
@@ -31,6 +31,31 @@ const ScAddress& RefMovedHint::getDelta() const
     return maMoveDelta;
 }
 
+RefColReorderHint::RefColReorderHint( const sc::ColReorderMapType& rColMap, SCTAB nTab, SCROW nRow1, SCROW nRow2 ) :
+    RefHint(ColumnReordered), mrColMap(rColMap), mnTab(nTab), mnRow1(nRow1), mnRow2(nRow2) {}
+
+RefColReorderHint::~RefColReorderHint() {}
+
+const sc::ColReorderMapType& RefColReorderHint::getColMap() const
+{
+    return mrColMap;
+}
+
+SCTAB RefColReorderHint::getTab() const
+{
+    return mnTab;
+}
+
+SCROW RefColReorderHint::getStartRow() const
+{
+    return mnRow1;
+}
+
+SCROW RefColReorderHint::getEndRow() const
+{
+    return mnRow2;
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 016ff79..bd884eb 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -2879,6 +2879,70 @@ void ScTokenArray::MoveReference(
     }
 }
 
+void ScTokenArray::MoveReference(
+    const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColReorderMapType& rColMap )
+{
+    FormulaToken** p = pCode;
+    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+    for (; p != pEnd; ++p)
+    {
+        switch ((*p)->GetType())
+        {
+            case svSingleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                ScSingleRefData& rRef = pToken->GetSingleRef();
+                ScAddress aAbs = rRef.toAbs(rPos);
+
+                if (aAbs.Tab() == nTab && nRow1 <= aAbs.Row() && aAbs.Row() <= nRow2)
+                {
+                    // Inside reordered row range.
+                    sc::ColReorderMapType::const_iterator it = rColMap.find(aAbs.Col());
+                    if (it != rColMap.end())
+                    {
+                        // This column is reordered.
+                        SCCOL nNewCol = it->second;
+                        aAbs.SetCol(nNewCol);
+                        rRef.SetAddress(aAbs, rPos);
+                    }
+                }
+            }
+            break;
+            case svDoubleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                ScComplexRefData& rRef = pToken->GetDoubleRef();
+                ScRange aAbs = rRef.toAbs(rPos);
+
+                if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
+                    // Must be a single-sheet reference.
+                    break;
+
+                if (aAbs.aStart.Col() != aAbs.aEnd.Col())
+                    // Whole range must fit in a single column.
+                    break;
+
+                if (aAbs.aStart.Tab() == nTab && nRow1 <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= nRow2)
+                {
+                    // Inside reordered row range.
+                    sc::ColReorderMapType::const_iterator it = rColMap.find(aAbs.aStart.Col());
+                    if (it != rColMap.end())
+                    {
+                        // This column is reordered.
+                        SCCOL nNewCol = it->second;
+                        aAbs.aStart.SetCol(nNewCol);
+                        aAbs.aEnd.SetCol(nNewCol);
+                        rRef.SetRange(aAbs, rPos);
+                    }
+                }
+            }
+            break;
+            default:
+                ;
+        }
+    }
+}
+
 namespace {
 
 bool adjustSingleRefInName(
@@ -3292,6 +3356,36 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMovedTab( sc::RefUpdateMoveTa
     return aRes;
 }
 
+void ScTokenArray::AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos )
+{
+    FormulaToken** p = pCode;
+    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+    for (; p != pEnd; ++p)
+    {
+        switch ((*p)->GetType())
+        {
+            case svSingleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                ScSingleRefData& rRef = pToken->GetSingleRef();
+                ScAddress aAbs = rRef.toAbs(rOldPos);
+                rRef.SetAddress(aAbs, rNewPos);
+            }
+            break;
+            case svDoubleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                ScComplexRefData& rRef = pToken->GetDoubleRef();
+                ScRange aAbs = rRef.toAbs(rOldPos);
+                rRef.SetRange(aAbs, rNewPos);
+            }
+            break;
+            default:
+                ;
+        }
+    }
+}
+
 namespace {
 
 void clearTabDeletedFlag( ScSingleRefData& rRef, const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
@@ -3341,28 +3435,23 @@ void ScTokenArray::ClearTabDeleted( const ScAddress& rPos, SCTAB nStartTab, SCTA
 namespace {
 
 void checkBounds(
-    const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
-    const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
+    const ScAddress& rPos, SCROW nGroupLen, const ScRange& rCheckRange,
+    const ScSingleRefData& rRef, std::vector<SCROW>& rBounds )
 {
     if (!rRef.IsRowRel())
         return;
 
-    ScRange aCheckRange = rCxt.maRange;
-    if (rCxt.meMode == URM_MOVE)
-        // Check bounds against the old range prior to the move.
-        aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta);
-
     ScRange aAbs(rRef.toAbs(rPos));
     aAbs.aEnd.IncRow(nGroupLen-1);
-    if (!aCheckRange.Intersects(aAbs))
+    if (!rCheckRange.Intersects(aAbs))
         return;
 
     // Get the boundary row positions.
-    if (aAbs.aEnd.Row() < aCheckRange.aStart.Row())
+    if (aAbs.aEnd.Row() < rCheckRange.aStart.Row())
         // No intersections.
         return;
 
-    if (aAbs.aStart.Row() <= aCheckRange.aStart.Row())
+    if (aAbs.aStart.Row() <= rCheckRange.aStart.Row())
     {
         //    +-+ <---- top
         //    | |
@@ -3372,11 +3461,11 @@ void checkBounds(
         // +-------+
 
         // Add offset from the reference top to the cell position.
-        SCROW nOffset = aCheckRange.aStart.Row() - aAbs.aStart.Row();
+        SCROW nOffset = rCheckRange.aStart.Row() - aAbs.aStart.Row();
         rBounds.push_back(rPos.Row()+nOffset);
     }
 
-    if (aAbs.aEnd.Row() >= aCheckRange.aEnd.Row())
+    if (aAbs.aEnd.Row() >= rCheckRange.aEnd.Row())
     {
         // only check for end range
 
@@ -3388,11 +3477,26 @@ void checkBounds(
         //    +-+
 
         // Ditto.
-        SCROW nOffset = aCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
+        SCROW nOffset = rCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
         rBounds.push_back(rPos.Row()+nOffset);
     }
 }
 
+void checkBounds(
+    const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
+    const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
+{
+    if (!rRef.IsRowRel())
+        return;
+
+    ScRange aCheckRange = rCxt.maRange;
+    if (rCxt.meMode == URM_MOVE)
+        // Check bounds against the old range prior to the move.
+        aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta);
+
+    checkBounds(rPos, nGroupLen, aCheckRange, rRef, rBounds);
+}
+
 }
 
 void ScTokenArray::CheckRelativeReferenceBounds(
@@ -3424,6 +3528,36 @@ void ScTokenArray::CheckRelativeReferenceBounds(
     }
 }
 
+void ScTokenArray::CheckRelativeReferenceBounds(
+    const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const
+{
+    FormulaToken** p = pCode;
+    FormulaToken** pEnd = p + static_cast<size_t>(nLen);
+    for (; p != pEnd; ++p)
+    {
+        switch ((*p)->GetType())
+        {
+            case svSingleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                const ScSingleRefData& rRef = pToken->GetSingleRef();
+                checkBounds(rPos, nGroupLen, rRange, rRef, rBounds);
+            }
+            break;
+            case svDoubleRef:
+            {
+                ScToken* pToken = static_cast<ScToken*>(*p);
+                const ScComplexRefData& rRef = pToken->GetDoubleRef();
+                checkBounds(rPos, nGroupLen, rRange, rRef.Ref1, rBounds);
+                checkBounds(rPos, nGroupLen, rRange, rRef.Ref2, rBounds);
+            }
+            break;
+            default:
+                ;
+        }
+    }
+}
+
 namespace {
 
 void appendDouble( sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, double fVal )
commit 0e443b773454fa153827753812757d88eea932c5
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Thu May 1 01:14:06 2014 -0400

    fdo#78079: Write test for this.
    
    Change-Id: I3d3fc54cb433ec60d51341c3b4a077414a99a14c

diff --git a/sc/qa/unit/helper/qahelper.hxx b/sc/qa/unit/helper/qahelper.hxx
index a970f98..0b93cc4 100644
--- a/sc/qa/unit/helper/qahelper.hxx
+++ b/sc/qa/unit/helper/qahelper.hxx
@@ -125,6 +125,43 @@ SCQAHELPER_DLLPUBLIC bool checkFormulaPosition(ScDocument& rDoc, const ScAddress
 SCQAHELPER_DLLPUBLIC bool checkFormulaPositions(
     ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SCROW* pRows, size_t nRowCount);
 
+template<size_t _Size>
+bool checkOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
+{
+    bool bResult = true;
+    const ScAddress& s = aOutRange.aStart;
+    const ScAddress& e = aOutRange.aEnd;
+    SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
+    SCROW nOutRowSize = e.Row() - s.Row() + 1;
+    SCCOL nOutColSize = e.Col() - s.Col() + 1;
+    for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
+    {
+        for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
+        {
+            OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
+            printer.set(nRow, nCol, aVal);
+            const char* p = aOutputCheck[nRow][nCol];
+            if (p)
+            {
+                OUString aCheckVal = OUString::createFromAscii(p);
+                bool bEqual = aCheckVal.equals(aVal);
+                if (!bEqual)
+                {
+                    cout << "Expected: " << aCheckVal << "  Actual: " << aVal << endl;
+                    bResult = false;
+                }
+            }
+            else if (!aVal.isEmpty())
+            {
+                cout << "Empty cell expected" << endl;
+                bResult = false;
+            }
+        }
+    }
+    printer.print(pCaption);
+    return bResult;
+}
+
 SCQAHELPER_DLLPUBLIC void clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange );
 
 /**
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 70e7f7b..ace06b2 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4785,6 +4785,80 @@ void Test::testSort()
     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 {
diff --git a/sc/qa/unit/ucalc.hxx b/sc/qa/unit/ucalc.hxx
index c26a433..2a256a1 100644
--- a/sc/qa/unit/ucalc.hxx
+++ b/sc/qa/unit/ucalc.hxx
@@ -322,6 +322,7 @@ public:
     void testFindAreaPosVertical();
     void testFindAreaPosColRight();
     void testSort();
+    void testSortHorizontal();
     void testSortWithFormulaRefs();
     void testSortWithStrings();
     void testSortInFormulaGroup();
@@ -478,6 +479,7 @@ public:
     CPPUNIT_TEST(testFindAreaPosVertical);
     CPPUNIT_TEST(testFindAreaPosColRight);
     CPPUNIT_TEST(testSort);
+    CPPUNIT_TEST(testSortHorizontal);
     CPPUNIT_TEST(testSortWithFormulaRefs);
     CPPUNIT_TEST(testSortWithStrings);
     CPPUNIT_TEST(testSortInFormulaGroup);
diff --git a/sc/qa/unit/ucalc_pivottable.cxx b/sc/qa/unit/ucalc_pivottable.cxx
index 85acd61..5f742a1 100644
--- a/sc/qa/unit/ucalc_pivottable.cxx
+++ b/sc/qa/unit/ucalc_pivottable.cxx
@@ -74,38 +74,7 @@ ScRange insertDPSourceData(ScDocument* pDoc, DPFieldDef aFields[], size_t nField
 template<size_t _Size>
 bool checkDPTableOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
 {
-    bool bResult = true;
-    const ScAddress& s = aOutRange.aStart;
-    const ScAddress& e = aOutRange.aEnd;
-    SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
-    SCROW nOutRowSize = e.Row() - s.Row() + 1;
-    SCCOL nOutColSize = e.Col() - s.Col() + 1;
-    for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
-    {
-        for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
-        {
-            OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
-            printer.set(nRow, nCol, aVal);
-            const char* p = aOutputCheck[nRow][nCol];
-            if (p)
-            {
-                OUString aCheckVal = OUString::createFromAscii(p);
-                bool bEqual = aCheckVal.equals(aVal);
-                if (!bEqual)
-                {
-                    cout << "Expected: " << aCheckVal << "  Actual: " << aVal << endl;
-                    bResult = false;
-                }
-            }
-            else if (!aVal.isEmpty())
-            {
-                cout << "Empty cell expected" << endl;
-                bResult = false;
-            }
-        }
-    }
-    printer.print(pCaption);
-    return bResult;
+    return checkOutput<_Size>(pDoc, aOutRange, aOutputCheck, pCaption);
 }
 
 ScDPObject* createDPFromSourceDesc(


More information about the Libreoffice-commits mailing list