Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / table3.cxx
blobf5b66a250ea7fb5cb6f87e321d1bbf22842ed9ba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/processfactory.hxx>
21 #include <comphelper/random.hxx>
22 #include <svl/numformat.hxx>
23 #include <svl/zforlist.hxx>
24 #include <svl/zformat.hxx>
25 #include <unotools/collatorwrapper.hxx>
26 #include <stdlib.h>
27 #include <com/sun/star/i18n/KParseTokens.hpp>
28 #include <com/sun/star/i18n/KParseType.hpp>
29 #include <sal/log.hxx>
30 #include <osl/diagnose.h>
32 #include <refdata.hxx>
33 #include <table.hxx>
34 #include <scitems.hxx>
35 #include <formulacell.hxx>
36 #include <document.hxx>
37 #include <globstr.hrc>
38 #include <scresid.hxx>
39 #include <global.hxx>
40 #include <stlpool.hxx>
41 #include <patattr.hxx>
42 #include <subtotal.hxx>
43 #include <markdata.hxx>
44 #include <rangelst.hxx>
45 #include <userlist.hxx>
46 #include <progress.hxx>
47 #include <queryparam.hxx>
48 #include <queryentry.hxx>
49 #include <subtotalparam.hxx>
50 #include <cellvalue.hxx>
51 #include <tokenarray.hxx>
52 #include <mtvcellfunc.hxx>
53 #include <columnspanset.hxx>
54 #include <fstalgorithm.hxx>
55 #include <listenercontext.hxx>
56 #include <sharedformula.hxx>
57 #include <stlsheet.hxx>
58 #include <refhint.hxx>
59 #include <listenerquery.hxx>
60 #include <bcaslot.hxx>
61 #include <reordermap.hxx>
62 #include <drwlayer.hxx>
63 #include <queryevaluator.hxx>
64 #include <scopetools.hxx>
66 #include <svl/sharedstringpool.hxx>
68 #include <memory>
69 #include <set>
70 #include <unordered_set>
71 #include <vector>
72 #include <mdds/flat_segment_tree.hpp>
74 using namespace ::com::sun::star;
76 namespace naturalsort {
78 using namespace ::com::sun::star::i18n;
80 /** Splits a given string into three parts: the prefix, number string, and
81 the suffix.
83 @param sWhole
84 Original string to be split into pieces
86 @param sPrefix
87 Prefix string that consists of the part before the first number token.
88 If no number was found, sPrefix is unchanged.
90 @param sSuffix
91 String after the last number token. This may still contain number strings.
92 If no number was found, sSuffix is unchanged.
94 @param fNum
95 Number converted from the middle number string
96 If no number was found, fNum is unchanged.
98 @return Returns TRUE if a numeral element is found in a given string, or
99 FALSE if no numeral element is found.
101 static bool SplitString( const OUString &sWhole,
102 OUString &sPrefix, OUString &sSuffix, double &fNum )
104 // Get prefix element, search for any digit and stop.
105 sal_Int32 nPos = 0;
106 while (nPos < sWhole.getLength())
108 const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos);
109 if (nType & KCharacterType::DIGIT)
110 break;
111 sWhole.iterateCodePoints( &nPos );
114 // Return FALSE if no numeral element is found
115 if ( nPos == sWhole.getLength() )
116 return false;
118 // Get numeral element
119 const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep();
120 ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken(
121 KParseType::ANY_NUMBER, sWhole, nPos,
122 KParseTokens::ANY_NUMBER, u""_ustr, KParseTokens::ANY_NUMBER, sUser );
124 if ( aPRNum.EndPos == nPos )
126 SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
127 nPos << " : " << sWhole);
128 return false;
131 sPrefix = sWhole.copy( 0, nPos );
132 fNum = aPRNum.Value;
133 sSuffix = sWhole.copy( aPRNum.EndPos );
135 return true;
138 /** Naturally compares two given strings.
140 This is the main function that should be called externally. It returns
141 either 1, 0, or -1 depending on the comparison result of given two strings.
143 @param sInput1
144 Input string 1
146 @param sInput2
147 Input string 2
149 @param bCaseSens
150 Boolean value for case sensitivity
152 @param pData
153 Pointer to user defined sort list
155 @param pCW
156 Pointer to collator wrapper for normal string comparison
158 @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
159 sInput2 is greater.
161 static short Compare( const OUString &sInput1, const OUString &sInput2,
162 const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
164 OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
168 double nNum1, nNum2;
169 bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
170 bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
172 short nPreRes; // Prefix comparison result
173 if ( pData )
175 if ( bCaseSens )
177 if ( !bNumFound1 || !bNumFound2 )
178 return static_cast<short>(pData->Compare( sStr1, sStr2 ));
179 else
180 nPreRes = pData->Compare( sPre1, sPre2 );
182 else
184 if ( !bNumFound1 || !bNumFound2 )
185 return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
186 else
187 nPreRes = pData->ICompare( sPre1, sPre2 );
190 else
192 if ( !bNumFound1 || !bNumFound2 )
193 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
194 else
195 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
198 // Prefix strings differ. Return immediately.
199 if ( nPreRes != 0 ) return nPreRes;
201 if ( nNum1 != nNum2 )
203 if ( nNum1 < nNum2 ) return -1;
204 return (nNum1 > nNum2) ? 1 : 0;
207 // The prefix and the first numerical elements are equal, but the suffix
208 // strings may still differ. Stay in the loop.
210 sStr1 = sSuf1;
211 sStr2 = sSuf2;
213 } while (true);
215 return 0;
220 // Assume that we can handle 512MB, which with a ~100 bytes
221 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
222 // overhead in one chunk.
223 constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
225 namespace {
227 void initDataRows(
228 ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
229 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
230 bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras )
232 // Fill row-wise data table.
233 ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
235 const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices();
236 assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1)
237 && nRow1 == rArray.GetStart()));
239 ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr);
240 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
242 ScColumn& rCol = rCols[nCol];
244 // Skip reordering of cell formats if the whole span is on the same pattern entry.
245 bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
247 sc::ColumnBlockConstPosition aBlockPos;
248 rCol.InitBlockPosition(aBlockPos);
249 std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
250 if (pDrawLayer)
251 aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
253 for (SCROW nR = nRow1; nR <= nRow2; ++nR)
255 const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR);
256 ScSortInfoArray::Row& rRow = rRows[nR-nRow1];
257 ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
258 if (!bOnlyDataAreaExtras)
260 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
261 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
263 if (bCellNotes)
264 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
265 if (pDrawLayer)
266 rCell.maDrawObjects = aRowDrawObjects[nRow];
268 if (!bUniformPattern && bPattern)
269 rCell.maPattern.setScPatternAttr(rCol.GetPattern(nRow));
273 if (!bOnlyDataAreaExtras && bHiddenFiltered)
275 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
277 ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
278 rRow.mbHidden = rTab.RowHidden(nRow);
279 rRow.mbFiltered = rTab.RowFiltered(nRow);
286 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
288 std::unique_ptr<ScSortInfoArray> pArray;
290 if (rParam.mbByRow)
292 // Create a sort info array with just the data table.
293 SCROW nRow1 = rParam.maSortRange.aStart.Row();
294 SCROW nRow2 = rParam.maSortRange.aEnd.Row();
295 SCCOL nCol1 = rParam.maSortRange.aStart.Col();
296 SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
298 pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
299 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
300 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
302 CreateColumnIfNotExists(nCol2);
303 initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered,
304 rParam.maDataAreaExtras.mbCellFormats, true, true, false);
306 else
308 SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
309 SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
311 pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
312 pArray->SetKeepQuery(rParam.mbHiddenFiltered);
313 pArray->SetUpdateRefs(rParam.mbUpdateRefs);
316 return pArray;
319 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
320 const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
321 bool bKeepQuery, bool bUpdateRefs )
323 sal_uInt16 nUsedSorts = 1;
324 while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
325 nUsedSorts++;
326 std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
327 pArray->SetKeepQuery(bKeepQuery);
328 pArray->SetUpdateRefs(bUpdateRefs);
330 if ( rSortParam.bByRow )
332 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
334 SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
335 ScColumn* pCol = &aCol[nCol];
336 sc::ColumnBlockConstPosition aBlockPos;
337 pCol->InitBlockPosition(aBlockPos);
338 for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
340 ScSortInfo & rInfo = pArray->Get( nSort, nRow );
341 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
342 rInfo.nOrg = nRow;
346 CreateColumnIfNotExists(rSortParam.nCol2);
347 initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery,
348 rSortParam.aDataAreaExtras.mbCellFormats, true, true, false);
350 else
352 for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
354 SCROW nRow = rSortParam.maKeyState[nSort].nField;
355 for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
356 nCol <= static_cast<SCCOL>(nInd2); nCol++ )
358 ScSortInfo & rInfo = pArray->Get( nSort, nCol );
359 rInfo.maCell = GetCellValue(nCol, nRow);
360 rInfo.nOrg = nCol;
364 return pArray;
367 namespace {
369 struct SortedColumn
371 typedef mdds::flat_segment_tree<SCROW, CellAttributeHolder> PatRangeType;
373 sc::CellStoreType maCells;
374 sc::CellTextAttrStoreType maCellTextAttrs;
375 sc::BroadcasterStoreType maBroadcasters;
376 sc::CellNoteStoreType maCellNotes;
377 std::vector<std::vector<SdrObject*>> maCellDrawObjects;
379 PatRangeType maPatterns;
380 PatRangeType::const_iterator miPatternPos;
382 SortedColumn(const SortedColumn&) = delete;
383 const SortedColumn operator=(const SortedColumn&) = delete;
385 explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
386 maCells(nTopEmptyRows),
387 maCellTextAttrs(nTopEmptyRows),
388 maBroadcasters(nTopEmptyRows),
389 maCellNotes(nTopEmptyRows),
390 maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
391 miPatternPos(maPatterns.begin()) {}
393 void setPattern( SCROW nRow, const CellAttributeHolder& rPat )
395 miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, rPat).first;
399 struct SortedRowFlags
401 typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
403 FlagsType maRowsHidden;
404 FlagsType maRowsFiltered;
405 FlagsType::const_iterator miPosHidden;
406 FlagsType::const_iterator miPosFiltered;
408 SortedRowFlags(const ScSheetLimits& rSheetLimits) :
409 maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
410 maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
411 miPosHidden(maRowsHidden.begin()),
412 miPosFiltered(maRowsFiltered.begin()) {}
414 void setRowHidden( SCROW nRow, bool b )
416 miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
419 void setRowFiltered( SCROW nRow, bool b )
421 miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
424 void swap( SortedRowFlags& r )
426 maRowsHidden.swap(r.maRowsHidden);
427 maRowsFiltered.swap(r.maRowsFiltered);
429 // Just reset the position hints.
430 miPosHidden = maRowsHidden.begin();
431 miPosFiltered = maRowsFiltered.begin();
435 struct PatternSpan
437 SCROW mnRow1;
438 SCROW mnRow2;
439 CellAttributeHolder maPattern;
441 PatternSpan( SCROW nRow1, SCROW nRow2, const CellAttributeHolder& rPat ) :
442 mnRow1(nRow1), mnRow2(nRow2), maPattern(rPat) {}
447 bool ScTable::IsSortCollatorGlobal() const
449 return pSortCollator == &ScGlobal::GetCollator() ||
450 pSortCollator == &ScGlobal::GetCaseCollator();
453 void ScTable::InitSortCollator( const ScSortParam& rPar )
455 if ( !rPar.aCollatorLocale.Language.isEmpty() )
457 if ( !pSortCollator || IsSortCollatorGlobal() )
458 pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
459 pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
460 rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
462 else
463 { // SYSTEM
464 DestroySortCollator();
465 pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens);
469 void ScTable::DestroySortCollator()
471 if ( pSortCollator )
473 if ( !IsSortCollatorGlobal() )
474 delete pSortCollator;
475 pSortCollator = nullptr;
479 namespace {
481 template<typename Hint, typename ReorderMap, typename Index>
482 class ReorderNotifier
484 Hint maHint;
485 public:
486 ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
487 maHint(rMap, nTab, nPos1, nPos2) {}
489 void operator() ( SvtListener* p )
491 p->Notify(maHint);
495 class FormulaGroupPosCollector
497 sc::RefQueryFormulaGroup& mrQuery;
499 public:
500 explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
502 void operator() ( const SvtListener* p )
504 p->Query(mrQuery);
508 void fillSortedColumnArray(
509 std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
510 SortedRowFlags& rRowFlags,
511 std::vector<SvtListener*>& rCellListeners,
512 ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable,
513 bool bOnlyDataAreaExtras )
515 assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs());
517 SCROW nRow1 = pArray->GetStart();
518 ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
519 std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
521 size_t nColCount = nCol2 - nCol1 + 1;
522 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
523 SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
524 aSortedCols.reserve(nColCount);
525 for (size_t i = 0; i < nColCount; ++i)
527 // In the sorted column container, element positions and row
528 // positions must match, else formula cells may mis-behave during
529 // grouping.
530 aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
533 for (size_t i = 0; i < pRows->size(); ++i)
535 const SCROW nRow = nRow1 + i;
537 ScSortInfoArray::Row& rRow = (*pRows)[i];
538 for (size_t j = 0; j < rRow.maCells.size(); ++j)
540 ScSortInfoArray::Cell& rCell = rRow.maCells[j];
542 // If bOnlyDataAreaExtras,
543 // sc::CellStoreType aSortedCols.at(j)->maCells
544 // and
545 // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
546 // are by definition all empty mdds::multi_type_vector, so nothing
547 // needs to be done to push *all* empty.
549 if (!bOnlyDataAreaExtras)
551 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
552 switch (rCell.maCell.getType())
554 case CELLTYPE_STRING:
555 assert(rCell.mpAttr);
556 rCellStore.push_back(*rCell.maCell.getSharedString());
557 break;
558 case CELLTYPE_VALUE:
559 assert(rCell.mpAttr);
560 rCellStore.push_back(rCell.maCell.getDouble());
561 break;
562 case CELLTYPE_EDIT:
563 assert(rCell.mpAttr);
564 rCellStore.push_back(rCell.maCell.getEditText()->Clone().release());
565 break;
566 case CELLTYPE_FORMULA:
568 assert(rCell.mpAttr);
569 ScAddress aOldPos = rCell.maCell.getFormula()->aPos;
571 const ScAddress aCellPos(nCol1 + j, nRow, nTab);
572 ScFormulaCell* pNew = rCell.maCell.getFormula()->Clone( aCellPos );
573 if (pArray->IsUpdateRefs())
575 pNew->CopyAllBroadcasters(*rCell.maCell.getFormula());
576 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
578 else
580 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
583 if (!rCellListeners.empty())
585 // Original source cells will be deleted during
586 // sc::CellStoreType::transfer(), SvtListener is a base
587 // class, so we need to replace it.
588 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.getFormula()));
589 if (it != rCellListeners.end())
590 *it = pNew;
593 rCellStore.push_back(pNew);
595 break;
596 default:
597 //assert(!rCell.mpAttr);
598 // This assert doesn't hold, for example
599 // CopyCellsFromClipHandler may omit copying cells during
600 // PasteSpecial for which CopyTextAttrsFromClipHandler
601 // still copies a CellTextAttr. So if that really is not
602 // expected then fix it there.
603 rCellStore.push_back_empty();
606 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
607 if (rCell.mpAttr)
608 rAttrStore.push_back(*rCell.mpAttr);
609 else
610 rAttrStore.push_back_empty();
613 if (pArray->IsUpdateRefs())
615 // At this point each broadcaster instance is managed by 2
616 // containers. We will release those in the original storage
617 // below before transferring them to the document.
618 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
619 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
620 if (pBroadcaster)
621 // A const pointer would be implicitly converted to a bool type.
622 rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
623 else
624 rBCStore.push_back_empty();
627 // The same with cell note instances ...
628 sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
629 if (rCell.mpNote)
630 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
631 else
632 rNoteStore.push_back_empty();
634 // Add cell anchored images
635 aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
637 if (rCell.maPattern)
638 aSortedCols.at(j)->setPattern(nRow, rCell.maPattern);
641 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
643 // Hidden and filtered flags are first converted to segments.
644 aRowFlags.setRowHidden(nRow, rRow.mbHidden);
645 aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
648 if (pProgress)
649 pProgress->SetStateOnPercent(i);
652 rSortedCols.swap(aSortedCols);
653 rRowFlags.swap(aRowFlags);
656 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
658 if (nTop < rRange.aStart.Row())
659 rRange.aStart.SetRow(nTop);
661 if (rRange.aEnd.Row() < nBottom)
662 rRange.aEnd.SetRow(nBottom);
665 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
667 std::vector<ScFormulaCell*>& mrCells;
668 ScColumn* mpCol;
670 public:
671 explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
672 mrCells(rCells), mpCol(nullptr) {}
674 virtual void startColumn( ScColumn* pCol ) override
676 mpCol = pCol;
679 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
681 assert(mpCol);
683 if (!bVal)
684 return;
686 mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
690 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
692 ScColumn* mpCol;
694 std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
695 sc::StartListeningContext maStartCxt;
696 sc::EndListeningContext maEndCxt;
698 public:
699 explicit ListenerStartAction( ScDocument& rDoc ) :
700 mpCol(nullptr),
701 mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
702 maStartCxt(rDoc, mpPosSet),
703 maEndCxt(rDoc, mpPosSet) {}
705 virtual void startColumn( ScColumn* pCol ) override
707 mpCol = pCol;
710 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
712 assert(mpCol);
714 if (!bVal)
715 return;
717 mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
723 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray,
724 SCCOL nDataCol1, SCCOL nDataCol2,
725 const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
727 const SCROW nRow1 = pArray->GetStart();
728 const SCROW nLastRow = pArray->GetLast();
729 const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1));
730 // Before data area.
731 for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols)
733 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1);
734 CreateColumnIfNotExists(nEndCol);
735 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
736 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
737 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
739 // Behind data area.
740 for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols)
742 const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol);
743 CreateColumnIfNotExists(nEndCol);
744 initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
745 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
746 SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
750 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray,
751 SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
753 const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart());
754 const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast());
755 const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1));
756 // Above data area.
757 for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows)
759 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1);
760 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
762 // Below data area.
763 for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows)
765 const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow);
766 SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
770 void ScTable::SortReorderByColumn(
771 const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
773 SCCOLROW nStart = pArray->GetStart();
774 SCCOLROW nLast = pArray->GetLast();
776 std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
777 size_t nCount = aIndices.size();
779 // Cut formula grouping at row and reference boundaries before the reordering.
780 ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
781 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
782 aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
784 // Collect all listeners of cell broadcasters of sorted range.
785 std::vector<SvtListener*> aCellListeners;
787 if (!pArray->IsUpdateRefs())
789 // Collect listeners of cell broadcasters.
790 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
791 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
793 // Remove any duplicate listener entries. We must ensure that we
794 // notify each unique listener only once.
795 std::sort(aCellListeners.begin(), aCellListeners.end());
796 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
798 // Notify the cells' listeners to stop listening.
799 /* TODO: for performance this could be enhanced to stop and later
800 * restart only listening to within the reordered range and keep
801 * listening to everything outside untouched. */
802 sc::RefStopListeningHint aHint;
803 for (auto const & l : aCellListeners)
804 l->Notify(aHint);
807 // table to keep track of column index to position in the index table.
808 std::vector<SCCOLROW> aPosTable(nCount);
809 for (size_t i = 0; i < nCount; ++i)
810 aPosTable[aIndices[i]-nStart] = i;
812 SCCOLROW nDest = nStart;
813 for (size_t i = 0; i < nCount; ++i, ++nDest)
815 SCCOLROW nSrc = aIndices[i];
816 if (nDest != nSrc)
818 aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
820 // Update the position of the index that was originally equal to nDest.
821 size_t nPos = aPosTable[nDest-nStart];
822 aIndices[nPos] = nSrc;
823 aPosTable[nSrc-nStart] = nPos;
826 if (pProgress)
827 pProgress->SetStateOnPercent(i);
830 // Reset formula cell positions which became out-of-sync after column reordering.
831 bool bUpdateRefs = pArray->IsUpdateRefs();
832 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
833 aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
835 if (pArray->IsUpdateRefs())
837 // Set up column reorder map (for later broadcasting of reference updates).
838 sc::ColRowReorderMapType aColMap;
839 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
840 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
842 SCCOL nNew = i + nStart;
843 SCCOL nOld = rOldIndices[i];
844 aColMap.emplace(nOld, nNew);
847 // Collect all listeners within sorted range ahead of time.
848 std::vector<SvtListener*> aListeners;
850 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
851 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
853 // Get all area listeners that listen on one column within the range
854 // and end their listening.
855 ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
856 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
857 aMoveRange, sc::AreaOverlapType::OneColumnInside);
859 for (auto& rAreaListener : aAreaListeners)
861 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
862 aListeners.push_back( rAreaListener.mpListener);
866 // Remove any duplicate listener entries and notify all listeners
867 // afterward. We must ensure that we notify each unique listener only
868 // once.
869 std::sort(aListeners.begin(), aListeners.end());
870 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
872 ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
873 std::for_each(aListeners.begin(), aListeners.end(), std::move(aFunc));
876 // Re-start area listeners on the reordered columns.
878 for (auto& rAreaListener : aAreaListeners)
880 ScRange aNewRange = rAreaListener.maArea;
881 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
882 if (itCol != aColMap.end())
884 aNewRange.aStart.SetCol( itCol->second);
885 aNewRange.aEnd.SetCol( itCol->second);
887 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
891 else // !(pArray->IsUpdateRefs())
893 // Notify the cells' listeners to (re-)start listening.
894 sc::RefStartListeningHint aHint;
895 for (auto const & l : aCellListeners)
896 l->Notify(aHint);
899 // Re-join formulas at row boundaries now that all the references have
900 // been adjusted for column reordering.
901 for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
903 sc::CellStoreType& rCells = aCol[nCol].maCells;
904 sc::CellStoreType::position_type aPos = rCells.position(nRow1);
905 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
906 if (nRow2 < rDocument.MaxRow())
908 aPos = rCells.position(aPos.first, nRow2+1);
909 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
914 static void backupObjectsVisibility(const std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
915 std::vector<std::vector<std::vector<bool>>>& rBackup)
917 size_t nSortedCols = rSortedCols.size();
918 for (size_t iCol = 0; iCol < nSortedCols; ++iCol)
920 std::vector<std::vector<SdrObject*>>& rSingleColCellDrawObjects
921 = rSortedCols[iCol]->maCellDrawObjects;
922 size_t nSingleColCellDrawObjects = rSingleColCellDrawObjects.size();
923 std::vector<std::vector<bool>> aColBackup;
924 for (size_t jRow = 0; jRow < nSingleColCellDrawObjects; ++jRow)
926 std::vector<SdrObject*>& rCellDrawObjects = rSingleColCellDrawObjects[jRow];
927 std::vector<bool> aCellBackup;
928 for (auto& pObject : rCellDrawObjects)
930 aCellBackup.push_back(pObject->IsVisible());
932 aColBackup.push_back(std::move(aCellBackup));
934 rBackup.push_back(std::move(aColBackup));
938 static void restoreObjectsVisibility(std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
939 const std::vector<std::vector<std::vector<bool>>>& rBackup)
941 size_t nSortedCols = rSortedCols.size();
942 for (size_t iCol = 0; iCol < nSortedCols; ++iCol)
944 std::vector<std::vector<SdrObject*>>& rSingleColCellDrawObjects
945 = rSortedCols[iCol]->maCellDrawObjects;
946 size_t nSingleColCellDrawObjects = rSingleColCellDrawObjects.size();
947 for (size_t jRow = 0; jRow < nSingleColCellDrawObjects; jRow++)
949 std::vector<SdrObject*>& rCellDrawObjects = rSingleColCellDrawObjects[jRow];
950 for (size_t kCell = 0; kCell < rCellDrawObjects.size(); ++kCell)
952 rCellDrawObjects[kCell]->SetVisible(rBackup[iCol][jRow][kCell]);
958 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2,
959 ScProgress* pProgress, bool bOnlyDataAreaExtras )
961 assert(!pArray->IsUpdateRefs());
963 if (nCol2 < nCol1)
964 return;
966 // bOnlyDataAreaExtras:
967 // Data area extras by definition do not have any cell content so no
968 // formula cells either, so that handling doesn't need to be executed.
969 // However, there may be listeners of formulas listening to broadcasters of
970 // empty cells.
972 SCROW nRow1 = pArray->GetStart();
973 SCROW nRow2 = pArray->GetLast();
975 // Collect all listeners of cell broadcasters of sorted range.
976 std::vector<SvtListener*> aCellListeners;
978 // When the update ref mode is disabled, we need to detach all formula
979 // cells in the sorted range before reordering, and re-start them
980 // afterward.
981 if (!bOnlyDataAreaExtras)
983 sc::EndListeningContext aCxt(rDocument);
984 DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
987 // Collect listeners of cell broadcasters.
988 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
989 aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
991 // Remove any duplicate listener entries. We must ensure that we notify
992 // each unique listener only once.
993 std::sort(aCellListeners.begin(), aCellListeners.end());
994 aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
996 // Notify the cells' listeners to stop listening.
997 /* TODO: for performance this could be enhanced to stop and later
998 * restart only listening to within the reordered range and keep
999 * listening to everything outside untouched. */
1001 sc::RefStopListeningHint aHint;
1002 for (auto const & l : aCellListeners)
1003 l->Notify(aHint);
1006 // Split formula groups at the sort range boundaries (if applicable).
1007 if (!bOnlyDataAreaExtras)
1009 std::vector<SCROW> aRowBounds
1011 nRow1,
1012 nRow2+1
1014 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1015 SplitFormulaGroups(nCol, aRowBounds);
1018 // Cells in the data rows only reference values in the document. Make
1019 // a copy before updating the document.
1020 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1021 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1022 fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2,
1023 pProgress, this, bOnlyDataAreaExtras);
1025 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1027 SCCOL nThisCol = i + nCol1;
1029 if (!bOnlyDataAreaExtras)
1032 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1033 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1034 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1038 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1039 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1040 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1045 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1046 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1048 // Do the same as broadcaster storage transfer (to prevent double deletion).
1049 rDest.release_range(nRow1, nRow2);
1050 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1051 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1055 // Get all row spans where the pattern is not NULL.
1056 std::vector<PatternSpan> aSpans =
1057 sc::toSpanArrayWithValue<SCROW, CellAttributeHolder, PatternSpan>(
1058 aSortedCols[i]->maPatterns);
1060 for (const auto& rSpan : aSpans)
1062 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, rSpan.maPattern);
1066 aCol[nThisCol].CellStorageModified();
1069 if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
1071 aRowFlags.maRowsHidden.build_tree();
1072 aRowFlags.maRowsFiltered.build_tree();
1074 // Backup visibility state of objects. States will be lost when changing the flags below.
1075 std::vector<std::vector<std::vector<bool>>> aBackup;
1076 backupObjectsVisibility(aSortedCols, aBackup);
1078 // Remove all flags in the range first.
1079 SetRowHidden(nRow1, nRow2, false);
1080 SetRowFiltered(nRow1, nRow2, false);
1082 std::vector<sc::RowSpan> aSpans =
1083 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1085 for (const auto& rSpan : aSpans)
1086 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1088 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1090 for (const auto& rSpan : aSpans)
1091 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1093 //Restore visibility state of objects
1094 restoreObjectsVisibility(aSortedCols, aBackup);
1097 // Update draw object positions
1098 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1100 SCCOL nThisCol = i + nCol1;
1101 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1104 // Notify the cells' listeners to (re-)start listening.
1106 sc::RefStartListeningHint aHint;
1107 for (auto const & l : aCellListeners)
1108 l->Notify(aHint);
1111 if (!bOnlyDataAreaExtras)
1113 // Re-group columns in the sorted range too.
1114 for (SCCOL i = nCol1; i <= nCol2; ++i)
1115 aCol[i].RegroupFormulaCells();
1118 sc::StartListeningContext aCxt(rDocument);
1119 AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1124 void ScTable::SortReorderByRowRefUpdate(
1125 ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1127 assert(pArray->IsUpdateRefs());
1129 if (nCol2 < nCol1)
1130 return;
1132 SCROW nRow1 = pArray->GetStart();
1133 SCROW nRow2 = pArray->GetLast();
1135 ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1136 sc::ColumnSpanSet aGrpListenerRanges;
1139 // Get the range of formula group listeners within sorted range (if any).
1140 sc::QueryRange aQuery;
1142 ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
1143 std::vector<sc::AreaListener> aGrpListeners =
1144 pBASM->GetAllListeners(
1145 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
1148 for (const auto& rGrpListener : aGrpListeners)
1150 assert(rGrpListener.mbGroupListening);
1151 SvtListener* pGrpLis = rGrpListener.mpListener;
1152 pGrpLis->Query(aQuery);
1153 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
1157 ScRangeList aTmp;
1158 aQuery.swapRanges(aTmp);
1160 // If the range is within the sorted range, we need to expand its rows
1161 // to the top and bottom of the sorted range, since the formula cells
1162 // could be anywhere in the sorted range after reordering.
1163 for (size_t i = 0, n = aTmp.size(); i < n; ++i)
1165 ScRange aRange = aTmp[i];
1166 if (!aMoveRange.Intersects(aRange))
1168 // Doesn't overlap with the sorted range at all.
1169 aGrpListenerRanges.set(GetDoc(), aRange, true);
1170 continue;
1173 if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
1175 // Its column range is within the column range of the sorted range.
1176 expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1177 aGrpListenerRanges.set(GetDoc(), aRange, true);
1178 continue;
1181 // It intersects with the sorted range, but its column range is
1182 // not within the column range of the sorted range. Split it into
1183 // 2 ranges.
1184 ScRange aR1 = aRange;
1185 ScRange aR2 = aRange;
1186 if (aRange.aStart.Col() < aMoveRange.aStart.Col())
1188 // Left half is outside the sorted range while the right half is inside.
1189 aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
1190 aR2.aStart.SetCol(aMoveRange.aStart.Col());
1191 expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1193 else
1195 // Left half is inside the sorted range while the right half is outside.
1196 aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
1197 aR2.aStart.SetCol(aMoveRange.aEnd.Col());
1198 expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1201 aGrpListenerRanges.set(GetDoc(), aR1, true);
1202 aGrpListenerRanges.set(GetDoc(), aR2, true);
1206 // Split formula groups at the sort range boundaries (if applicable).
1207 std::vector<SCROW> aRowBounds
1209 nRow1,
1210 nRow2+1
1212 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1213 SplitFormulaGroups(nCol, aRowBounds);
1215 // Cells in the data rows only reference values in the document. Make
1216 // a copy before updating the document.
1217 std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1218 SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1219 std::vector<SvtListener*> aListenersDummy;
1220 fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
1222 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1224 SCCOL nThisCol = i + nCol1;
1227 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1228 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1229 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1233 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1234 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1235 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1239 sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
1240 sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1242 // Release current broadcasters first, to prevent them from getting deleted.
1243 rDest.release_range(nRow1, nRow2);
1245 // Transfer sorted broadcaster segment to the document.
1246 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1250 sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1251 sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1253 // Do the same as broadcaster storage transfer (to prevent double deletion).
1254 rDest.release_range(nRow1, nRow2);
1255 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1256 aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1260 // Get all row spans where the pattern is not NULL.
1261 std::vector<PatternSpan> aSpans =
1262 sc::toSpanArrayWithValue<SCROW, CellAttributeHolder, PatternSpan>(
1263 aSortedCols[i]->maPatterns);
1265 for (const auto& rSpan : aSpans)
1267 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, rSpan.maPattern);
1271 aCol[nThisCol].CellStorageModified();
1274 if (pArray->IsKeepQuery())
1276 aRowFlags.maRowsHidden.build_tree();
1277 aRowFlags.maRowsFiltered.build_tree();
1279 // Backup visibility state of objects. States will be lost when changing the flags below.
1280 std::vector<std::vector<std::vector<bool>>> aBackup;
1281 backupObjectsVisibility(aSortedCols, aBackup);
1283 // Remove all flags in the range first.
1284 SetRowHidden(nRow1, nRow2, false);
1285 SetRowFiltered(nRow1, nRow2, false);
1287 std::vector<sc::RowSpan> aSpans =
1288 sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1290 for (const auto& rSpan : aSpans)
1291 SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1293 aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1295 for (const auto& rSpan : aSpans)
1296 SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1298 //Restore visibility state of objects
1299 restoreObjectsVisibility(aSortedCols, aBackup);
1302 // Update draw object positions
1303 for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1305 SCCOL nThisCol = i + nCol1;
1306 aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1309 // Set up row reorder map (for later broadcasting of reference updates).
1310 sc::ColRowReorderMapType aRowMap;
1311 const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1312 for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1314 SCROW nNew = i + nRow1;
1315 SCROW nOld = rOldIndices[i];
1316 aRowMap.emplace(nOld, nNew);
1319 // Collect all listeners within sorted range ahead of time.
1320 std::vector<SvtListener*> aListeners;
1322 // Collect listeners of cell broadcasters.
1323 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1324 aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1326 // Get all area listeners that listen on one row within the range and end
1327 // their listening.
1328 std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1329 aMoveRange, sc::AreaOverlapType::OneRowInside);
1331 for (auto& rAreaListener : aAreaListeners)
1333 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1334 aListeners.push_back( rAreaListener.mpListener);
1339 // Get all formula cells from the former group area listener ranges.
1341 std::vector<ScFormulaCell*> aFCells;
1342 FormulaCellCollectAction aAction(aFCells);
1343 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1345 aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
1348 // Remove any duplicate listener entries. We must ensure that we notify
1349 // each unique listener only once.
1350 std::sort(aListeners.begin(), aListeners.end());
1351 aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1353 // Collect positions of all shared formula cells outside the sorted range,
1354 // and make them unshared before notifying them.
1355 sc::RefQueryFormulaGroup aFormulaGroupPos;
1356 aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1358 std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1359 const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1360 for (const auto& [rTab, rCols] : rGroupTabs)
1362 for (const auto& [nCol, rCol] : rCols)
1364 std::vector<SCROW> aBounds(rCol);
1365 rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
1369 // Notify the listeners to update their references.
1371 ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
1372 std::for_each(aListeners.begin(), aListeners.end(), std::move(aFunc));
1375 // Re-group formulas in affected columns.
1376 for (const auto& [rTab, rCols] : rGroupTabs)
1378 for (const auto& rEntry : rCols)
1379 rDocument.RegroupFormulaCells(rTab, rEntry.first);
1382 // Re-start area listeners on the reordered rows.
1383 for (const auto& rAreaListener : aAreaListeners)
1385 ScRange aNewRange = rAreaListener.maArea;
1386 sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1387 if (itRow != aRowMap.end())
1389 aNewRange.aStart.SetRow( itRow->second);
1390 aNewRange.aEnd.SetRow( itRow->second);
1392 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1395 // Re-group columns in the sorted range too.
1396 for (SCCOL i = nCol1; i <= nCol2; ++i)
1397 aCol[i].RegroupFormulaCells();
1400 // Re-start area listeners on the old group listener ranges.
1401 ListenerStartAction aAction(rDocument);
1402 aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1406 short ScTable::CompareCell(
1407 sal_uInt16 nSort,
1408 ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1409 ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1411 short nRes = 0;
1413 CellType eType1 = rCell1.getType(), eType2 = rCell2.getType();
1415 // tdf#95520 Sort by color - selected color goes on top, everything else according to compare function
1416 if (aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::TextColor
1417 || aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::BackgroundColor)
1419 ScAddress aPos1(nCell1Col, nCell1Row, GetTab());
1420 ScAddress aPos2(nCell2Col, nCell2Row, GetTab());
1421 Color aTheChosenColor = aSortParam.maKeyState[nSort].aColorSortColor;
1422 Color aColor1;
1423 Color aColor2;
1424 if (aSortParam.maKeyState[nSort].aColorSortMode == ScColorSortMode::TextColor)
1426 aColor1 = GetCellTextColor(aPos1);
1427 aColor2 = GetCellTextColor(aPos2);
1429 else
1431 aColor1 = GetCellBackgroundColor(aPos1);
1432 aColor2 = GetCellBackgroundColor(aPos2);
1434 if (aTheChosenColor == aColor1)
1435 return -1;
1436 if (aTheChosenColor == aColor2)
1437 return 1;
1438 if (aColor1 == aColor2)
1439 return 0;
1440 if (aColor1 > aColor2)
1441 return 1;
1442 if (aColor1 < aColor2)
1443 return -1;
1446 if (!rCell1.isEmpty())
1448 if (!rCell2.isEmpty())
1450 bool bErr1 = false;
1451 bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1452 if (eType1 == CELLTYPE_FORMULA)
1454 if (rCell1.getFormula()->GetErrCode() != FormulaError::NONE)
1456 bErr1 = true;
1457 bStr1 = false;
1459 else if (rCell1.getFormula()->IsValue())
1461 bStr1 = false;
1465 bool bErr2 = false;
1466 bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1467 if (eType2 == CELLTYPE_FORMULA)
1469 if (rCell2.getFormula()->GetErrCode() != FormulaError::NONE)
1471 bErr2 = true;
1472 bStr2 = false;
1474 else if (rCell2.getFormula()->IsValue())
1476 bStr2 = false;
1480 if ( bStr1 && bStr2 ) // only compare strings as strings!
1482 OUString aStr1;
1483 OUString aStr2;
1484 if (eType1 == CELLTYPE_STRING)
1485 aStr1 = rCell1.getSharedString()->getString();
1486 else
1487 aStr1 = GetString(nCell1Col, nCell1Row);
1488 if (eType2 == CELLTYPE_STRING)
1489 aStr2 = rCell2.getSharedString()->getString();
1490 else
1491 aStr2 = GetString(nCell2Col, nCell2Row);
1493 bool bUserDef = aSortParam.bUserDef; // custom sort order
1494 bool bNaturalSort = aSortParam.bNaturalSort; // natural sort
1495 bool bCaseSens = aSortParam.bCaseSens; // case sensitivity
1497 ScUserList& rList = ScGlobal::GetUserList();
1498 if (bUserDef && rList.size() > aSortParam.nUserIndex)
1500 const ScUserListData& rData = rList[aSortParam.nUserIndex];
1502 if ( bNaturalSort )
1503 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
1504 else
1506 if ( bCaseSens )
1507 nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
1508 else
1509 nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
1513 if (!bUserDef)
1515 if ( bNaturalSort )
1516 nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
1517 else
1518 nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1521 else if ( bStr1 ) // String <-> Number or Error
1523 if (bErr2)
1524 nRes = -1; // String in front of Error
1525 else
1526 nRes = 1; // Number in front of String
1528 else if ( bStr2 ) // Number or Error <-> String
1530 if (bErr1)
1531 nRes = 1; // String in front of Error
1532 else
1533 nRes = -1; // Number in front of String
1535 else if (bErr1 && bErr2)
1537 // nothing, two Errors are equal
1539 else if (bErr1) // Error <-> Number
1541 nRes = 1; // Number in front of Error
1543 else if (bErr2) // Number <-> Error
1545 nRes = -1; // Number in front of Error
1547 else // Mixed numbers
1549 double nVal1 = rCell1.getValue();
1550 double nVal2 = rCell2.getValue();
1551 if (nVal1 < nVal2)
1552 nRes = -1;
1553 else if (nVal1 > nVal2)
1554 nRes = 1;
1556 if ( !aSortParam.maKeyState[nSort].bAscending )
1557 nRes = -nRes;
1559 else
1560 nRes = -1;
1562 else
1564 if (!rCell2.isEmpty())
1565 nRes = 1;
1566 else
1567 nRes = 0; // both empty
1569 return nRes;
1572 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1574 short nRes;
1575 sal_uInt16 nSort = 0;
1578 ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
1579 ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
1580 if ( aSortParam.bByRow )
1581 nRes = CompareCell( nSort,
1582 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
1583 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
1584 else
1585 nRes = CompareCell( nSort,
1586 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
1587 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
1588 } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1589 if( nRes == 0 )
1591 ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
1592 ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
1593 if( rInfo1.nOrg < rInfo2.nOrg )
1594 nRes = -1;
1595 else if( rInfo1.nOrg > rInfo2.nOrg )
1596 nRes = 1;
1598 return nRes;
1601 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
1603 if ((nHi - nLo) == 1)
1605 if (Compare(pArray, nLo, nHi) > 0)
1606 pArray->Swap( nLo, nHi );
1608 else
1610 SCCOLROW ni = nLo;
1611 SCCOLROW nj = nHi;
1614 while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1615 ni++;
1616 while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1617 nj--;
1618 if (ni <= nj)
1620 if (ni != nj)
1621 pArray->Swap( ni, nj );
1622 ni++;
1623 nj--;
1625 } while (ni < nj);
1626 if ((nj - nLo) < (nHi - ni))
1628 if (nLo < nj)
1629 QuickSort(pArray, nLo, nj);
1630 if (ni < nHi)
1631 QuickSort(pArray, ni, nHi);
1633 else
1635 if (ni < nHi)
1636 QuickSort(pArray, ni, nHi);
1637 if (nLo < nj)
1638 QuickSort(pArray, nLo, nj);
1643 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1645 short nRes;
1646 sal_uInt16 nSort = 0;
1647 const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1648 if (aSortParam.bByRow)
1652 SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1653 nRes = 0;
1654 if(nCol < GetAllocatedColumnsCount())
1656 ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1657 ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1658 nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1660 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1662 else
1666 SCROW nRow = aSortParam.maKeyState[nSort].nField;
1667 ScRefCellValue aCell1;
1668 ScRefCellValue aCell2;
1669 if(nIndex1 < GetAllocatedColumnsCount())
1670 aCell1 = aCol[nIndex1].GetCellValue(nRow);
1671 if(nIndex2 < GetAllocatedColumnsCount())
1672 aCell2 = aCol[nIndex2].GetCellValue(nRow);
1673 nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1674 nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1675 } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1677 return nRes;
1680 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const // over aSortParam
1682 for (SCCOLROW i=nStart; i<nEnd; i++)
1684 if (Compare( i, i+1 ) > 0)
1685 return false;
1687 return true;
1690 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1692 SCROW nRow;
1693 int nMax = nRow2 - nRow1;
1694 for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1696 nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1697 pArray->Swap(i, nRow1 + nRow);
1701 void ScTable::Sort(
1702 const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1703 ScProgress* pProgress, sc::ReorderParam* pUndo )
1705 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(GetDoc());
1706 InitSortCollator( rSortParam );
1707 bGlobalKeepQuery = bKeepQuery;
1709 if (pUndo)
1711 // Copy over the basic sort parameters.
1712 pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras;
1713 pUndo->mbByRow = rSortParam.bByRow;
1714 pUndo->mbHiddenFiltered = bKeepQuery;
1715 pUndo->mbUpdateRefs = bUpdateRefs;
1716 pUndo->mbHasHeaders = rSortParam.bHasHeader;
1719 // It is assumed that the data area has already been trimmed as necessary.
1721 aSortParam = rSortParam; // must be assigned before calling IsSorted()
1722 if (rSortParam.bByRow)
1724 const SCROW nLastRow = rSortParam.nRow2;
1725 const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1726 if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1728 if(pProgress)
1729 pProgress->SetState( 0, nLastRow-nRow1 );
1731 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1732 aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1734 if ( nLastRow - nRow1 > 255 )
1735 DecoladeRow(pArray.get(), nRow1, nLastRow);
1737 QuickSort(pArray.get(), nRow1, nLastRow);
1738 if (pArray->IsUpdateRefs())
1739 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1740 else
1742 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false);
1743 if (rSortParam.aDataAreaExtras.anyExtrasWanted())
1744 SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2,
1745 rSortParam.aDataAreaExtras, pProgress);
1748 if (pUndo)
1750 // Stored is the first data row without header row.
1751 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1752 pUndo->maDataAreaExtras.mnStartRow = nRow1;
1753 pUndo->maOrderIndices = pArray->GetOrderIndices();
1757 else
1759 const SCCOL nLastCol = rSortParam.nCol2;
1760 const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1761 if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1763 if(pProgress)
1764 pProgress->SetState( 0, nLastCol-nCol1 );
1766 std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1767 aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1769 QuickSort(pArray.get(), nCol1, nLastCol);
1770 SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2,
1771 rSortParam.aDataAreaExtras.mbCellFormats, pProgress);
1772 if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1773 SortReorderAreaExtrasByColumn( pArray.get(),
1774 rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress);
1776 if (pUndo)
1778 // Stored is the first data column without header column.
1779 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1780 pUndo->maDataAreaExtras.mnStartCol = nCol1;
1781 pUndo->maOrderIndices = pArray->GetOrderIndices();
1785 DestroySortCollator();
1788 void ScTable::Reorder( const sc::ReorderParam& rParam )
1790 if (rParam.maOrderIndices.empty())
1791 return;
1793 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1794 if (!pArray)
1795 return;
1797 if (rParam.mbByRow)
1799 // Re-play sorting from the known sort indices.
1800 pArray->ReorderByRow(rParam.maOrderIndices);
1801 if (pArray->IsUpdateRefs())
1802 SortReorderByRowRefUpdate(
1803 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1804 else
1806 SortReorderByRow( pArray.get(),
1807 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false);
1808 if (rParam.maDataAreaExtras.anyExtrasWanted())
1809 SortReorderAreaExtrasByRow( pArray.get(),
1810 rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(),
1811 rParam.maDataAreaExtras, nullptr);
1814 else
1816 // Ordering by column is much simpler. Just set the order indices and we are done.
1817 pArray->SetOrderIndices(std::vector(rParam.maOrderIndices));
1818 SortReorderByColumn(
1819 pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1820 rParam.maDataAreaExtras.mbCellFormats, nullptr);
1821 if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1822 SortReorderAreaExtrasByColumn( pArray.get(),
1823 rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1824 rParam.maDataAreaExtras, nullptr);
1828 namespace {
1830 class SubTotalRowFinder
1832 const ScTable& mrTab;
1833 const ScSubTotalParam& mrParam;
1835 public:
1836 SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1837 mrTab(rTab), mrParam(rParam) {}
1839 bool operator() (size_t nRow, const ScFormulaCell* pCell)
1841 if (!pCell->IsSubTotal())
1842 return false;
1844 SCCOL nStartCol = mrParam.nCol1;
1845 SCCOL nEndCol = mrParam.nCol2;
1847 for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(0, nStartCol - 1))
1849 if (mrTab.HasData(nCol, nRow))
1850 return true;
1852 for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
1854 if (mrTab.HasData(nCol, nRow))
1855 return true;
1857 return false;
1863 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1865 SCCOL nStartCol = rParam.nCol1;
1866 SCROW nStartRow = rParam.nRow1 + 1; // Header
1867 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1868 SCROW nEndRow = rParam.nRow2;
1870 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1872 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1873 SubTotalRowFinder aFunc(*this, rParam);
1874 std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1875 sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1876 if (aPos.first != rCells.end())
1877 return true;
1879 return false;
1882 namespace {
1884 struct RemoveSubTotalsHandler
1886 std::set<SCROW> aRemoved;
1888 void operator() (size_t nRow, const ScFormulaCell* p)
1890 if (p->IsSubTotal())
1891 aRemoved.insert(nRow);
1897 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1899 SCCOL nStartCol = rParam.nCol1;
1900 SCROW nStartRow = rParam.nRow1 + 1; // Header
1901 SCCOL nEndCol = ClampToAllocatedColumns(rParam.nCol2);
1902 SCROW nEndRow = rParam.nRow2; // will change
1904 RemoveSubTotalsHandler aFunc;
1905 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1907 const sc::CellStoreType& rCells = aCol[nCol].maCells;
1908 sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1911 auto& aRows = aFunc.aRemoved;
1913 std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
1914 RemoveRowBreak(nRow+1, false, true);
1915 rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
1918 rParam.nRow2 -= aRows.size();
1921 // Delete hard number formats (for result formulas)
1923 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
1925 const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
1926 if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
1927 == SfxItemState::SET )
1929 ScPatternAttr* pNewPattern(new ScPatternAttr( *pPattern ));
1930 SfxItemSet& rSet = pNewPattern->GetItemSet();
1931 rSet.ClearItem( ATTR_VALUE_FORMAT );
1932 rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
1933 pTab->SetPattern( nCol, nRow, CellAttributeHolder(pNewPattern, true) );
1937 namespace {
1939 struct RowEntry
1941 sal_uInt16 nGroupNo;
1942 SCROW nSubStartRow;
1943 SCROW nDestRow;
1944 SCROW nFuncStart;
1945 SCROW nFuncEnd;
1950 static TranslateId lcl_GetSubTotalStrId(ScSubTotalFunc id)
1952 switch ( id )
1954 case SUBTOTAL_FUNC_AVE: return STR_FUN_TEXT_AVG;
1955 case SUBTOTAL_FUNC_CNT:
1956 case SUBTOTAL_FUNC_CNT2: return STR_FUN_TEXT_COUNT;
1957 case SUBTOTAL_FUNC_MAX: return STR_FUN_TEXT_MAX;
1958 case SUBTOTAL_FUNC_MIN: return STR_FUN_TEXT_MIN;
1959 case SUBTOTAL_FUNC_PROD: return STR_FUN_TEXT_PRODUCT;
1960 case SUBTOTAL_FUNC_STD:
1961 case SUBTOTAL_FUNC_STDP: return STR_FUN_TEXT_STDDEV;
1962 case SUBTOTAL_FUNC_SUM: return STR_FUN_TEXT_SUM;
1963 case SUBTOTAL_FUNC_VAR:
1964 case SUBTOTAL_FUNC_VARP: return STR_FUN_TEXT_VAR;
1965 default:
1967 return STR_EMPTYDATA;
1968 // added to avoid warnings
1973 // Gets the string used for "Grand" results
1974 static TranslateId lcl_GetGrandSubTotalStrId(ScSubTotalFunc id)
1976 switch ( id )
1978 case SUBTOTAL_FUNC_AVE: return STR_TABLE_GRAND_AVG;
1979 case SUBTOTAL_FUNC_CNT:
1980 case SUBTOTAL_FUNC_CNT2: return STR_TABLE_GRAND_COUNT;
1981 case SUBTOTAL_FUNC_MAX: return STR_TABLE_GRAND_MAX;
1982 case SUBTOTAL_FUNC_MIN: return STR_TABLE_GRAND_MIN;
1983 case SUBTOTAL_FUNC_PROD: return STR_TABLE_GRAND_PRODUCT;
1984 case SUBTOTAL_FUNC_STD:
1985 case SUBTOTAL_FUNC_STDP: return STR_TABLE_GRAND_STDDEV;
1986 case SUBTOTAL_FUNC_SUM: return STR_TABLE_GRAND_SUM;
1987 case SUBTOTAL_FUNC_VAR:
1988 case SUBTOTAL_FUNC_VARP: return STR_TABLE_GRAND_VAR;
1989 default:
1991 return STR_EMPTYDATA;
1992 // added to avoid warnings
1997 // new intermediate results
1998 // rParam.nRow2 is changed!
2000 bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
2002 SCCOL nStartCol = rParam.nCol1;
2003 SCROW nStartRow = rParam.nRow1 + 1; // Header
2004 SCCOL nEndCol = rParam.nCol2;
2005 SCROW nEndRow = rParam.nRow2; // will change
2007 // Remove empty rows at the end
2008 // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
2009 // If sorted, all empty rows are at the end.
2010 SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
2011 nEndRow -= nEmpty;
2013 sal_uInt16 nLevelCount = 0; // Number of levels
2014 for (const auto& group : rParam.aGroups)
2016 if (!group.bActive)
2017 break;
2018 ++nLevelCount;
2021 if (nLevelCount==0) // do nothing
2022 return true;
2024 // With (blank) as a separate category, subtotal rows from
2025 // the other columns must always be tested
2026 // (previously only when a column occurred more than once)
2027 bool bTestPrevSub = ( nLevelCount > 1 );
2029 OUString aSubString;
2031 bool bIgnoreCase = !rParam.bCaseSens;
2033 OUString aCompString[MAXSUBTOTAL];
2035 //TODO: sort?
2037 ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
2038 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
2040 bool bSpaceLeft = true; // Success when inserting?
2042 // For performance reasons collect formula entries so their
2043 // references don't have to be tested for updates each time a new row is
2044 // inserted
2045 RowEntry aRowEntry;
2046 ::std::vector< RowEntry > aRowVector;
2048 for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
2050 aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
2052 const auto& group = rParam.aGroups[aRowEntry.nGroupNo];
2053 // how many results per level
2054 SCCOL nResCount = group.nSubTotals;
2056 if (nResCount > 0) // otherwise only sort
2058 SCROW nAboveRows = rParam.bSummaryBelow ? nStartRow : nStartRow + nLevel;
2059 for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo; ++i)
2061 aSubString = GetString(rParam.aGroups[i].nField, nAboveRows);
2062 if ( bIgnoreCase )
2063 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2064 else
2065 aCompString[i] = aSubString;
2066 } // aSubString stays on the last
2068 bool bBlockVis = false; // group visible?
2069 aRowEntry.nSubStartRow = nAboveRows;
2070 for (SCROW nRow=nAboveRows; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
2072 bool bChanged;
2073 if (nRow>nEndRow)
2074 bChanged = true;
2075 else
2077 bChanged = false;
2078 OUString aString;
2079 for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo && !bChanged; ++i)
2081 aString = GetString(rParam.aGroups[i].nField, nRow);
2082 if (bIgnoreCase)
2083 aString = ScGlobal::getCharClass().uppercase(aString);
2084 // when sorting, blanks are separate group
2085 // otherwise blank cells are allowed below
2086 bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2087 aString != aCompString[i] );
2089 if ( bChanged && bTestPrevSub )
2091 // No group change on rows that will contain subtotal formulas
2092 bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
2093 [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
2096 if ( bChanged )
2098 if (rParam.bSummaryBelow)
2100 aRowEntry.nDestRow = nRow;
2101 aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2102 aRowEntry.nFuncEnd = nRow - 1;
2104 else
2106 aRowEntry.nDestRow = aRowEntry.nSubStartRow;
2107 aRowEntry.nFuncStart = aRowEntry.nSubStartRow + 1;
2108 if (nRow != nEndRow + 1)
2109 aRowEntry.nFuncEnd = nRow - nLevel;
2110 else
2111 aRowEntry.nFuncEnd = nRow;
2114 bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
2115 aRowEntry.nDestRow, 1 );
2116 DBShowRow( aRowEntry.nDestRow, bBlockVis );
2117 if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
2118 aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2119 SetRowBreak(aRowEntry.nSubStartRow, false, true);
2121 if (bSpaceLeft)
2123 for ( auto& rRowEntry : aRowVector)
2125 if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
2126 ++rRowEntry.nSubStartRow;
2127 if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
2128 ++rRowEntry.nDestRow;
2129 if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
2130 ++rRowEntry.nFuncStart;
2131 if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
2132 ++rRowEntry.nFuncEnd;
2134 // collect formula positions
2135 aRowVector.push_back( aRowEntry );
2137 OUString aOutString = aSubString;
2138 if (aOutString.isEmpty())
2139 aOutString = ScResId( STR_EMPTYDATA );
2140 aOutString += " ";
2141 TranslateId pStrId = STR_TABLE_ERGEBNIS;
2142 if ( nResCount == 1 )
2143 pStrId = lcl_GetSubTotalStrId(group.func(0));
2144 aOutString += ScResId(pStrId);
2145 SetString(group.nField, aRowEntry.nDestRow, nTab, aOutString);
2146 ApplyStyle(group.nField, aRowEntry.nDestRow, pStyle);
2148 ++nRow;
2149 ++nEndRow;
2150 aRowEntry.nSubStartRow = nRow;
2151 for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo; ++i)
2153 aSubString = GetString(rParam.aGroups[i].nField, nRow);
2154 if ( bIgnoreCase )
2155 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2156 else
2157 aCompString[i] = aSubString;
2161 bBlockVis = !RowFiltered(nRow);
2166 if (!aRowVector.empty())
2168 SCROW nGlobalEndRow = 0;
2169 SCROW nGlobalEndFunc = 0;
2170 for (auto& rRowEntry : aRowVector)
2172 if (!rParam.bSummaryBelow)
2174 // if we have Global summary above, we need to shift summary rows down
2175 rRowEntry.nDestRow = rRowEntry.nDestRow + nLevelCount;
2176 rRowEntry.nFuncEnd = rRowEntry.nFuncEnd + nLevelCount;
2177 rRowEntry.nFuncStart = rRowEntry.nFuncStart + nLevelCount - rRowEntry.nGroupNo;
2178 rRowEntry.nSubStartRow = rRowEntry.nSubStartRow + nLevelCount;
2181 nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
2182 nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
2185 // generate global total
2186 SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
2187 SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
2189 for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
2191 const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
2192 const auto& group = rParam.aGroups[nGroupNo];
2193 if (!group.nSubTotals)
2195 // No subtotal function given for this group => no formula or
2196 // label and do not insert a row.
2197 continue;
2200 aRowEntry.nGroupNo = nGroupNo;
2201 if (rParam.bSummaryBelow)
2203 // increment end row
2204 nGlobalEndRow++;
2206 // add row entry for formula
2207 aRowEntry.nSubStartRow = nGlobalStartRow;
2208 aRowEntry.nFuncStart = nGlobalStartFunc;
2209 aRowEntry.nDestRow = nGlobalEndRow;
2210 aRowEntry.nFuncEnd = nGlobalEndFunc;
2212 // increment row
2213 nGlobalEndFunc++;
2215 else
2217 // if we have Global summary we need to shift summary rows down
2218 aRowEntry.nSubStartRow = nGlobalStartRow - nGroupNo - 1;
2219 aRowEntry.nFuncStart = nGlobalStartFunc - nGroupNo - 1;
2220 aRowEntry.nDestRow = nGlobalStartRow - nGroupNo - 1;
2221 aRowEntry.nFuncEnd = nGlobalEndFunc;
2224 bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
2226 if (bSpaceLeft)
2228 aRowVector.push_back(aRowEntry);
2229 nEndRow++;
2230 DBShowRow(aRowEntry.nDestRow, true);
2232 // insert label
2233 OUString label = ScResId(lcl_GetGrandSubTotalStrId(group.func(0)));
2234 SetString(group.nField, aRowEntry.nDestRow, nTab, label);
2235 ApplyStyle(group.nField, aRowEntry.nDestRow, pStyle);
2240 // now insert the formulas
2241 ScComplexRefData aRef;
2242 aRef.InitFlags();
2243 aRef.Ref1.SetAbsTab(nTab);
2244 aRef.Ref2.SetAbsTab(nTab);
2245 for (const auto& rRowEntry : aRowVector)
2247 const auto& group = rParam.aGroups[rRowEntry.nGroupNo];
2248 SCCOL nResCount = group.nSubTotals;
2249 for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2251 aRef.Ref1.SetAbsCol(group.col(nResult));
2252 aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
2253 aRef.Ref2.SetAbsCol(group.col(nResult));
2254 aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
2256 ScTokenArray aArr(rDocument);
2257 aArr.AddOpCode( ocSubTotal );
2258 aArr.AddOpCode( ocOpen );
2259 aArr.AddDouble( static_cast<double>(group.func(nResult)) );
2260 aArr.AddOpCode( ocSep );
2261 aArr.AddDoubleReference( aRef );
2262 aArr.AddOpCode( ocClose );
2263 aArr.AddOpCode( ocStop );
2264 ScFormulaCell* pCell = new ScFormulaCell(
2265 rDocument, ScAddress(group.col(nResult), rRowEntry.nDestRow, nTab), aArr);
2266 if ( rParam.bIncludePattern )
2267 pCell->SetNeedNumberFormat(true);
2269 SetFormulaCell(group.col(nResult), rRowEntry.nDestRow, pCell);
2270 if (group.col(nResult) != group.nField)
2272 ApplyStyle(group.col(nResult), rRowEntry.nDestRow, pStyle);
2274 lcl_RemoveNumberFormat(this, group.col(nResult), rRowEntry.nDestRow);
2280 //TODO: according to setting, shift intermediate-sum rows up?
2282 //TODO: create Outlines directly?
2284 if (bSpaceLeft)
2285 DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2287 rParam.nRow2 = nEndRow; // new end
2288 return bSpaceLeft;
2291 void ScTable::TopTenQuery( ScQueryParam& rParam )
2293 bool bSortCollatorInitialized = false;
2294 SCSIZE nEntryCount = rParam.GetEntryCount();
2295 SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
2296 SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
2297 for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
2299 ScQueryEntry& rEntry = rParam.GetEntry(i);
2300 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2302 for (ScQueryEntry::Item& rItem : rItems)
2304 switch (rEntry.eOp)
2306 case SC_TOPVAL:
2307 case SC_BOTVAL:
2308 case SC_TOPPERC:
2309 case SC_BOTPERC:
2311 ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField));
2312 aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare
2313 if (!bSortCollatorInitialized)
2315 bSortCollatorInitialized = true;
2316 InitSortCollator(aLocalSortParam);
2318 std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
2319 DecoladeRow(pArray.get(), nRow1, rParam.nRow2);
2320 QuickSort(pArray.get(), nRow1, rParam.nRow2);
2321 std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray();
2322 SCSIZE nValidCount = nCount;
2323 // Don't count note or blank cells, they are sorted to the end
2324 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty())
2325 nValidCount--;
2326 // Don't count Strings, they are between Value and blank
2327 while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString())
2328 nValidCount--;
2329 if (nValidCount > 0)
2331 if (rItem.meType == ScQueryEntry::ByString)
2332 { // by string ain't going to work
2333 rItem.meType = ScQueryEntry::ByValue;
2334 rItem.mfVal = 10; // 10 and 10% respectively
2336 SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
2337 SCSIZE nOffset = 0;
2338 switch (rEntry.eOp)
2340 case SC_TOPVAL:
2342 rEntry.eOp = SC_GREATER_EQUAL;
2343 if (nVal > nValidCount)
2344 nVal = nValidCount;
2345 nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount
2347 break;
2348 case SC_BOTVAL:
2350 rEntry.eOp = SC_LESS_EQUAL;
2351 if (nVal > nValidCount)
2352 nVal = nValidCount;
2353 nOffset = nVal - 1; // 1 <= nVal <= nValidCount
2355 break;
2356 case SC_TOPPERC:
2358 rEntry.eOp = SC_GREATER_EQUAL;
2359 if (nVal > 100)
2360 nVal = 100;
2361 nOffset = nValidCount - (nValidCount * nVal / 100);
2362 if (nOffset >= nValidCount)
2363 nOffset = nValidCount - 1;
2365 break;
2366 case SC_BOTPERC:
2368 rEntry.eOp = SC_LESS_EQUAL;
2369 if (nVal > 100)
2370 nVal = 100;
2371 nOffset = (nValidCount * nVal / 100);
2372 if (nOffset >= nValidCount)
2373 nOffset = nValidCount - 1;
2375 break;
2376 default:
2378 // added to avoid warnings
2381 ScRefCellValue aCell = ppInfo[nOffset].maCell;
2382 if (aCell.hasNumeric())
2383 rItem.mfVal = aCell.getValue();
2384 else
2386 OSL_FAIL("TopTenQuery: pCell no ValueData");
2387 rEntry.eOp = SC_GREATER_EQUAL;
2388 rItem.mfVal = 0;
2391 else
2393 rEntry.eOp = SC_GREATER_EQUAL;
2394 rItem.meType = ScQueryEntry::ByValue;
2395 rItem.mfVal = 0;
2398 break;
2399 default:
2401 // added to avoid warnings
2406 if ( bSortCollatorInitialized )
2407 DestroySortCollator();
2410 namespace {
2412 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
2414 // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
2415 // The problem with this optimization is that the autofilter dialog apparently converts
2416 // the value to text and then converts that back to a number for filtering.
2417 // If that leads to any change of value (such as when time is rounded to seconds),
2418 // even matching values will be filtered out. Therefore query by value only for formats
2419 // where no such change should occur.
2420 if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
2422 switch(pEntry->GetType())
2424 case SvNumFormatType::NUMBER:
2425 case SvNumFormatType::FRACTION:
2426 case SvNumFormatType::SCIENTIFIC:
2427 return true;
2428 case SvNumFormatType::DATE:
2429 case SvNumFormatType::DATETIME:
2430 bDateFormat = true;
2431 break;
2432 default:
2433 break;
2436 return false;
2439 class PrepareQueryItem
2441 const ScDocument& mrDoc;
2442 const bool mbRoundForFilter;
2443 public:
2444 explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) :
2445 mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {}
2447 void operator() (ScQueryEntry::Item& rItem)
2449 rItem.mbRoundForFilter = mbRoundForFilter;
2451 if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
2452 return;
2454 sal_uInt32 nIndex = 0;
2455 bool bNumber = mrDoc.GetFormatTable()->
2456 IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
2458 // Advanced Filter creates only ByString queries that need to be
2459 // converted to ByValue if appropriate. rItem.mfVal now holds the value
2460 // if bNumber==true.
2462 if (rItem.meType == ScQueryEntry::ByString)
2464 bool bDateFormat = false;
2465 if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
2466 rItem.meType = ScQueryEntry::ByValue;
2467 if (!bDateFormat)
2468 return;
2471 // Double-check if the query by date is really appropriate.
2473 if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
2475 const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
2476 if (pEntry)
2478 SvNumFormatType nNumFmtType = pEntry->GetType();
2479 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
2480 rItem.meType = ScQueryEntry::ByValue; // not a date only
2481 else
2482 rItem.meType = ScQueryEntry::ByDate; // date only
2484 else
2485 rItem.meType = ScQueryEntry::ByValue; // what the ... not a date
2487 else
2488 rItem.meType = ScQueryEntry::ByValue; // not a date
2492 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter )
2494 bool bTopTen = false;
2495 SCSIZE nEntryCount = rParam.GetEntryCount();
2497 for ( SCSIZE i = 0; i < nEntryCount; ++i )
2499 ScQueryEntry& rEntry = rParam.GetEntry(i);
2500 if (!rEntry.bDoQuery)
2501 continue;
2503 ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2504 std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter));
2506 if ( !bTopTen )
2508 switch ( rEntry.eOp )
2510 case SC_TOPVAL:
2511 case SC_BOTVAL:
2512 case SC_TOPPERC:
2513 case SC_BOTPERC:
2515 bTopTen = true;
2517 break;
2518 default:
2525 if ( bTopTen )
2527 pTab->TopTenQuery( rParam );
2533 void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
2535 lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
2538 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
2540 ScQueryParam aParam( rParamOrg );
2541 typedef std::unordered_set<OUString> StrSetType;
2542 StrSetType aStrSet;
2544 bool bStarted = false;
2545 bool bOldResult = true;
2546 SCROW nOldStart = 0;
2547 SCROW nOldEnd = 0;
2549 SCSIZE nCount = 0;
2550 SCROW nOutRow = 0;
2551 SCROW nHeader = aParam.bHasHeader ? 1 : 0;
2553 lcl_PrepareQuery(&rDocument, this, aParam, true);
2555 if (!aParam.bInplace)
2557 nOutRow = aParam.nDestRow + nHeader;
2558 if (nHeader > 0)
2559 CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
2560 aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
2563 sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
2564 ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
2566 SCROW nRealRow2 = aParam.nRow2;
2567 for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
2569 bool bResult; // Filter result
2570 bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos);
2571 if (!bValid && bKeepSub) // Keep subtotals
2573 for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
2575 ScRefCellValue aCell = GetCellValue(nCol, j);
2576 if (aCell.getType() != CELLTYPE_FORMULA)
2577 continue;
2579 if (!aCell.getFormula()->IsSubTotal())
2580 continue;
2582 if (RefVisible(aCell.getFormula()))
2583 bValid = true;
2586 if (bValid)
2588 if (aParam.bDuplicate)
2589 bResult = true;
2590 else
2592 OUStringBuffer aStr;
2593 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
2595 OUString aCellStr = GetString(k, j);
2596 aStr.append(aCellStr + u"\x0001");
2599 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
2602 else
2603 bResult = false;
2605 if (aParam.bInplace)
2607 if (bResult == bOldResult && bStarted)
2608 nOldEnd = j;
2609 else
2611 if (bStarted)
2612 DBShowRows(nOldStart,nOldEnd, bOldResult);
2613 nOldStart = nOldEnd = j;
2614 bOldResult = bResult;
2616 bStarted = true;
2618 else
2620 if (bResult)
2622 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
2623 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
2624 blockPos.invalidate();
2625 ++nOutRow;
2628 if (bResult)
2629 ++nCount;
2632 if (aParam.bInplace && bStarted)
2633 DBShowRows(nOldStart,nOldEnd, bOldResult);
2635 if (aParam.bInplace)
2636 SetDrawPageSize();
2638 return nCount;
2641 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2643 bool bValid = true;
2644 std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
2645 OUString aCellStr;
2646 SCCOL nCol = nCol1;
2647 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
2648 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
2649 SCROW nDBRow1 = rQueryParam.nRow1;
2650 SCCOL nDBCol2 = rQueryParam.nCol2;
2651 // First row must be column headers
2652 while (bValid && (nCol <= nCol2))
2654 OUString aQueryStr = GetUpperCellString(nCol, nRow1);
2655 bool bFound = false;
2656 SCCOL i = rQueryParam.nCol1;
2657 while (!bFound && (i <= nDBCol2))
2659 if ( nTab == nDBTab )
2660 aCellStr = GetUpperCellString(i, nDBRow1);
2661 else
2662 aCellStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
2663 bFound = (aCellStr == aQueryStr);
2664 if (!bFound) i++;
2666 if (bFound)
2667 pFields[nCol - nCol1] = i;
2668 else
2669 bValid = false;
2670 nCol++;
2672 if (bValid)
2674 SCSIZE nVisible = 0;
2675 for ( nCol=nCol1; nCol<=ClampToAllocatedColumns(nCol2); nCol++ )
2676 nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
2678 if ( nVisible > SCSIZE_MAX / sizeof(void*) )
2680 OSL_FAIL("too many filter criteria");
2681 nVisible = 0;
2684 SCSIZE nNewEntries = nVisible;
2685 rQueryParam.Resize( nNewEntries );
2687 SCSIZE nIndex = 0;
2688 SCROW nRow = nRow1 + 1;
2689 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2690 while (nRow <= nRow2)
2692 nCol = nCol1;
2693 while (nCol <= nCol2)
2695 aCellStr = GetInputString( nCol, nRow );
2696 if (!aCellStr.isEmpty())
2698 if (nIndex < nNewEntries)
2700 rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
2701 rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
2702 nIndex++;
2703 if (nIndex < nNewEntries)
2704 rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
2706 else
2707 bValid = false;
2709 nCol++;
2711 nRow++;
2712 if (nIndex < nNewEntries)
2713 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
2716 return bValid;
2719 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2721 // A valid StarQuery must be at least 4 columns wide. To be precise it
2722 // should be exactly 4 columns ...
2723 // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
2724 // column Excel style query range immediately left to itself would result
2725 // in a circular reference when the field name or operator or value (first
2726 // to third query range column) is obtained (#i58354#). Furthermore, if the
2727 // range wasn't sufficiently specified data changes wouldn't flag formula
2728 // cells for recalculation.
2729 if (nCol2 - nCol1 < 3)
2730 return false;
2732 bool bValid;
2733 OUString aCellStr;
2734 SCSIZE nIndex = 0;
2735 SCROW nRow = nRow1;
2736 OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
2737 SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
2738 SCROW nDBRow1 = rQueryParam.nRow1;
2739 SCCOL nDBCol2 = rQueryParam.nCol2;
2741 SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
2742 rQueryParam.Resize( nNewEntries );
2743 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2747 ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
2749 bValid = false;
2750 // First column AND/OR
2751 if (nIndex > 0)
2753 aCellStr = GetUpperCellString(nCol1, nRow);
2754 if ( aCellStr == ScResId(STR_TABLE_AND) )
2756 rEntry.eConnect = SC_AND;
2757 bValid = true;
2759 else if ( aCellStr == ScResId(STR_TABLE_OR) )
2761 rEntry.eConnect = SC_OR;
2762 bValid = true;
2765 // Second column field name
2766 if ((nIndex < 1) || bValid)
2768 bool bFound = false;
2769 aCellStr = GetUpperCellString(nCol1 + 1, nRow);
2770 for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
2772 OUString aFieldStr;
2773 if ( nTab == nDBTab )
2774 aFieldStr = GetUpperCellString(i, nDBRow1);
2775 else
2776 aFieldStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
2777 bFound = (aCellStr == aFieldStr);
2778 if (bFound)
2780 rEntry.nField = i;
2781 bValid = true;
2783 else
2784 bValid = false;
2787 // Third column operator =<>...
2788 if (bValid)
2790 aCellStr = GetUpperCellString(nCol1 + 2, nRow);
2791 if (aCellStr.startsWith("<"))
2793 if (aCellStr[1] == '>')
2794 rEntry.eOp = SC_NOT_EQUAL;
2795 else if (aCellStr[1] == '=')
2796 rEntry.eOp = SC_LESS_EQUAL;
2797 else
2798 rEntry.eOp = SC_LESS;
2800 else if (aCellStr.startsWith(">"))
2802 if (aCellStr[1] == '=')
2803 rEntry.eOp = SC_GREATER_EQUAL;
2804 else
2805 rEntry.eOp = SC_GREATER;
2807 else if (aCellStr.startsWith("="))
2808 rEntry.eOp = SC_EQUAL;
2811 // Fourth column values
2812 if (bValid)
2814 OUString aStr = GetString(nCol1 + 3, nRow);
2815 rEntry.GetQueryItem().maString = rPool.intern(aStr);
2816 rEntry.bDoQuery = true;
2818 nIndex++;
2819 nRow++;
2821 while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
2822 return bValid;
2825 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2827 SCSIZE i, nCount;
2828 PutInOrder(nCol1, nCol2);
2829 PutInOrder(nRow1, nRow2);
2831 nCount = rQueryParam.GetEntryCount();
2832 for (i=0; i < nCount; i++)
2833 rQueryParam.GetEntry(i).Clear();
2835 // Standard query table
2836 bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2837 // Excel Query table
2838 if (!bValid)
2839 bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2841 SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
2842 nCount = rQueryParam.GetEntryCount();
2843 if (bValid)
2845 // query type must be set
2846 for (i=0; i < nCount; i++)
2848 ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
2849 sal_uInt32 nIndex = 0;
2850 bool bNumber = pFormatter->IsNumberFormat(
2851 rItem.maString.getString(), nIndex, rItem.mfVal);
2852 bool bDateFormat = false;
2853 rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
2854 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
2857 else
2859 for (i=0; i < nCount; i++)
2860 rQueryParam.GetEntry(i).Clear();
2862 return bValid;
2865 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
2867 if (nStartRow == nEndRow)
2868 // Assume only data.
2869 /* XXX NOTE: previous behavior still checked this one row and could
2870 * evaluate it has header row, but that doesn't make much sense. */
2871 return false;
2873 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2875 CellType eType = GetCellType( nCol, nStartRow );
2876 // Any non-text cell in first row => not headers.
2877 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2878 return false;
2881 // First row all text cells, any non-text cell in second row => headers.
2882 SCROW nTestRow = nStartRow + 1;
2883 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2885 CellType eType = GetCellType( nCol, nTestRow );
2886 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2887 return true;
2890 // Also second row all text cells => first row not headers.
2891 return false;
2894 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
2896 if (nStartCol == nEndCol)
2897 // Assume only data.
2898 /* XXX NOTE: previous behavior still checked this one column and could
2899 * evaluate it has header column, but that doesn't make much sense. */
2900 return false;
2902 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
2904 CellType eType = GetCellType( nStartCol, nRow );
2905 // Any non-text cell in first column => not headers.
2906 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2907 return false;
2910 // First column all text cells, any non-text cell in second column => headers.
2911 SCCOL nTestCol = nStartCol + 1;
2912 for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
2914 CellType eType = GetCellType( nRow, nTestCol );
2915 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2916 return true;
2919 // Also second column all text cells => first column not headers.
2920 return false;
2923 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering )
2925 if (nCol >= aCol.size())
2926 return;
2928 sc::ColumnBlockConstPosition aBlockPos;
2929 aCol[nCol].InitBlockPosition(aBlockPos);
2930 aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering, false /*bFilteredRow*/);
2932 SCROW nLastRow = aBlockPos.miCellPos->position;
2933 if (nLastRow < nRow2)
2935 aCol[nCol].GetBackColorFilterEntries(nLastRow, nRow2, rFilterEntries);
2939 void ScTable::GetFilteredFilterEntries(
2940 SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
2942 if (nCol >= aCol.size())
2943 return;
2945 sc::ColumnBlockConstPosition aBlockPos;
2946 aCol[nCol].InitBlockPosition(aBlockPos);
2948 // remove the entry for this column from the query parameter
2949 ScQueryParam aParam( rParam );
2950 aParam.RemoveEntryByField(nCol);
2952 lcl_PrepareQuery(&rDocument, this, aParam, true);
2953 ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
2954 for ( SCROW j = nRow1; j <= nRow2; ++j )
2956 if (queryEvaluator.ValidQuery(j))
2958 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, false/*bFilteredRow*/);
2960 else
2962 aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, true/*bFilteredRow*/);
2967 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings)
2969 if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
2970 return false;
2971 return aCol[nCol].GetDataEntries( nRow, rStrings);
2974 sal_uInt64 ScTable::GetCellCount() const
2976 sal_uInt64 nCellCount = 0;
2978 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
2979 nCellCount += aCol[nCol].GetCellCount();
2981 return nCellCount;
2984 sal_uInt64 ScTable::GetWeightedCount() const
2986 sal_uInt64 nCellCount = 0;
2988 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
2989 nCellCount += aCol[nCol].GetWeightedCount();
2991 return nCellCount;
2994 sal_uInt64 ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
2996 sal_uInt64 nCellCount = 0;
2998 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
2999 nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
3001 return nCellCount;
3004 sal_uInt64 ScTable::GetCodeCount() const
3006 sal_uInt64 nCodeCount = 0;
3008 for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3009 if ( aCol[nCol].GetCellCount() )
3010 nCodeCount += aCol[nCol].GetCodeCount();
3012 return nCodeCount;
3015 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3016 SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3018 if ( IsColValid( nCol ) )
3019 return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3020 else
3021 return 0;
3024 sal_Int32 ScTable::GetMaxNumberStringLen(
3025 sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3027 if ( IsColValid( nCol ) )
3028 return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3029 else
3030 return 0;
3033 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3035 ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
3036 ScRange aMarkArea( ScAddress::UNINITIALIZED );
3037 if (rMark.IsMultiMarked())
3038 aMarkArea = rMark.GetMultiMarkArea();
3039 else if (rMark.IsMarked())
3040 aMarkArea = rMark.GetMarkArea();
3041 else
3043 assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3044 aMarkArea.aStart.SetCol(0);
3045 aMarkArea.aEnd.SetCol(rDocument.MaxCol());
3047 const SCCOL nStartCol = aMarkArea.aStart.Col();
3048 const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
3049 for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
3051 if (mpColFlags && ColHidden(nCol))
3052 continue;
3054 aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3058 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */