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.yosh...@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.yosh...@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( _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits