/* -*- 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/** Splits a given string into three parts: the prefix, number string, and the suffix.
@param sWhole Original string to be split into pieces
@param sPrefix Prefix string that consists of the part before the first number token. If no number was found, sPrefix is unchanged.
@param sSuffix String after the last number token. This may still contain number strings. If no number was found, sSuffix is unchanged.
@param fNum Number converted from the middle number string If no number was found, fNum is unchanged.
@return Returns TRUE if a numeral element is found in a given string, or FALSE if no numeral element is found.
*/ staticbool SplitString( const OUString &sWhole,
OUString &sPrefix, OUString &sSuffix, double &fNum )
{ // Get prefix element, search for any digit and stop.
sal_Int32 nPos = 0; while (nPos < sWhole.getLength())
{ const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos); if (nType & KCharacterType::DIGIT) break;
sWhole.iterateCodePoints( &nPos );
}
// Return FALSE if no numeral element is found if ( nPos == sWhole.getLength() ) returnfalse;
// The prefix and the first numerical elements are equal, but the suffix // strings may still differ. Stay in the loop.
sStr1 = sSuf1;
sStr2 = sSuf2;
} while (true);
return 0;
}
}
// Assume that we can handle 512MB, which with a ~100 bytes // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus // overhead in one chunk.
constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
if (rParam.mbByRow)
{ // Create a sort info array with just the data table.
SCROW nRow1 = rParam.maSortRange.aStart.Row();
SCROW nRow2 = rParam.maSortRange.aEnd.Row();
SCCOL nCol1 = rParam.maSortRange.aStart.Col();
SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
size_t nColCount = nCol2 - nCol1 + 1;
std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
aSortedCols.reserve(nColCount); for (size_t i = 0; i < nColCount; ++i)
{ // In the sorted column container, element positions and row // positions must match, else formula cells may mis-behave during // grouping.
aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
}
for (size_t i = 0; i < pRows->size(); ++i)
{ const SCROW nRow = nRow1 + i;
// If bOnlyDataAreaExtras, // sc::CellStoreType aSortedCols.at(j)->maCells // and // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs // are by definition all empty mdds::multi_type_vector, so nothing // needs to be done to push *all* empty.
if (!bOnlyDataAreaExtras)
{
sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells; switch (rCell.maCell.getType())
{ case CELLTYPE_STRING:
assert(rCell.mpAttr);
rCellStore.push_back(*rCell.maCell.getSharedString()); break; case CELLTYPE_VALUE:
assert(rCell.mpAttr);
rCellStore.push_back(rCell.maCell.getDouble()); break; case CELLTYPE_EDIT:
assert(rCell.mpAttr);
rCellStore.push_back(rCell.maCell.getEditText()->Clone().release()); break; case CELLTYPE_FORMULA:
{
assert(rCell.mpAttr);
ScAddress aOldPos = rCell.maCell.getFormula()->aPos;
if (!rCellListeners.empty())
{ // Original source cells will be deleted during // sc::CellStoreType::transfer(), SvtListener is a base // class, so we need to replace it. auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.getFormula())); if (it != rCellListeners.end())
*it = pNew;
}
rCellStore.push_back(pNew);
} break; default: //assert(!rCell.mpAttr); // This assert doesn't hold, for example // CopyCellsFromClipHandler may omit copying cells during // PasteSpecial for which CopyTextAttrsFromClipHandler // still copies a CellTextAttr. So if that really is not // expected then fix it there.
rCellStore.push_back_empty();
}
sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs; if (rCell.mpAttr)
rAttrStore.push_back(*rCell.mpAttr); else
rAttrStore.push_back_empty();
}
if (pArray->IsUpdateRefs())
{ // At this point each broadcaster instance is managed by 2 // containers. We will release those in the original storage // below before transferring them to the document. const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters; if (pBroadcaster) // A const pointer would be implicitly converted to a bool type.
rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster)); else
rBCStore.push_back_empty();
}
// The same with cell note instances ...
sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes; if (rCell.mpNote)
rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote)); else
rNoteStore.push_back_empty();
if (rCell.maPattern)
aSortedCols.at(j)->setPattern(nRow, rCell.maPattern);
}
if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
{ // Hidden and filtered flags are first converted to segments.
aRowFlags.setRowHidden(nRow, rRow.mbHidden);
aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
}
// Cut formula grouping at row and reference boundaries before the reordering.
ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab); for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
// Collect all listeners of cell broadcasters of sorted range.
std::vector<SvtListener*> aCellListeners;
if (!pArray->IsUpdateRefs())
{ // Collect listeners of cell broadcasters. for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
// Remove any duplicate listener entries. We must ensure that we // notify each unique listener only once.
std::sort(aCellListeners.begin(), aCellListeners.end());
aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
// Notify the cells' listeners to stop listening. /* TODO: for performance this could be enhanced to stop and later * restart only listening to within the reordered range and keep
* listening to everything outside untouched. */
sc::RefStopListeningHint aHint; for (autoconst & l : aCellListeners)
l->Notify(aHint);
}
// table to keep track of column index to position in the index table.
std::vector<SCCOLROW> aPosTable(nCount); for (size_t i = 0; i < nCount; ++i)
aPosTable[aIndices[i]-nStart] = i;
SCCOLROW nDest = nStart; for (size_t i = 0; i < nCount; ++i, ++nDest)
{
SCCOLROW nSrc = aIndices[i]; if (nDest != nSrc)
{
aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
// Update the position of the index that was originally equal to nDest.
size_t nPos = aPosTable[nDest-nStart];
aIndices[nPos] = nSrc;
aPosTable[nSrc-nStart] = nPos;
}
if (pProgress)
pProgress->SetStateOnPercent(i);
}
// Reset formula cell positions which became out-of-sync after column reordering. bool bUpdateRefs = pArray->IsUpdateRefs(); for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
if (pArray->IsUpdateRefs())
{ // Set up column reorder map (for later broadcasting of reference updates).
sc::ColRowReorderMapType aColMap; const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices(); for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
{
SCCOL nNew = i + nStart;
SCCOL nOld = rOldIndices[i];
aColMap.emplace(nOld, nNew);
}
// Collect all listeners within sorted range ahead of time.
std::vector<SvtListener*> aListeners;
// Get all area listeners that listen on one column within the range // and end their listening.
ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
aMoveRange, sc::AreaOverlapType::OneColumnInside);
{ for (auto& rAreaListener : aAreaListeners)
{
rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
aListeners.push_back( rAreaListener.mpListener);
}
}
// 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());
{
ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
std::for_each(aListeners.begin(), aListeners.end(), std::move(aFunc));
}
// Re-start area listeners on the reordered columns.
{ for (auto& rAreaListener : aAreaListeners)
{
ScRange aNewRange = rAreaListener.maArea;
sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col()); if (itCol != aColMap.end())
{
aNewRange.aStart.SetCol( itCol->second);
aNewRange.aEnd.SetCol( itCol->second);
}
rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
}
}
} else// !(pArray->IsUpdateRefs())
{ // Notify the cells' listeners to (re-)start listening.
sc::RefStartListeningHint aHint; for (autoconst & l : aCellListeners)
l->Notify(aHint);
}
// Re-join formulas at row boundaries now that all the references have // been adjusted for column reordering. for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
{
sc::CellStoreType& rCells = aCol[nCol].maCells;
sc::CellStoreType::position_type aPos = rCells.position(nRow1);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos); if (nRow2 < rDocument.MaxRow())
{
aPos = rCells.position(aPos.first, nRow2+1);
sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
}
}
}
// bOnlyDataAreaExtras: // Data area extras by definition do not have any cell content so no // formula cells either, so that handling doesn't need to be executed. // However, there may be listeners of formulas listening to broadcasters of // empty cells.
// Collect all listeners of cell broadcasters of sorted range.
std::vector<SvtListener*> aCellListeners;
// When the update ref mode is disabled, we need to detach all formula // cells in the sorted range before reordering, and re-start them // afterward. if (!bOnlyDataAreaExtras)
{
sc::EndListeningContext aCxt(rDocument);
DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
}
// Collect listeners of cell broadcasters. for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
// Remove any duplicate listener entries. We must ensure that we notify // each unique listener only once.
std::sort(aCellListeners.begin(), aCellListeners.end());
aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
// Notify the cells' listeners to stop listening. /* TODO: for performance this could be enhanced to stop and later * restart only listening to within the reordered range and keep
* listening to everything outside untouched. */
{
sc::RefStopListeningHint aHint; for (autoconst & l : aCellListeners)
l->Notify(aHint);
}
// Split formula groups at the sort range boundaries (if applicable). if (!bOnlyDataAreaExtras)
{
std::vector<SCROW> aRowBounds
{
nRow1,
nRow2+1
}; for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
SplitFormulaGroups(nCol, aRowBounds);
}
// Cells in the data rows only reference values in the document. Make // a copy before updating the document.
std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2,
pProgress, this, bOnlyDataAreaExtras);
for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
{
SCCOL nThisCol = i + nCol1;
// Do the same as broadcaster storage transfer (to prevent double deletion).
rDest.release_range(nRow1, nRow2);
rSrc.transfer(nRow1, nRow2, rDest, nRow1);
aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
}
{ // Get all row spans where the pattern is not NULL.
std::vector<PatternSpan> aSpans =
sc::toSpanArrayWithValue<SCROW, CellAttributeHolder, PatternSpan>(
aSortedCols[i]->maPatterns);
if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
{
aRowFlags.maRowsHidden.build_tree();
aRowFlags.maRowsFiltered.build_tree();
// Backup visibility state of objects. States will be lost when changing the flags below.
std::vector<std::vector<std::vector<bool>>> aBackup;
backupObjectsVisibility(aSortedCols, aBackup);
// Remove all flags in the range first.
SetRowHidden(nRow1, nRow2, false);
SetRowFiltered(nRow1, nRow2, false);
// If the range is within the sorted range, we need to expand its rows // to the top and bottom of the sorted range, since the formula cells // could be anywhere in the sorted range after reordering. for (size_t i = 0, n = aTmp.size(); i < n; ++i)
{
ScRange aRange = aTmp[i]; if (!aMoveRange.Intersects(aRange))
{ // Doesn't overlap with the sorted range at all.
aGrpListenerRanges.set(GetDoc(), aRange, true); continue;
}
if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
{ // Its column range is within the column range of the sorted range.
expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
aGrpListenerRanges.set(GetDoc(), aRange, true); continue;
}
// It intersects with the sorted range, but its column range is // not within the column range of the sorted range. Split it into // 2 ranges.
ScRange aR1 = aRange;
ScRange aR2 = aRange; if (aRange.aStart.Col() < aMoveRange.aStart.Col())
{ // Left half is outside the sorted range while the right half is inside.
aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
aR2.aStart.SetCol(aMoveRange.aStart.Col());
expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
} else
{ // Left half is inside the sorted range while the right half is outside.
aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
aR2.aStart.SetCol(aMoveRange.aEnd.Col());
expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
}
// Split formula groups at the sort range boundaries (if applicable).
std::vector<SCROW> aRowBounds
{
nRow1,
nRow2+1
}; for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
SplitFormulaGroups(nCol, aRowBounds);
// Cells in the data rows only reference values in the document. Make // a copy before updating the document.
std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
std::vector<SvtListener*> aListenersDummy;
fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
{
SCCOL nThisCol = i + nCol1;
// Do the same as broadcaster storage transfer (to prevent double deletion).
rDest.release_range(nRow1, nRow2);
rSrc.transfer(nRow1, nRow2, rDest, nRow1);
aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
}
{ // Get all row spans where the pattern is not NULL.
std::vector<PatternSpan> aSpans =
sc::toSpanArrayWithValue<SCROW, CellAttributeHolder, PatternSpan>(
aSortedCols[i]->maPatterns);
if (pArray->IsKeepQuery())
{
aRowFlags.maRowsHidden.build_tree();
aRowFlags.maRowsFiltered.build_tree();
// Backup visibility state of objects. States will be lost when changing the flags below.
std::vector<std::vector<std::vector<bool>>> aBackup;
backupObjectsVisibility(aSortedCols, aBackup);
// Remove all flags in the range first.
SetRowHidden(nRow1, nRow2, false);
SetRowFiltered(nRow1, nRow2, false);
for (constauto& rSpan : aSpans)
SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
//Restore visibility state of objects
restoreObjectsVisibility(aSortedCols, aBackup);
}
// Update draw object positions for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
{
SCCOL nThisCol = i + nCol1;
aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
}
// Set up row reorder map (for later broadcasting of reference updates).
sc::ColRowReorderMapType aRowMap; const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices(); for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
{
SCROW nNew = i + nRow1;
SCROW nOld = rOldIndices[i];
aRowMap.emplace(nOld, nNew);
}
// Collect all listeners within sorted range ahead of time.
std::vector<SvtListener*> aListeners;
// Collect listeners of cell broadcasters. for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
// Get all area listeners that listen on one row within the range and end // their listening.
std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
aMoveRange, sc::AreaOverlapType::OneRowInside);
{ for (auto& rAreaListener : aAreaListeners)
{
rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
aListeners.push_back( rAreaListener.mpListener);
}
}
{ // Get all formula cells from the former group area listener ranges.
// Remove any duplicate listener entries. 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());
// Collect positions of all shared formula cells outside the sorted range, // and make them unshared before notifying them.
sc::RefQueryFormulaGroup aFormulaGroupPos;
aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
// Notify the listeners to update their references.
{
ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
std::for_each(aListeners.begin(), aListeners.end(), std::move(aFunc));
}
// Re-group formulas in affected columns. for (constauto& [rTab, rCols] : rGroupTabs)
{ for (constauto& rEntry : rCols)
rDocument.RegroupFormulaCells(rTab, rEntry.first);
}
// Re-start area listeners on the reordered rows. for (constauto& rAreaListener : aAreaListeners)
{
ScRange aNewRange = rAreaListener.maArea;
sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row()); if (itRow != aRowMap.end())
{
aNewRange.aStart.SetRow( itRow->second);
aNewRange.aEnd.SetRow( itRow->second);
}
rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
}
// Re-group columns in the sorted range too. for (SCCOL i = nCol1; i <= nCol2; ++i)
aCol[i].RegroupFormulaCells();
{ // Re-start area listeners on the old group listener ranges.
ListenerStartAction aAction(rDocument);
aGrpListenerRanges.executeColumnAction(rDocument, aAction);
}
}
if (pUndo)
{ // Stored is the first data column without header column.
pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
pUndo->maDataAreaExtras.mnStartCol = nCol1;
pUndo->maOrderIndices = pArray->GetOrderIndices();
}
}
}
DestroySortCollator();
}
void ScTable::Reorder( const sc::ReorderParam& rParam )
{ if (rParam.maOrderIndices.empty()) return;
std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam)); if (!pArray) return;
if (rParam.mbByRow)
{ // Re-play sorting from the known sort indices.
pArray->ReorderByRow(rParam.maOrderIndices); if (pArray->IsUpdateRefs())
SortReorderByRowRefUpdate(
pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr); else
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.