xref: /core/sc/source/core/data/table3.cxx (revision a6ec933d)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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>
31 
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 <docpool.hxx>
51 #include <cellvalue.hxx>
52 #include <tokenarray.hxx>
53 #include <mtvcellfunc.hxx>
54 #include <columnspanset.hxx>
55 #include <fstalgorithm.hxx>
56 #include <listenercontext.hxx>
57 #include <sharedformula.hxx>
58 #include <stlsheet.hxx>
59 #include <refhint.hxx>
60 #include <listenerquery.hxx>
61 #include <bcaslot.hxx>
62 #include <reordermap.hxx>
63 #include <drwlayer.hxx>
64 #include <queryevaluator.hxx>
65 #include <scopetools.hxx>
66 
67 #include <svl/sharedstringpool.hxx>
68 
69 #include <memory>
70 #include <set>
71 #include <unordered_set>
72 #include <vector>
73 #include <mdds/flat_segment_tree.hpp>
74 
75 using namespace ::com::sun::star;
76 
77 namespace naturalsort {
78 
79 using namespace ::com::sun::star::i18n;
80 
81 /** Splits a given string into three parts: the prefix, number string, and
82     the suffix.
83 
84     @param sWhole
85     Original string to be split into pieces
86 
87     @param sPrefix
88     Prefix string that consists of the part before the first number token.
89     If no number was found, sPrefix is unchanged.
90 
91     @param sSuffix
92     String after the last number token.  This may still contain number strings.
93     If no number was found, sSuffix is unchanged.
94 
95     @param fNum
96     Number converted from the middle number string
97     If no number was found, fNum is unchanged.
98 
99     @return Returns TRUE if a numeral element is found in a given string, or
100     FALSE if no numeral element is found.
101 */
102 static bool SplitString( const OUString &sWhole,
103     OUString &sPrefix, OUString &sSuffix, double &fNum )
104 {
105     // Get prefix element, search for any digit and stop.
106     sal_Int32 nPos = 0;
107     while (nPos < sWhole.getLength())
108     {
109         const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos);
110         if (nType & KCharacterType::DIGIT)
111             break;
112         sWhole.iterateCodePoints( &nPos );
113     }
114 
115     // Return FALSE if no numeral element is found
116     if ( nPos == sWhole.getLength() )
117         return false;
118 
119     // Get numeral element
120     const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep();
121     ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken(
122         KParseType::ANY_NUMBER, sWhole, nPos,
123         KParseTokens::ANY_NUMBER, "", KParseTokens::ANY_NUMBER, sUser );
124 
125     if ( aPRNum.EndPos == nPos )
126     {
127         SAL_WARN("sc.core","naturalsort::SplitString - digit found but no number parsed, pos " <<
128                 nPos << " : " << sWhole);
129         return false;
130     }
131 
132     sPrefix = sWhole.copy( 0, nPos );
133     fNum = aPRNum.Value;
134     sSuffix = sWhole.copy( aPRNum.EndPos );
135 
136     return true;
137 }
138 
139 /** Naturally compares two given strings.
140 
141     This is the main function that should be called externally.  It returns
142     either 1, 0, or -1 depending on the comparison result of given two strings.
143 
144     @param sInput1
145     Input string 1
146 
147     @param sInput2
148     Input string 2
149 
150     @param bCaseSens
151     Boolean value for case sensitivity
152 
153     @param pData
154     Pointer to user defined sort list
155 
156     @param pCW
157     Pointer to collator wrapper for normal string comparison
158 
159     @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if
160     sInput2 is greater.
161 */
162 static short Compare( const OUString &sInput1, const OUString &sInput2,
163                const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW )
164 {
165     OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2;
166 
167     do
168     {
169         double nNum1, nNum2;
170         bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 );
171         bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 );
172 
173         short nPreRes; // Prefix comparison result
174         if ( pData )
175         {
176             if ( bCaseSens )
177             {
178                 if ( !bNumFound1 || !bNumFound2 )
179                     return static_cast<short>(pData->Compare( sStr1, sStr2 ));
180                 else
181                     nPreRes = pData->Compare( sPre1, sPre2 );
182             }
183             else
184             {
185                 if ( !bNumFound1 || !bNumFound2 )
186                     return static_cast<short>(pData->ICompare( sStr1, sStr2 ));
187                 else
188                     nPreRes = pData->ICompare( sPre1, sPre2 );
189             }
190         }
191         else
192         {
193             if ( !bNumFound1 || !bNumFound2 )
194                 return static_cast<short>(pCW->compareString( sStr1, sStr2 ));
195             else
196                 nPreRes = static_cast<short>(pCW->compareString( sPre1, sPre2 ));
197         }
198 
199         // Prefix strings differ.  Return immediately.
200         if ( nPreRes != 0 ) return nPreRes;
201 
202         if ( nNum1 != nNum2 )
203         {
204             if ( nNum1 < nNum2 ) return -1;
205             return (nNum1 > nNum2) ? 1 : 0;
206         }
207 
208         // The prefix and the first numerical elements are equal, but the suffix
209         // strings may still differ.  Stay in the loop.
210 
211         sStr1 = sSuf1;
212         sStr2 = sSuf2;
213 
214     } while (true);
215 
216     return 0;
217 }
218 
219 }
220 
221 namespace {
222 
223 struct ScSortInfo final
224 {
225     ScRefCellValue maCell;
226     SCCOLROW        nOrg;
227 };
228 
229 }
230 
231 class ScSortInfoArray
232 {
233 public:
234 
235     struct Cell
236     {
237         ScRefCellValue maCell;
238         const sc::CellTextAttr* mpAttr;
239         const ScPostIt* mpNote;
240         std::vector<SdrObject*> maDrawObjects;
241         const ScPatternAttr* mpPattern;
242 
243         Cell() : mpAttr(nullptr), mpNote(nullptr),  mpPattern(nullptr) {}
244     };
245 
246     struct Row
247     {
248         std::vector<Cell> maCells;
249 
250         bool mbHidden:1;
251         bool mbFiltered:1;
252 
253         explicit Row( size_t nColSize ) : maCells(nColSize, Cell()), mbHidden(false), mbFiltered(false) {}
254     };
255 
256     typedef std::vector<Row> RowsType;
257 
258 private:
259     std::unique_ptr<RowsType> mpRows; /// row-wise data table for sort by row operation.
260 
261     std::vector<std::unique_ptr<ScSortInfo[]>> mvppInfo;
262     SCCOLROW        nStart;
263     SCCOLROW        mnLastIndex; /// index of last non-empty cell position.
264 
265     std::vector<SCCOLROW> maOrderIndices;
266     bool mbKeepQuery;
267     bool mbUpdateRefs;
268 
269 public:
270     ScSortInfoArray(const ScSortInfoArray&) = delete;
271     const ScSortInfoArray& operator=(const ScSortInfoArray&) = delete;
272 
273     ScSortInfoArray( sal_uInt16 nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) :
274         mvppInfo(nSorts),
275         nStart( nInd1 ),
276         mnLastIndex(nInd2),
277         mbKeepQuery(false),
278         mbUpdateRefs(false)
279     {
280         SCSIZE nCount( nInd2 - nInd1 + 1 );
281         if (nSorts)
282         {
283             for ( sal_uInt16 nSort = 0; nSort < nSorts; nSort++ )
284             {
285                 mvppInfo[nSort].reset(new ScSortInfo[nCount]);
286             }
287         }
288 
289         for (size_t i = 0; i < nCount; ++i)
290             maOrderIndices.push_back(i+nStart);
291     }
292 
293     void SetKeepQuery( bool b ) { mbKeepQuery = b; }
294 
295     bool IsKeepQuery() const { return mbKeepQuery; }
296 
297     void SetUpdateRefs( bool b ) { mbUpdateRefs = b; }
298 
299     bool IsUpdateRefs() const { return mbUpdateRefs; }
300 
301     /**
302      * Call this only during normal sorting, not from reordering.
303      */
304     std::unique_ptr<ScSortInfo[]> const & GetFirstArray() const
305     {
306         return mvppInfo[0];
307     }
308 
309     /**
310      * Call this only during normal sorting, not from reordering.
311      */
312     ScSortInfo & Get( sal_uInt16 nSort, SCCOLROW nInd )
313     {
314         return mvppInfo[nSort][ nInd - nStart ];
315     }
316 
317     /**
318      * Call this only during normal sorting, not from reordering.
319      */
320     void Swap( SCCOLROW nInd1, SCCOLROW nInd2 )
321     {
322         if (nInd1 == nInd2) // avoid self-move-assign
323             return;
324         SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart);
325         SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart);
326         for ( sal_uInt16 nSort = 0; nSort < static_cast<sal_uInt16>(mvppInfo.size()); nSort++ )
327         {
328             auto & ppInfo = mvppInfo[nSort];
329             std::swap(ppInfo[n1], ppInfo[n2]);
330         }
331 
332         std::swap(maOrderIndices[n1], maOrderIndices[n2]);
333 
334         if (mpRows)
335         {
336             // Swap rows in data table.
337             RowsType& rRows = *mpRows;
338             std::swap(rRows[n1], rRows[n2]);
339         }
340     }
341 
342     void SetOrderIndices( std::vector<SCCOLROW>&& rIndices )
343     {
344         maOrderIndices = std::move(rIndices);
345     }
346 
347     /**
348      * @param rIndices indices are actual row positions on the sheet, not an
349      *                 offset from the top row.
350      */
351     void ReorderByRow( const std::vector<SCCOLROW>& rIndices )
352     {
353         if (!mpRows)
354             return;
355 
356         RowsType& rRows = *mpRows;
357 
358         std::vector<SCCOLROW> aOrderIndices2;
359         aOrderIndices2.reserve(rIndices.size());
360 
361         RowsType aRows2;
362         aRows2.reserve(rRows.size());
363 
364         for (const auto& rIndex : rIndices)
365         {
366             size_t nPos = rIndex - nStart; // switch to an offset to top row.
367             aRows2.push_back(rRows[nPos]);
368             aOrderIndices2.push_back(maOrderIndices[nPos]);
369         }
370 
371         rRows.swap(aRows2);
372         maOrderIndices.swap(aOrderIndices2);
373     }
374 
375     sal_uInt16      GetUsedSorts() const { return mvppInfo.size(); }
376 
377     SCCOLROW    GetStart() const { return nStart; }
378     SCCOLROW GetLast() const { return mnLastIndex; }
379 
380     const std::vector<SCCOLROW>& GetOrderIndices() const { return maOrderIndices; }
381 
382     RowsType& InitDataRows( size_t nRowSize, size_t nColSize )
383     {
384         mpRows.reset(new RowsType);
385         mpRows->resize(nRowSize, Row(nColSize));
386         return *mpRows;
387     }
388 
389     RowsType* GetDataRows()
390     {
391         return mpRows.get();
392     }
393 };
394 
395 // Assume that we can handle 512MB, which with a ~100 bytes
396 // ScSortInfoArray::Cell element for 500MB are about 5 million cells plus
397 // overhead in one chunk.
398 constexpr sal_Int32 kSortCellsChunk = 500 * 1024 * 1024 / sizeof(ScSortInfoArray::Cell);
399 
400 namespace {
401 
402 void initDataRows(
403     ScSortInfoArray& rArray, ScTable& rTab, ScColContainer& rCols,
404     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
405     bool bHiddenFiltered, bool bPattern, bool bCellNotes, bool bCellDrawObjects, bool bOnlyDataAreaExtras )
406 {
407     // Fill row-wise data table.
408     ScSortInfoArray::RowsType& rRows = rArray.InitDataRows(nRow2-nRow1+1, nCol2-nCol1+1);
409 
410     const std::vector<SCCOLROW>& rOrderIndices = rArray.GetOrderIndices();
411     assert(!bOnlyDataAreaExtras || (rOrderIndices.size() == static_cast<size_t>(nRow2 - nRow1 + 1)
412                 && nRow1 == rArray.GetStart()));
413 
414     ScDrawLayer* pDrawLayer = (bCellDrawObjects ? rTab.GetDoc().GetDrawLayer() : nullptr);
415     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
416     {
417         ScColumn& rCol = rCols[nCol];
418 
419         // Skip reordering of cell formats if the whole span is on the same pattern entry.
420         bool bUniformPattern = rCol.GetPatternCount(nRow1, nRow2) < 2u;
421 
422         sc::ColumnBlockConstPosition aBlockPos;
423         rCol.InitBlockPosition(aBlockPos);
424         std::map<SCROW, std::vector<SdrObject*>> aRowDrawObjects;
425         if (pDrawLayer)
426             aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRange(rTab.GetTab(), nCol, nRow1, nRow2);
427 
428         for (SCROW nR = nRow1; nR <= nRow2; ++nR)
429         {
430             const SCROW nRow = (bOnlyDataAreaExtras ? rOrderIndices[nR - rArray.GetStart()] : nR);
431             ScSortInfoArray::Row& rRow = rRows[nR-nRow1];
432             ScSortInfoArray::Cell& rCell = rRow.maCells[nCol-nCol1];
433             if (!bOnlyDataAreaExtras)
434             {
435                 rCell.maCell = rCol.GetCellValue(aBlockPos, nRow);
436                 rCell.mpAttr = rCol.GetCellTextAttr(aBlockPos, nRow);
437             }
438             if (bCellNotes)
439                 rCell.mpNote = rCol.GetCellNote(aBlockPos, nRow);
440             if (pDrawLayer)
441                 rCell.maDrawObjects = aRowDrawObjects[nRow];
442 
443             if (!bUniformPattern && bPattern)
444                 rCell.mpPattern = rCol.GetPattern(nRow);
445         }
446     }
447 
448     if (!bOnlyDataAreaExtras && bHiddenFiltered)
449     {
450         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
451         {
452             ScSortInfoArray::Row& rRow = rRows[nRow-nRow1];
453             rRow.mbHidden = rTab.RowHidden(nRow);
454             rRow.mbFiltered = rTab.RowFiltered(nRow);
455         }
456     }
457 }
458 
459 }
460 
461 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray( const sc::ReorderParam& rParam )
462 {
463     std::unique_ptr<ScSortInfoArray> pArray;
464 
465     if (rParam.mbByRow)
466     {
467         // Create a sort info array with just the data table.
468         SCROW nRow1 = rParam.maSortRange.aStart.Row();
469         SCROW nRow2 = rParam.maSortRange.aEnd.Row();
470         SCCOL nCol1 = rParam.maSortRange.aStart.Col();
471         SCCOL nCol2 = rParam.maSortRange.aEnd.Col();
472 
473         pArray.reset(new ScSortInfoArray(0, nRow1, nRow2));
474         pArray->SetKeepQuery(rParam.mbHiddenFiltered);
475         pArray->SetUpdateRefs(rParam.mbUpdateRefs);
476 
477         CreateColumnIfNotExists(nCol2);
478         initDataRows( *pArray, *this, aCol, nCol1, nRow1, nCol2, nRow2, rParam.mbHiddenFiltered,
479                 rParam.maDataAreaExtras.mbCellFormats, true, true, false);
480     }
481     else
482     {
483         SCCOLROW nCol1 = rParam.maSortRange.aStart.Col();
484         SCCOLROW nCol2 = rParam.maSortRange.aEnd.Col();
485 
486         pArray.reset(new ScSortInfoArray(0, nCol1, nCol2));
487         pArray->SetKeepQuery(rParam.mbHiddenFiltered);
488         pArray->SetUpdateRefs(rParam.mbUpdateRefs);
489     }
490 
491     return pArray;
492 }
493 
494 std::unique_ptr<ScSortInfoArray> ScTable::CreateSortInfoArray(
495     const ScSortParam& rSortParam, SCCOLROW nInd1, SCCOLROW nInd2,
496     bool bKeepQuery, bool bUpdateRefs )
497 {
498     sal_uInt16 nUsedSorts = 1;
499     while ( nUsedSorts < rSortParam.GetSortKeyCount() && rSortParam.maKeyState[nUsedSorts].bDoSort )
500         nUsedSorts++;
501     std::unique_ptr<ScSortInfoArray> pArray(new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ));
502     pArray->SetKeepQuery(bKeepQuery);
503     pArray->SetUpdateRefs(bUpdateRefs);
504 
505     if ( rSortParam.bByRow )
506     {
507         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
508         {
509             SCCOL nCol = static_cast<SCCOL>(rSortParam.maKeyState[nSort].nField);
510             ScColumn* pCol = &aCol[nCol];
511             sc::ColumnBlockConstPosition aBlockPos;
512             pCol->InitBlockPosition(aBlockPos);
513             for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ )
514             {
515                 ScSortInfo & rInfo = pArray->Get( nSort, nRow );
516                 rInfo.maCell = pCol->GetCellValue(aBlockPos, nRow);
517                 rInfo.nOrg = nRow;
518             }
519         }
520 
521         CreateColumnIfNotExists(rSortParam.nCol2);
522         initDataRows( *pArray, *this, aCol, rSortParam.nCol1, nInd1, rSortParam.nCol2, nInd2, bKeepQuery,
523                 rSortParam.aDataAreaExtras.mbCellFormats, true, true, false);
524     }
525     else
526     {
527         for ( sal_uInt16 nSort = 0; nSort < nUsedSorts; nSort++ )
528         {
529             SCROW nRow = rSortParam.maKeyState[nSort].nField;
530             for ( SCCOL nCol = static_cast<SCCOL>(nInd1);
531                     nCol <= static_cast<SCCOL>(nInd2); nCol++ )
532             {
533                 ScSortInfo & rInfo = pArray->Get( nSort, nCol );
534                 rInfo.maCell = GetCellValue(nCol, nRow);
535                 rInfo.nOrg = nCol;
536             }
537         }
538     }
539     return pArray;
540 }
541 
542 namespace {
543 
544 struct SortedColumn
545 {
546     typedef mdds::flat_segment_tree<SCROW, const ScPatternAttr*> PatRangeType;
547 
548     sc::CellStoreType maCells;
549     sc::CellTextAttrStoreType maCellTextAttrs;
550     sc::BroadcasterStoreType maBroadcasters;
551     sc::CellNoteStoreType maCellNotes;
552     std::vector<std::vector<SdrObject*>> maCellDrawObjects;
553 
554     PatRangeType maPatterns;
555     PatRangeType::const_iterator miPatternPos;
556 
557     SortedColumn(const SortedColumn&) = delete;
558     const SortedColumn operator=(const SortedColumn&) = delete;
559 
560     explicit SortedColumn( size_t nTopEmptyRows, const ScSheetLimits& rSheetLimits ) :
561         maCells(nTopEmptyRows),
562         maCellTextAttrs(nTopEmptyRows),
563         maBroadcasters(nTopEmptyRows),
564         maCellNotes(nTopEmptyRows),
565         maPatterns(0, rSheetLimits.GetMaxRowCount(), nullptr),
566         miPatternPos(maPatterns.begin()) {}
567 
568     void setPattern( SCROW nRow, const ScPatternAttr* pPat )
569     {
570         miPatternPos = maPatterns.insert(miPatternPos, nRow, nRow+1, pPat).first;
571     }
572 };
573 
574 struct SortedRowFlags
575 {
576     typedef mdds::flat_segment_tree<SCROW,bool> FlagsType;
577 
578     FlagsType maRowsHidden;
579     FlagsType maRowsFiltered;
580     FlagsType::const_iterator miPosHidden;
581     FlagsType::const_iterator miPosFiltered;
582 
583     SortedRowFlags(const ScSheetLimits& rSheetLimits) :
584         maRowsHidden(0, rSheetLimits.GetMaxRowCount(), false),
585         maRowsFiltered(0, rSheetLimits.GetMaxRowCount(), false),
586         miPosHidden(maRowsHidden.begin()),
587         miPosFiltered(maRowsFiltered.begin()) {}
588 
589     void setRowHidden( SCROW nRow, bool b )
590     {
591         miPosHidden = maRowsHidden.insert(miPosHidden, nRow, nRow+1, b).first;
592     }
593 
594     void setRowFiltered( SCROW nRow, bool b )
595     {
596         miPosFiltered = maRowsFiltered.insert(miPosFiltered, nRow, nRow+1, b).first;
597     }
598 
599     void swap( SortedRowFlags& r )
600     {
601         maRowsHidden.swap(r.maRowsHidden);
602         maRowsFiltered.swap(r.maRowsFiltered);
603 
604         // Just reset the position hints.
605         miPosHidden = maRowsHidden.begin();
606         miPosFiltered = maRowsFiltered.begin();
607     }
608 };
609 
610 struct PatternSpan
611 {
612     SCROW mnRow1;
613     SCROW mnRow2;
614     const ScPatternAttr* mpPattern;
615 
616     PatternSpan( SCROW nRow1, SCROW nRow2, const ScPatternAttr* pPat ) :
617         mnRow1(nRow1), mnRow2(nRow2), mpPattern(pPat) {}
618 };
619 
620 }
621 
622 bool ScTable::IsSortCollatorGlobal() const
623 {
624     return  pSortCollator == &ScGlobal::GetCollator() ||
625             pSortCollator == &ScGlobal::GetCaseCollator();
626 }
627 
628 void ScTable::InitSortCollator( const ScSortParam& rPar )
629 {
630     if ( !rPar.aCollatorLocale.Language.isEmpty() )
631     {
632         if ( !pSortCollator || IsSortCollatorGlobal() )
633             pSortCollator = new CollatorWrapper( comphelper::getProcessComponentContext() );
634         pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm,
635             rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) );
636     }
637     else
638     {   // SYSTEM
639         DestroySortCollator();
640         pSortCollator = &ScGlobal::GetCollator(rPar.bCaseSens);
641     }
642 }
643 
644 void ScTable::DestroySortCollator()
645 {
646     if ( pSortCollator )
647     {
648         if ( !IsSortCollatorGlobal() )
649             delete pSortCollator;
650         pSortCollator = nullptr;
651     }
652 }
653 
654 namespace {
655 
656 template<typename Hint, typename ReorderMap, typename Index>
657 class ReorderNotifier
658 {
659     Hint maHint;
660 public:
661     ReorderNotifier( const ReorderMap& rMap, SCTAB nTab, Index nPos1, Index nPos2 ) :
662         maHint(rMap, nTab, nPos1, nPos2) {}
663 
664     void operator() ( SvtListener* p )
665     {
666         p->Notify(maHint);
667     }
668 };
669 
670 class FormulaGroupPosCollector
671 {
672     sc::RefQueryFormulaGroup& mrQuery;
673 
674 public:
675     explicit FormulaGroupPosCollector( sc::RefQueryFormulaGroup& rQuery ) : mrQuery(rQuery) {}
676 
677     void operator() ( const SvtListener* p )
678     {
679         p->Query(mrQuery);
680     }
681 };
682 
683 void fillSortedColumnArray(
684     std::vector<std::unique_ptr<SortedColumn>>& rSortedCols,
685     SortedRowFlags& rRowFlags,
686     std::vector<SvtListener*>& rCellListeners,
687     ScSortInfoArray* pArray, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress, const ScTable* pTable,
688     bool bOnlyDataAreaExtras )
689 {
690     assert(!bOnlyDataAreaExtras || !pArray->IsUpdateRefs());
691 
692     SCROW nRow1 = pArray->GetStart();
693     ScSortInfoArray::RowsType* pRows = pArray->GetDataRows();
694     std::vector<SCCOLROW> aOrderIndices = pArray->GetOrderIndices();
695 
696     size_t nColCount = nCol2 - nCol1 + 1;
697     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
698     SortedRowFlags aRowFlags(pTable->GetDoc().GetSheetLimits());
699     aSortedCols.reserve(nColCount);
700     for (size_t i = 0; i < nColCount; ++i)
701     {
702         // In the sorted column container, element positions and row
703         // positions must match, else formula cells may mis-behave during
704         // grouping.
705         aSortedCols.push_back(std::make_unique<SortedColumn>(nRow1, pTable->GetDoc().GetSheetLimits()));
706     }
707 
708     for (size_t i = 0; i < pRows->size(); ++i)
709     {
710         const SCROW nRow = nRow1 + i;
711 
712         ScSortInfoArray::Row& rRow = (*pRows)[i];
713         for (size_t j = 0; j < rRow.maCells.size(); ++j)
714         {
715             ScSortInfoArray::Cell& rCell = rRow.maCells[j];
716 
717             // If bOnlyDataAreaExtras,
718             // sc::CellStoreType aSortedCols.at(j)->maCells
719             // and
720             // sc::CellTextAttrStoreType aSortedCols.at(j)->maCellTextAttrs
721             // are by definition all empty mdds::multi_type_vector, so nothing
722             // needs to be done to push *all* empty.
723 
724             if (!bOnlyDataAreaExtras)
725             {
726                 sc::CellStoreType& rCellStore = aSortedCols.at(j)->maCells;
727                 switch (rCell.maCell.getType())
728                 {
729                     case CELLTYPE_STRING:
730                         assert(rCell.mpAttr);
731                         rCellStore.push_back(*rCell.maCell.getSharedString());
732                     break;
733                     case CELLTYPE_VALUE:
734                         assert(rCell.mpAttr);
735                         rCellStore.push_back(rCell.maCell.getDouble());
736                     break;
737                     case CELLTYPE_EDIT:
738                         assert(rCell.mpAttr);
739                         rCellStore.push_back(rCell.maCell.getEditText()->Clone().release());
740                     break;
741                     case CELLTYPE_FORMULA:
742                         {
743                             assert(rCell.mpAttr);
744                             ScAddress aOldPos = rCell.maCell.getFormula()->aPos;
745 
746                             const ScAddress aCellPos(nCol1 + j, nRow, nTab);
747                             ScFormulaCell* pNew = rCell.maCell.getFormula()->Clone( aCellPos );
748                             if (pArray->IsUpdateRefs())
749                             {
750                                 pNew->CopyAllBroadcasters(*rCell.maCell.getFormula());
751                                 pNew->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, aCellPos);
752                             }
753                             else
754                             {
755                                 pNew->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, aCellPos);
756                             }
757 
758                             if (!rCellListeners.empty())
759                             {
760                                 // Original source cells will be deleted during
761                                 // sc::CellStoreType::transfer(), SvtListener is a base
762                                 // class, so we need to replace it.
763                                 auto it( ::std::find( rCellListeners.begin(), rCellListeners.end(), rCell.maCell.getFormula()));
764                                 if (it != rCellListeners.end())
765                                     *it = pNew;
766                             }
767 
768                             rCellStore.push_back(pNew);
769                         }
770                     break;
771                     default:
772                         //assert(!rCell.mpAttr);
773                         // This assert doesn't hold, for example
774                         // CopyCellsFromClipHandler may omit copying cells during
775                         // PasteSpecial for which CopyTextAttrsFromClipHandler
776                         // still copies a CellTextAttr. So if that really is not
777                         // expected then fix it there.
778                         rCellStore.push_back_empty();
779                 }
780 
781                 sc::CellTextAttrStoreType& rAttrStore = aSortedCols.at(j)->maCellTextAttrs;
782                 if (rCell.mpAttr)
783                     rAttrStore.push_back(*rCell.mpAttr);
784                 else
785                     rAttrStore.push_back_empty();
786             }
787 
788             if (pArray->IsUpdateRefs())
789             {
790                 // At this point each broadcaster instance is managed by 2
791                 // containers. We will release those in the original storage
792                 // below before transferring them to the document.
793                 const SvtBroadcaster* pBroadcaster = pTable->GetBroadcaster( nCol1 + j, aOrderIndices[i]);
794                 sc::BroadcasterStoreType& rBCStore = aSortedCols.at(j)->maBroadcasters;
795                 if (pBroadcaster)
796                     // A const pointer would be implicitly converted to a bool type.
797                     rBCStore.push_back(const_cast<SvtBroadcaster*>(pBroadcaster));
798                 else
799                     rBCStore.push_back_empty();
800             }
801 
802             // The same with cell note instances ...
803             sc::CellNoteStoreType& rNoteStore = aSortedCols.at(j)->maCellNotes;
804             if (rCell.mpNote)
805                 rNoteStore.push_back(const_cast<ScPostIt*>(rCell.mpNote));
806             else
807                 rNoteStore.push_back_empty();
808 
809             // Add cell anchored images
810             aSortedCols.at(j)->maCellDrawObjects.push_back(rCell.maDrawObjects);
811 
812             if (rCell.mpPattern)
813                 aSortedCols.at(j)->setPattern(nRow, rCell.mpPattern);
814         }
815 
816         if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
817         {
818             // Hidden and filtered flags are first converted to segments.
819             aRowFlags.setRowHidden(nRow, rRow.mbHidden);
820             aRowFlags.setRowFiltered(nRow, rRow.mbFiltered);
821         }
822 
823         if (pProgress)
824             pProgress->SetStateOnPercent(i);
825     }
826 
827     rSortedCols.swap(aSortedCols);
828     rRowFlags.swap(aRowFlags);
829 }
830 
831 void expandRowRange( ScRange& rRange, SCROW nTop, SCROW nBottom )
832 {
833     if (nTop < rRange.aStart.Row())
834         rRange.aStart.SetRow(nTop);
835 
836     if (rRange.aEnd.Row() < nBottom)
837         rRange.aEnd.SetRow(nBottom);
838 }
839 
840 class FormulaCellCollectAction : public sc::ColumnSpanSet::ColumnAction
841 {
842     std::vector<ScFormulaCell*>& mrCells;
843     ScColumn* mpCol;
844 
845 public:
846     explicit FormulaCellCollectAction( std::vector<ScFormulaCell*>& rCells ) :
847         mrCells(rCells), mpCol(nullptr) {}
848 
849     virtual void startColumn( ScColumn* pCol ) override
850     {
851         mpCol = pCol;
852     }
853 
854     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
855     {
856         assert(mpCol);
857 
858         if (!bVal)
859             return;
860 
861         mpCol->CollectFormulaCells(mrCells, nRow1, nRow2);
862     }
863 };
864 
865 class ListenerStartAction : public sc::ColumnSpanSet::ColumnAction
866 {
867     ScColumn* mpCol;
868 
869     std::shared_ptr<sc::ColumnBlockPositionSet> mpPosSet;
870     sc::StartListeningContext maStartCxt;
871     sc::EndListeningContext maEndCxt;
872 
873 public:
874     explicit ListenerStartAction( ScDocument& rDoc ) :
875         mpCol(nullptr),
876         mpPosSet(std::make_shared<sc::ColumnBlockPositionSet>(rDoc)),
877         maStartCxt(rDoc, mpPosSet),
878         maEndCxt(rDoc, mpPosSet) {}
879 
880     virtual void startColumn( ScColumn* pCol ) override
881     {
882         mpCol = pCol;
883     }
884 
885     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
886     {
887         assert(mpCol);
888 
889         if (!bVal)
890             return;
891 
892         mpCol->StartListeningFormulaCells(maStartCxt, maEndCxt, nRow1, nRow2);
893     }
894 };
895 
896 }
897 
898 void ScTable::SortReorderAreaExtrasByRow( ScSortInfoArray* pArray,
899         SCCOL nDataCol1, SCCOL nDataCol2,
900         const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
901 {
902     const SCROW nRow1 = pArray->GetStart();
903     const SCROW nLastRow = pArray->GetLast();
904     const SCCOL nChunkCols = std::max<SCCOL>( 1, kSortCellsChunk / (nLastRow - nRow1 + 1));
905     // Before data area.
906     for (SCCOL nCol = rDataAreaExtras.mnStartCol; nCol < nDataCol1; nCol += nChunkCols)
907     {
908         const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, nDataCol1 - 1);
909         CreateColumnIfNotExists(nEndCol);
910         initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
911                 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
912         SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
913     }
914     // Behind data area.
915     for (SCCOL nCol = nDataCol2 + 1; nCol <= rDataAreaExtras.mnEndCol; nCol += nChunkCols)
916     {
917         const SCCOL nEndCol = std::min<SCCOL>( nCol + nChunkCols - 1, rDataAreaExtras.mnEndCol);
918         CreateColumnIfNotExists(nEndCol);
919         initDataRows( *pArray, *this, aCol, nCol, nRow1, nEndCol, nLastRow, false,
920                 rDataAreaExtras.mbCellFormats, rDataAreaExtras.mbCellNotes, rDataAreaExtras.mbCellDrawObjects, true);
921         SortReorderByRow( pArray, nCol, nEndCol, pProgress, true);
922     }
923 }
924 
925 void ScTable::SortReorderAreaExtrasByColumn( const ScSortInfoArray* pArray,
926         SCROW nDataRow1, SCROW nDataRow2, const ScDataAreaExtras& rDataAreaExtras, ScProgress* pProgress )
927 {
928     const SCCOL nCol1 = static_cast<SCCOL>(pArray->GetStart());
929     const SCCOL nLastCol = static_cast<SCCOL>(pArray->GetLast());
930     const SCROW nChunkRows = std::max<SCROW>( 1, kSortCellsChunk / (nLastCol - nCol1 + 1));
931     // Above data area.
932     for (SCROW nRow = rDataAreaExtras.mnStartRow; nRow < nDataRow1; nRow += nChunkRows)
933     {
934         const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, nDataRow1 - 1);
935         SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
936     }
937     // Below data area.
938     for (SCROW nRow = nDataRow2 + 1; nRow <= rDataAreaExtras.mnEndRow; nRow += nChunkRows)
939     {
940         const SCROW nEndRow = std::min<SCROW>( nRow + nChunkRows - 1, rDataAreaExtras.mnEndRow);
941         SortReorderByColumn( pArray, nRow, nEndRow, rDataAreaExtras.mbCellFormats, pProgress);
942     }
943 }
944 
945 void ScTable::SortReorderByColumn(
946     const ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2, bool bPattern, ScProgress* pProgress )
947 {
948     SCCOLROW nStart = pArray->GetStart();
949     SCCOLROW nLast = pArray->GetLast();
950 
951     std::vector<SCCOLROW> aIndices = pArray->GetOrderIndices();
952     size_t nCount = aIndices.size();
953 
954     // Cut formula grouping at row and reference boundaries before the reordering.
955     ScRange aSortRange(nStart, nRow1, nTab, nLast, nRow2, nTab);
956     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
957         aCol[nCol].SplitFormulaGroupByRelativeRef(aSortRange);
958 
959     // Collect all listeners of cell broadcasters of sorted range.
960     std::vector<SvtListener*> aCellListeners;
961 
962     if (!pArray->IsUpdateRefs())
963     {
964         // Collect listeners of cell broadcasters.
965         for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
966             aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
967 
968         // Remove any duplicate listener entries.  We must ensure that we
969         // notify each unique listener only once.
970         std::sort(aCellListeners.begin(), aCellListeners.end());
971         aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
972 
973         // Notify the cells' listeners to stop listening.
974         /* TODO: for performance this could be enhanced to stop and later
975          * restart only listening to within the reordered range and keep
976          * listening to everything outside untouched. */
977         sc::RefStopListeningHint aHint;
978         for (auto const & l : aCellListeners)
979             l->Notify(aHint);
980     }
981 
982     // table to keep track of column index to position in the index table.
983     std::vector<SCCOLROW> aPosTable(nCount);
984     for (size_t i = 0; i < nCount; ++i)
985         aPosTable[aIndices[i]-nStart] = i;
986 
987     SCCOLROW nDest = nStart;
988     for (size_t i = 0; i < nCount; ++i, ++nDest)
989     {
990         SCCOLROW nSrc = aIndices[i];
991         if (nDest != nSrc)
992         {
993             aCol[nDest].Swap(aCol[nSrc], nRow1, nRow2, bPattern);
994 
995             // Update the position of the index that was originally equal to nDest.
996             size_t nPos = aPosTable[nDest-nStart];
997             aIndices[nPos] = nSrc;
998             aPosTable[nSrc-nStart] = nPos;
999         }
1000 
1001         if (pProgress)
1002             pProgress->SetStateOnPercent(i);
1003     }
1004 
1005     // Reset formula cell positions which became out-of-sync after column reordering.
1006     bool bUpdateRefs = pArray->IsUpdateRefs();
1007     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1008         aCol[nCol].ResetFormulaCellPositions(nRow1, nRow2, bUpdateRefs);
1009 
1010     if (pArray->IsUpdateRefs())
1011     {
1012         // Set up column reorder map (for later broadcasting of reference updates).
1013         sc::ColRowReorderMapType aColMap;
1014         const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1015         for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1016         {
1017             SCCOL nNew = i + nStart;
1018             SCCOL nOld = rOldIndices[i];
1019             aColMap.emplace(nOld, nNew);
1020         }
1021 
1022         // Collect all listeners within sorted range ahead of time.
1023         std::vector<SvtListener*> aListeners;
1024 
1025         for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1026             aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1027 
1028         // Get all area listeners that listen on one column within the range
1029         // and end their listening.
1030         ScRange aMoveRange( nStart, nRow1, nTab, nLast, nRow2, nTab);
1031         std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1032                 aMoveRange, sc::AreaOverlapType::OneColumnInside);
1033         {
1034             for (auto& rAreaListener : aAreaListeners)
1035             {
1036                 rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1037                 aListeners.push_back( rAreaListener.mpListener);
1038             }
1039         }
1040 
1041         // Remove any duplicate listener entries and notify all listeners
1042         // afterward.  We must ensure that we notify each unique listener only
1043         // once.
1044         std::sort(aListeners.begin(), aListeners.end());
1045         aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1046         ReorderNotifier<sc::RefColReorderHint, sc::ColRowReorderMapType, SCCOL> aFunc(aColMap, nTab, nRow1, nRow2);
1047         std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1048 
1049         // Re-start area listeners on the reordered columns.
1050         {
1051             for (auto& rAreaListener : aAreaListeners)
1052             {
1053                 ScRange aNewRange = rAreaListener.maArea;
1054                 sc::ColRowReorderMapType::const_iterator itCol = aColMap.find( aNewRange.aStart.Col());
1055                 if (itCol != aColMap.end())
1056                 {
1057                     aNewRange.aStart.SetCol( itCol->second);
1058                     aNewRange.aEnd.SetCol( itCol->second);
1059                 }
1060                 rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1061             }
1062         }
1063     }
1064     else    // !(pArray->IsUpdateRefs())
1065     {
1066         // Notify the cells' listeners to (re-)start listening.
1067         sc::RefStartListeningHint aHint;
1068         for (auto const & l : aCellListeners)
1069             l->Notify(aHint);
1070     }
1071 
1072     // Re-join formulas at row boundaries now that all the references have
1073     // been adjusted for column reordering.
1074     for (SCCOL nCol = nStart; nCol <= static_cast<SCCOL>(nLast); ++nCol)
1075     {
1076         sc::CellStoreType& rCells = aCol[nCol].maCells;
1077         sc::CellStoreType::position_type aPos = rCells.position(nRow1);
1078         sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1079         if (nRow2 < rDocument.MaxRow())
1080         {
1081             aPos = rCells.position(aPos.first, nRow2+1);
1082             sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1083         }
1084     }
1085 }
1086 
1087 void ScTable::SortReorderByRow( ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2,
1088         ScProgress* pProgress, bool bOnlyDataAreaExtras )
1089 {
1090     assert(!pArray->IsUpdateRefs());
1091 
1092     if (nCol2 < nCol1)
1093         return;
1094 
1095     // bOnlyDataAreaExtras:
1096     // Data area extras by definition do not have any cell content so no
1097     // formula cells either, so that handling doesn't need to be executed.
1098     // However, there may be listeners of formulas listening to broadcasters of
1099     // empty cells.
1100 
1101     SCROW nRow1 = pArray->GetStart();
1102     SCROW nRow2 = pArray->GetLast();
1103 
1104     // Collect all listeners of cell broadcasters of sorted range.
1105     std::vector<SvtListener*> aCellListeners;
1106 
1107     // When the update ref mode is disabled, we need to detach all formula
1108     // cells in the sorted range before reordering, and re-start them
1109     // afterward.
1110     if (!bOnlyDataAreaExtras)
1111     {
1112         sc::EndListeningContext aCxt(rDocument);
1113         DetachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1114     }
1115 
1116     // Collect listeners of cell broadcasters.
1117     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1118         aCol[nCol].CollectListeners(aCellListeners, nRow1, nRow2);
1119 
1120     // Remove any duplicate listener entries.  We must ensure that we notify
1121     // each unique listener only once.
1122     std::sort(aCellListeners.begin(), aCellListeners.end());
1123     aCellListeners.erase(std::unique(aCellListeners.begin(), aCellListeners.end()), aCellListeners.end());
1124 
1125     // Notify the cells' listeners to stop listening.
1126     /* TODO: for performance this could be enhanced to stop and later
1127      * restart only listening to within the reordered range and keep
1128      * listening to everything outside untouched. */
1129     {
1130         sc::RefStopListeningHint aHint;
1131         for (auto const & l : aCellListeners)
1132             l->Notify(aHint);
1133     }
1134 
1135     // Split formula groups at the sort range boundaries (if applicable).
1136     if (!bOnlyDataAreaExtras)
1137     {
1138         std::vector<SCROW> aRowBounds
1139         {
1140             nRow1,
1141             nRow2+1
1142         };
1143         for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1144             SplitFormulaGroups(nCol, aRowBounds);
1145     }
1146 
1147     // Cells in the data rows only reference values in the document. Make
1148     // a copy before updating the document.
1149     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1150     SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1151     fillSortedColumnArray(aSortedCols, aRowFlags, aCellListeners, pArray, nTab, nCol1, nCol2,
1152             pProgress, this, bOnlyDataAreaExtras);
1153 
1154     for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1155     {
1156         SCCOL nThisCol = i + nCol1;
1157 
1158         if (!bOnlyDataAreaExtras)
1159         {
1160             {
1161                 sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1162                 sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1163                 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1164             }
1165 
1166             {
1167                 sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1168                 sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1169                 rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1170             }
1171         }
1172 
1173         {
1174             sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1175             sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1176 
1177             // Do the same as broadcaster storage transfer (to prevent double deletion).
1178             rDest.release_range(nRow1, nRow2);
1179             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1180             aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1181         }
1182 
1183         // Update draw object positions
1184         aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1185 
1186         {
1187             // Get all row spans where the pattern is not NULL.
1188             std::vector<PatternSpan> aSpans =
1189                 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1190                     aSortedCols[i]->maPatterns);
1191 
1192             for (const auto& rSpan : aSpans)
1193             {
1194                 assert(rSpan.mpPattern); // should never be NULL.
1195                 rDocument.GetPool()->Put(*rSpan.mpPattern);
1196             }
1197 
1198             for (const auto& rSpan : aSpans)
1199             {
1200                 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1201                 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1202             }
1203         }
1204 
1205         aCol[nThisCol].CellStorageModified();
1206     }
1207 
1208     if (!bOnlyDataAreaExtras && pArray->IsKeepQuery())
1209     {
1210         aRowFlags.maRowsHidden.build_tree();
1211         aRowFlags.maRowsFiltered.build_tree();
1212 
1213         // Remove all flags in the range first.
1214         SetRowHidden(nRow1, nRow2, false);
1215         SetRowFiltered(nRow1, nRow2, false);
1216 
1217         std::vector<sc::RowSpan> aSpans =
1218             sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1219 
1220         for (const auto& rSpan : aSpans)
1221             SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1222 
1223         aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1224 
1225         for (const auto& rSpan : aSpans)
1226             SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1227     }
1228 
1229     // Notify the cells' listeners to (re-)start listening.
1230     {
1231         sc::RefStartListeningHint aHint;
1232         for (auto const & l : aCellListeners)
1233             l->Notify(aHint);
1234     }
1235 
1236     if (!bOnlyDataAreaExtras)
1237     {
1238         // Re-group columns in the sorted range too.
1239         for (SCCOL i = nCol1; i <= nCol2; ++i)
1240             aCol[i].RegroupFormulaCells();
1241 
1242         {
1243             sc::StartListeningContext aCxt(rDocument);
1244             AttachFormulaCells(aCxt, nCol1, nRow1, nCol2, nRow2);
1245         }
1246     }
1247 }
1248 
1249 void ScTable::SortReorderByRowRefUpdate(
1250     ScSortInfoArray* pArray, SCCOL nCol1, SCCOL nCol2, ScProgress* pProgress )
1251 {
1252     assert(pArray->IsUpdateRefs());
1253 
1254     if (nCol2 < nCol1)
1255         return;
1256 
1257     SCROW nRow1 = pArray->GetStart();
1258     SCROW nRow2 = pArray->GetLast();
1259 
1260     ScRange aMoveRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1261     sc::ColumnSpanSet aGrpListenerRanges;
1262 
1263     {
1264         // Get the range of formula group listeners within sorted range (if any).
1265         sc::QueryRange aQuery;
1266 
1267         ScBroadcastAreaSlotMachine* pBASM = rDocument.GetBASM();
1268         std::vector<sc::AreaListener> aGrpListeners =
1269             pBASM->GetAllListeners(
1270                 aMoveRange, sc::AreaOverlapType::InsideOrOverlap, sc::ListenerGroupType::Group);
1271 
1272         {
1273             for (const auto& rGrpListener : aGrpListeners)
1274             {
1275                 assert(rGrpListener.mbGroupListening);
1276                 SvtListener* pGrpLis = rGrpListener.mpListener;
1277                 pGrpLis->Query(aQuery);
1278                 rDocument.EndListeningArea(rGrpListener.maArea, rGrpListener.mbGroupListening, pGrpLis);
1279             }
1280         }
1281 
1282         ScRangeList aTmp;
1283         aQuery.swapRanges(aTmp);
1284 
1285         // If the range is within the sorted range, we need to expand its rows
1286         // to the top and bottom of the sorted range, since the formula cells
1287         // could be anywhere in the sorted range after reordering.
1288         for (size_t i = 0, n = aTmp.size(); i < n; ++i)
1289         {
1290             ScRange aRange = aTmp[i];
1291             if (!aMoveRange.Intersects(aRange))
1292             {
1293                 // Doesn't overlap with the sorted range at all.
1294                 aGrpListenerRanges.set(GetDoc(), aRange, true);
1295                 continue;
1296             }
1297 
1298             if (aMoveRange.aStart.Col() <= aRange.aStart.Col() && aRange.aEnd.Col() <= aMoveRange.aEnd.Col())
1299             {
1300                 // Its column range is within the column range of the sorted range.
1301                 expandRowRange(aRange, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1302                 aGrpListenerRanges.set(GetDoc(), aRange, true);
1303                 continue;
1304             }
1305 
1306             // It intersects with the sorted range, but its column range is
1307             // not within the column range of the sorted range.  Split it into
1308             // 2 ranges.
1309             ScRange aR1 = aRange;
1310             ScRange aR2 = aRange;
1311             if (aRange.aStart.Col() < aMoveRange.aStart.Col())
1312             {
1313                 // Left half is outside the sorted range while the right half is inside.
1314                 aR1.aEnd.SetCol(aMoveRange.aStart.Col()-1);
1315                 aR2.aStart.SetCol(aMoveRange.aStart.Col());
1316                 expandRowRange(aR2, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1317             }
1318             else
1319             {
1320                 // Left half is inside the sorted range while the right half is outside.
1321                 aR1.aEnd.SetCol(aMoveRange.aEnd.Col()-1);
1322                 aR2.aStart.SetCol(aMoveRange.aEnd.Col());
1323                 expandRowRange(aR1, aMoveRange.aStart.Row(), aMoveRange.aEnd.Row());
1324             }
1325 
1326             aGrpListenerRanges.set(GetDoc(), aR1, true);
1327             aGrpListenerRanges.set(GetDoc(), aR2, true);
1328         }
1329     }
1330 
1331     // Split formula groups at the sort range boundaries (if applicable).
1332     std::vector<SCROW> aRowBounds
1333     {
1334         nRow1,
1335         nRow2+1
1336     };
1337     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1338         SplitFormulaGroups(nCol, aRowBounds);
1339 
1340     // Cells in the data rows only reference values in the document. Make
1341     // a copy before updating the document.
1342     std::vector<std::unique_ptr<SortedColumn>> aSortedCols; // storage for copied cells.
1343     SortedRowFlags aRowFlags(GetDoc().GetSheetLimits());
1344     std::vector<SvtListener*> aListenersDummy;
1345     fillSortedColumnArray(aSortedCols, aRowFlags, aListenersDummy, pArray, nTab, nCol1, nCol2, pProgress, this, false);
1346 
1347     for (size_t i = 0, n = aSortedCols.size(); i < n; ++i)
1348     {
1349         SCCOL nThisCol = i + nCol1;
1350 
1351         {
1352             sc::CellStoreType& rDest = aCol[nThisCol].maCells;
1353             sc::CellStoreType& rSrc = aSortedCols[i]->maCells;
1354             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1355         }
1356 
1357         {
1358             sc::CellTextAttrStoreType& rDest = aCol[nThisCol].maCellTextAttrs;
1359             sc::CellTextAttrStoreType& rSrc = aSortedCols[i]->maCellTextAttrs;
1360             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1361         }
1362 
1363         {
1364             sc::BroadcasterStoreType& rSrc = aSortedCols[i]->maBroadcasters;
1365             sc::BroadcasterStoreType& rDest = aCol[nThisCol].maBroadcasters;
1366 
1367             // Release current broadcasters first, to prevent them from getting deleted.
1368             rDest.release_range(nRow1, nRow2);
1369 
1370             // Transfer sorted broadcaster segment to the document.
1371             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1372         }
1373 
1374         {
1375             sc::CellNoteStoreType& rSrc = aSortedCols[i]->maCellNotes;
1376             sc::CellNoteStoreType& rDest = aCol[nThisCol].maCellNotes;
1377 
1378             // Do the same as broadcaster storage transfer (to prevent double deletion).
1379             rDest.release_range(nRow1, nRow2);
1380             rSrc.transfer(nRow1, nRow2, rDest, nRow1);
1381             aCol[nThisCol].UpdateNoteCaptions(nRow1, nRow2);
1382         }
1383 
1384         // Update draw object positions
1385         aCol[nThisCol].UpdateDrawObjects(aSortedCols[i]->maCellDrawObjects, nRow1, nRow2);
1386 
1387         {
1388             // Get all row spans where the pattern is not NULL.
1389             std::vector<PatternSpan> aSpans =
1390                 sc::toSpanArrayWithValue<SCROW,const ScPatternAttr*,PatternSpan>(
1391                     aSortedCols[i]->maPatterns);
1392 
1393             for (const auto& rSpan : aSpans)
1394             {
1395                 assert(rSpan.mpPattern); // should never be NULL.
1396                 rDocument.GetPool()->Put(*rSpan.mpPattern);
1397             }
1398 
1399             for (const auto& rSpan : aSpans)
1400             {
1401                 aCol[nThisCol].SetPatternArea(rSpan.mnRow1, rSpan.mnRow2, *rSpan.mpPattern);
1402                 rDocument.GetPool()->Remove(*rSpan.mpPattern);
1403             }
1404         }
1405 
1406         aCol[nThisCol].CellStorageModified();
1407     }
1408 
1409     if (pArray->IsKeepQuery())
1410     {
1411         aRowFlags.maRowsHidden.build_tree();
1412         aRowFlags.maRowsFiltered.build_tree();
1413 
1414         // Remove all flags in the range first.
1415         SetRowHidden(nRow1, nRow2, false);
1416         SetRowFiltered(nRow1, nRow2, false);
1417 
1418         std::vector<sc::RowSpan> aSpans =
1419             sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsHidden, nRow1);
1420 
1421         for (const auto& rSpan : aSpans)
1422             SetRowHidden(rSpan.mnRow1, rSpan.mnRow2, true);
1423 
1424         aSpans = sc::toSpanArray<SCROW,sc::RowSpan>(aRowFlags.maRowsFiltered, nRow1);
1425 
1426         for (const auto& rSpan : aSpans)
1427             SetRowFiltered(rSpan.mnRow1, rSpan.mnRow2, true);
1428     }
1429 
1430     // Set up row reorder map (for later broadcasting of reference updates).
1431     sc::ColRowReorderMapType aRowMap;
1432     const std::vector<SCCOLROW>& rOldIndices = pArray->GetOrderIndices();
1433     for (size_t i = 0, n = rOldIndices.size(); i < n; ++i)
1434     {
1435         SCROW nNew = i + nRow1;
1436         SCROW nOld = rOldIndices[i];
1437         aRowMap.emplace(nOld, nNew);
1438     }
1439 
1440     // Collect all listeners within sorted range ahead of time.
1441     std::vector<SvtListener*> aListeners;
1442 
1443     // Collect listeners of cell broadcasters.
1444     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
1445         aCol[nCol].CollectListeners(aListeners, nRow1, nRow2);
1446 
1447     // Get all area listeners that listen on one row within the range and end
1448     // their listening.
1449     std::vector<sc::AreaListener> aAreaListeners = rDocument.GetBASM()->GetAllListeners(
1450             aMoveRange, sc::AreaOverlapType::OneRowInside);
1451     {
1452         for (auto& rAreaListener : aAreaListeners)
1453         {
1454             rDocument.EndListeningArea(rAreaListener.maArea, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1455             aListeners.push_back( rAreaListener.mpListener);
1456         }
1457     }
1458 
1459     {
1460         // Get all formula cells from the former group area listener ranges.
1461 
1462         std::vector<ScFormulaCell*> aFCells;
1463         FormulaCellCollectAction aAction(aFCells);
1464         aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1465 
1466         aListeners.insert( aListeners.end(), aFCells.begin(), aFCells.end() );
1467     }
1468 
1469     // Remove any duplicate listener entries.  We must ensure that we notify
1470     // each unique listener only once.
1471     std::sort(aListeners.begin(), aListeners.end());
1472     aListeners.erase(std::unique(aListeners.begin(), aListeners.end()), aListeners.end());
1473 
1474     // Collect positions of all shared formula cells outside the sorted range,
1475     // and make them unshared before notifying them.
1476     sc::RefQueryFormulaGroup aFormulaGroupPos;
1477     aFormulaGroupPos.setSkipRange(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1478 
1479     std::for_each(aListeners.begin(), aListeners.end(), FormulaGroupPosCollector(aFormulaGroupPos));
1480     const sc::RefQueryFormulaGroup::TabsType& rGroupTabs = aFormulaGroupPos.getAllPositions();
1481     for (const auto& [rTab, rCols] : rGroupTabs)
1482     {
1483         for (const auto& [nCol, rCol] : rCols)
1484         {
1485             std::vector<SCROW> aBounds(rCol);
1486             rDocument.UnshareFormulaCells(rTab, nCol, aBounds);
1487         }
1488     }
1489 
1490     // Notify the listeners to update their references.
1491     ReorderNotifier<sc::RefRowReorderHint, sc::ColRowReorderMapType, SCROW> aFunc(aRowMap, nTab, nCol1, nCol2);
1492     std::for_each(aListeners.begin(), aListeners.end(), aFunc);
1493 
1494     // Re-group formulas in affected columns.
1495     for (const auto& [rTab, rCols] : rGroupTabs)
1496     {
1497         for (const auto& rEntry : rCols)
1498             rDocument.RegroupFormulaCells(rTab, rEntry.first);
1499     }
1500 
1501     // Re-start area listeners on the reordered rows.
1502     for (const auto& rAreaListener : aAreaListeners)
1503     {
1504         ScRange aNewRange = rAreaListener.maArea;
1505         sc::ColRowReorderMapType::const_iterator itRow = aRowMap.find( aNewRange.aStart.Row());
1506         if (itRow != aRowMap.end())
1507         {
1508             aNewRange.aStart.SetRow( itRow->second);
1509             aNewRange.aEnd.SetRow( itRow->second);
1510         }
1511         rDocument.StartListeningArea(aNewRange, rAreaListener.mbGroupListening, rAreaListener.mpListener);
1512     }
1513 
1514     // Re-group columns in the sorted range too.
1515     for (SCCOL i = nCol1; i <= nCol2; ++i)
1516         aCol[i].RegroupFormulaCells();
1517 
1518     {
1519         // Re-start area listeners on the old group listener ranges.
1520         ListenerStartAction aAction(rDocument);
1521         aGrpListenerRanges.executeColumnAction(rDocument, aAction);
1522     }
1523 }
1524 
1525 short ScTable::CompareCell(
1526     sal_uInt16 nSort,
1527     ScRefCellValue& rCell1, SCCOL nCell1Col, SCROW nCell1Row,
1528     ScRefCellValue& rCell2, SCCOL nCell2Col, SCROW nCell2Row ) const
1529 {
1530     short nRes = 0;
1531 
1532     CellType eType1 = rCell1.getType(), eType2 = rCell2.getType();
1533 
1534     if (!rCell1.isEmpty())
1535     {
1536         if (!rCell2.isEmpty())
1537         {
1538             bool bErr1 = false;
1539             bool bStr1 = ( eType1 != CELLTYPE_VALUE );
1540             if (eType1 == CELLTYPE_FORMULA)
1541             {
1542                 if (rCell1.getFormula()->GetErrCode() != FormulaError::NONE)
1543                 {
1544                     bErr1 = true;
1545                     bStr1 = false;
1546                 }
1547                 else if (rCell1.getFormula()->IsValue())
1548                 {
1549                     bStr1 = false;
1550                 }
1551             }
1552 
1553             bool bErr2 = false;
1554             bool bStr2 = ( eType2 != CELLTYPE_VALUE );
1555             if (eType2 == CELLTYPE_FORMULA)
1556             {
1557                 if (rCell2.getFormula()->GetErrCode() != FormulaError::NONE)
1558                 {
1559                     bErr2 = true;
1560                     bStr2 = false;
1561                 }
1562                 else if (rCell2.getFormula()->IsValue())
1563                 {
1564                     bStr2 = false;
1565                 }
1566             }
1567 
1568             if ( bStr1 && bStr2 )           // only compare strings as strings!
1569             {
1570                 OUString aStr1;
1571                 OUString aStr2;
1572                 if (eType1 == CELLTYPE_STRING)
1573                     aStr1 = rCell1.getSharedString()->getString();
1574                 else
1575                     aStr1 = GetString(nCell1Col, nCell1Row);
1576                 if (eType2 == CELLTYPE_STRING)
1577                     aStr2 = rCell2.getSharedString()->getString();
1578                 else
1579                     aStr2 = GetString(nCell2Col, nCell2Row);
1580 
1581                 bool bUserDef     = aSortParam.bUserDef;        // custom sort order
1582                 bool bNaturalSort = aSortParam.bNaturalSort;    // natural sort
1583                 bool bCaseSens    = aSortParam.bCaseSens;       // case sensitivity
1584 
1585                 ScUserList* pList = ScGlobal::GetUserList();
1586                 if (bUserDef && pList && pList->size() > aSortParam.nUserIndex )
1587                 {
1588                     const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
1589 
1590                     if ( bNaturalSort )
1591                         nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator );
1592                     else
1593                     {
1594                         if ( bCaseSens )
1595                             nRes = sal::static_int_cast<short>( rData.Compare(aStr1, aStr2) );
1596                         else
1597                             nRes = sal::static_int_cast<short>( rData.ICompare(aStr1, aStr2) );
1598                     }
1599 
1600                 }
1601                 if (!bUserDef)
1602                 {
1603                     if ( bNaturalSort )
1604                         nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator );
1605                     else
1606                         nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) );
1607                 }
1608             }
1609             else if ( bStr1 )               // String <-> Number or Error
1610             {
1611                 if (bErr2)
1612                     nRes = -1;              // String in front of Error
1613                 else
1614                     nRes = 1;               // Number in front of String
1615             }
1616             else if ( bStr2 )               // Number or Error <-> String
1617             {
1618                 if (bErr1)
1619                     nRes = 1;               // String in front of Error
1620                 else
1621                     nRes = -1;              // Number in front of String
1622             }
1623             else if (bErr1 && bErr2)
1624             {
1625                 // nothing, two Errors are equal
1626             }
1627             else if (bErr1)                 // Error <-> Number
1628             {
1629                 nRes = 1;                   // Number in front of Error
1630             }
1631             else if (bErr2)                 // Number <-> Error
1632             {
1633                 nRes = -1;                  // Number in front of Error
1634             }
1635             else                            // Mixed numbers
1636             {
1637                 double nVal1 = rCell1.getValue();
1638                 double nVal2 = rCell2.getValue();
1639                 if (nVal1 < nVal2)
1640                     nRes = -1;
1641                 else if (nVal1 > nVal2)
1642                     nRes = 1;
1643             }
1644             if ( !aSortParam.maKeyState[nSort].bAscending )
1645                 nRes = -nRes;
1646         }
1647         else
1648             nRes = -1;
1649     }
1650     else
1651     {
1652         if (!rCell2.isEmpty())
1653             nRes = 1;
1654         else
1655             nRes = 0;                   // both empty
1656     }
1657     return nRes;
1658 }
1659 
1660 short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) const
1661 {
1662     short nRes;
1663     sal_uInt16 nSort = 0;
1664     do
1665     {
1666         ScSortInfo& rInfo1 = pArray->Get( nSort, nIndex1 );
1667         ScSortInfo& rInfo2 = pArray->Get( nSort, nIndex2 );
1668         if ( aSortParam.bByRow )
1669             nRes = CompareCell( nSort,
1670                 rInfo1.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo1.nOrg,
1671                 rInfo2.maCell, static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField), rInfo2.nOrg );
1672         else
1673             nRes = CompareCell( nSort,
1674                 rInfo1.maCell, static_cast<SCCOL>(rInfo1.nOrg), aSortParam.maKeyState[nSort].nField,
1675                 rInfo2.maCell, static_cast<SCCOL>(rInfo2.nOrg), aSortParam.maKeyState[nSort].nField );
1676     } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() );
1677     if( nRes == 0 )
1678     {
1679         ScSortInfo& rInfo1 = pArray->Get( 0, nIndex1 );
1680         ScSortInfo& rInfo2 = pArray->Get( 0, nIndex2 );
1681         if( rInfo1.nOrg < rInfo2.nOrg )
1682             nRes = -1;
1683         else if( rInfo1.nOrg > rInfo2.nOrg )
1684             nRes = 1;
1685     }
1686     return nRes;
1687 }
1688 
1689 void ScTable::QuickSort( ScSortInfoArray* pArray, SCCOLROW nLo, SCCOLROW nHi )
1690 {
1691     if ((nHi - nLo) == 1)
1692     {
1693         if (Compare(pArray, nLo, nHi) > 0)
1694             pArray->Swap( nLo, nHi );
1695     }
1696     else
1697     {
1698         SCCOLROW ni = nLo;
1699         SCCOLROW nj = nHi;
1700         do
1701         {
1702             while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0)
1703                 ni++;
1704             while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0)
1705                 nj--;
1706             if (ni <= nj)
1707             {
1708                 if (ni != nj)
1709                     pArray->Swap( ni, nj );
1710                 ni++;
1711                 nj--;
1712             }
1713         } while (ni < nj);
1714         if ((nj - nLo) < (nHi - ni))
1715         {
1716             if (nLo < nj)
1717                 QuickSort(pArray, nLo, nj);
1718             if (ni < nHi)
1719                 QuickSort(pArray, ni, nHi);
1720         }
1721         else
1722         {
1723             if (ni < nHi)
1724                 QuickSort(pArray, ni, nHi);
1725             if (nLo < nj)
1726                 QuickSort(pArray, nLo, nj);
1727         }
1728     }
1729 }
1730 
1731 short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) const
1732 {
1733     short nRes;
1734     sal_uInt16 nSort = 0;
1735     const sal_uInt32 nMaxSorts = aSortParam.GetSortKeyCount();
1736     if (aSortParam.bByRow)
1737     {
1738         do
1739         {
1740             SCCOL nCol = static_cast<SCCOL>(aSortParam.maKeyState[nSort].nField);
1741             nRes = 0;
1742             if(nCol < GetAllocatedColumnsCount())
1743             {
1744                 ScRefCellValue aCell1 = aCol[nCol].GetCellValue(nIndex1);
1745                 ScRefCellValue aCell2 = aCol[nCol].GetCellValue(nIndex2);
1746                 nRes = CompareCell(nSort, aCell1, nCol, nIndex1, aCell2, nCol, nIndex2);
1747             }
1748         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1749     }
1750     else
1751     {
1752         do
1753         {
1754             SCROW nRow = aSortParam.maKeyState[nSort].nField;
1755             ScRefCellValue aCell1;
1756             ScRefCellValue aCell2;
1757             if(nIndex1 < GetAllocatedColumnsCount())
1758                 aCell1 = aCol[nIndex1].GetCellValue(nRow);
1759             if(nIndex2 < GetAllocatedColumnsCount())
1760                 aCell2 = aCol[nIndex2].GetCellValue(nRow);
1761             nRes = CompareCell( nSort, aCell1, static_cast<SCCOL>(nIndex1),
1762                     nRow, aCell2, static_cast<SCCOL>(nIndex2), nRow );
1763         } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.maKeyState[nSort].bDoSort );
1764     }
1765     return nRes;
1766 }
1767 
1768 bool ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) const   // over aSortParam
1769 {
1770     for (SCCOLROW i=nStart; i<nEnd; i++)
1771     {
1772         if (Compare( i, i+1 ) > 0)
1773             return false;
1774     }
1775     return true;
1776 }
1777 
1778 void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 )
1779 {
1780     SCROW nRow;
1781     int nMax = nRow2 - nRow1;
1782     for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4)
1783     {
1784         nRow = comphelper::rng::uniform_int_distribution(0, nMax-1);
1785         pArray->Swap(i, nRow1 + nRow);
1786     }
1787 }
1788 
1789 void ScTable::Sort(
1790     const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
1791     ScProgress* pProgress, sc::ReorderParam* pUndo )
1792 {
1793     sc::DelayDeletingBroadcasters delayDeletingBroadcasters(GetDoc());
1794     InitSortCollator( rSortParam );
1795     bGlobalKeepQuery = bKeepQuery;
1796 
1797     if (pUndo)
1798     {
1799         // Copy over the basic sort parameters.
1800         pUndo->maDataAreaExtras = rSortParam.aDataAreaExtras;
1801         pUndo->mbByRow = rSortParam.bByRow;
1802         pUndo->mbHiddenFiltered = bKeepQuery;
1803         pUndo->mbUpdateRefs = bUpdateRefs;
1804         pUndo->mbHasHeaders = rSortParam.bHasHeader;
1805     }
1806 
1807     // It is assumed that the data area has already been trimmed as necessary.
1808 
1809     aSortParam = rSortParam;    // must be assigned before calling IsSorted()
1810     if (rSortParam.bByRow)
1811     {
1812         const SCROW nLastRow = rSortParam.nRow2;
1813         const SCROW nRow1 = (rSortParam.bHasHeader ? rSortParam.nRow1 + 1 : rSortParam.nRow1);
1814         if (nRow1 < nLastRow && !IsSorted(nRow1, nLastRow))
1815         {
1816             if(pProgress)
1817                 pProgress->SetState( 0, nLastRow-nRow1 );
1818 
1819             std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1820                         aSortParam, nRow1, nLastRow, bKeepQuery, bUpdateRefs));
1821 
1822             if ( nLastRow - nRow1 > 255 )
1823                 DecoladeRow(pArray.get(), nRow1, nLastRow);
1824 
1825             QuickSort(pArray.get(), nRow1, nLastRow);
1826             if (pArray->IsUpdateRefs())
1827                 SortReorderByRowRefUpdate(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress);
1828             else
1829             {
1830                 SortReorderByRow(pArray.get(), aSortParam.nCol1, aSortParam.nCol2, pProgress, false);
1831                 if (rSortParam.aDataAreaExtras.anyExtrasWanted())
1832                     SortReorderAreaExtrasByRow( pArray.get(), aSortParam.nCol1, aSortParam.nCol2,
1833                             rSortParam.aDataAreaExtras, pProgress);
1834             }
1835 
1836             if (pUndo)
1837             {
1838                 // Stored is the first data row without header row.
1839                 pUndo->maSortRange = ScRange(rSortParam.nCol1, nRow1, nTab, rSortParam.nCol2, nLastRow, nTab);
1840                 pUndo->maDataAreaExtras.mnStartRow = nRow1;
1841                 pUndo->maOrderIndices = pArray->GetOrderIndices();
1842             }
1843         }
1844     }
1845     else
1846     {
1847         const SCCOL nLastCol = rSortParam.nCol2;
1848         const SCCOL nCol1 = (rSortParam.bHasHeader ? rSortParam.nCol1 + 1 : rSortParam.nCol1);
1849         if (nCol1 < nLastCol && !IsSorted(nCol1, nLastCol))
1850         {
1851             if(pProgress)
1852                 pProgress->SetState( 0, nLastCol-nCol1 );
1853 
1854             std::unique_ptr<ScSortInfoArray> pArray( CreateSortInfoArray(
1855                         aSortParam, nCol1, nLastCol, bKeepQuery, bUpdateRefs));
1856 
1857             QuickSort(pArray.get(), nCol1, nLastCol);
1858             SortReorderByColumn(pArray.get(), rSortParam.nRow1, rSortParam.nRow2,
1859                     rSortParam.aDataAreaExtras.mbCellFormats, pProgress);
1860             if (rSortParam.aDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1861                 SortReorderAreaExtrasByColumn( pArray.get(),
1862                         rSortParam.nRow1, rSortParam.nRow2, rSortParam.aDataAreaExtras, pProgress);
1863 
1864             if (pUndo)
1865             {
1866                 // Stored is the first data column without header column.
1867                 pUndo->maSortRange = ScRange(nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab);
1868                 pUndo->maDataAreaExtras.mnStartCol = nCol1;
1869                 pUndo->maOrderIndices = pArray->GetOrderIndices();
1870             }
1871         }
1872     }
1873     DestroySortCollator();
1874 }
1875 
1876 void ScTable::Reorder( const sc::ReorderParam& rParam )
1877 {
1878     if (rParam.maOrderIndices.empty())
1879         return;
1880 
1881     std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(rParam));
1882     if (!pArray)
1883         return;
1884 
1885     if (rParam.mbByRow)
1886     {
1887         // Re-play sorting from the known sort indices.
1888         pArray->ReorderByRow(rParam.maOrderIndices);
1889         if (pArray->IsUpdateRefs())
1890             SortReorderByRowRefUpdate(
1891                 pArray.get(), rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr);
1892         else
1893         {
1894             SortReorderByRow( pArray.get(),
1895                     rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(), nullptr, false);
1896             if (rParam.maDataAreaExtras.anyExtrasWanted())
1897                 SortReorderAreaExtrasByRow( pArray.get(),
1898                         rParam.maSortRange.aStart.Col(), rParam.maSortRange.aEnd.Col(),
1899                         rParam.maDataAreaExtras, nullptr);
1900         }
1901     }
1902     else
1903     {
1904         // Ordering by column is much simpler.  Just set the order indices and we are done.
1905         pArray->SetOrderIndices(std::vector(rParam.maOrderIndices));
1906         SortReorderByColumn(
1907             pArray.get(), rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1908             rParam.maDataAreaExtras.mbCellFormats, nullptr);
1909         if (rParam.maDataAreaExtras.anyExtrasWanted() && !pArray->IsUpdateRefs())
1910             SortReorderAreaExtrasByColumn( pArray.get(),
1911                     rParam.maSortRange.aStart.Row(), rParam.maSortRange.aEnd.Row(),
1912                     rParam.maDataAreaExtras, nullptr);
1913     }
1914 }
1915 
1916 namespace {
1917 
1918 class SubTotalRowFinder
1919 {
1920     const ScTable& mrTab;
1921     const ScSubTotalParam& mrParam;
1922 
1923 public:
1924     SubTotalRowFinder(const ScTable& rTab, const ScSubTotalParam& rParam) :
1925         mrTab(rTab), mrParam(rParam) {}
1926 
1927     bool operator() (size_t nRow, const ScFormulaCell* pCell)
1928     {
1929         if (!pCell->IsSubTotal())
1930             return false;
1931 
1932         SCCOL nStartCol = mrParam.nCol1;
1933         SCCOL nEndCol = mrParam.nCol2;
1934 
1935         for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(0, nStartCol - 1))
1936         {
1937             if (mrTab.HasData(nCol, nRow))
1938                 return true;
1939         }
1940         for (SCCOL nCol : mrTab.GetAllocatedColumnsRange(nEndCol + 1, mrTab.GetDoc().MaxCol()))
1941         {
1942             if (mrTab.HasData(nCol, nRow))
1943                 return true;
1944         }
1945         return false;
1946     }
1947 };
1948 
1949 }
1950 
1951 bool ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam )
1952 {
1953     SCCOL nStartCol = rParam.nCol1;
1954     SCROW nStartRow = rParam.nRow1 + 1;     // Header
1955     SCCOL nEndCol   = ClampToAllocatedColumns(rParam.nCol2);
1956     SCROW nEndRow    = rParam.nRow2;
1957 
1958     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1959     {
1960         const sc::CellStoreType& rCells = aCol[nCol].maCells;
1961         SubTotalRowFinder aFunc(*this, rParam);
1962         std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
1963             sc::FindFormula(rCells, nStartRow, nEndRow, aFunc);
1964         if (aPos.first != rCells.end())
1965             return true;
1966     }
1967     return false;
1968 }
1969 
1970 namespace {
1971 
1972 struct RemoveSubTotalsHandler
1973 {
1974     std::set<SCROW> aRemoved;
1975 
1976     void operator() (size_t nRow, const ScFormulaCell* p)
1977     {
1978         if (p->IsSubTotal())
1979             aRemoved.insert(nRow);
1980     }
1981 };
1982 
1983 }
1984 
1985 void ScTable::RemoveSubTotals( ScSubTotalParam& rParam )
1986 {
1987     SCCOL nStartCol = rParam.nCol1;
1988     SCROW nStartRow = rParam.nRow1 + 1;     // Header
1989     SCCOL nEndCol   = ClampToAllocatedColumns(rParam.nCol2);
1990     SCROW nEndRow    = rParam.nRow2;        // will change
1991 
1992     RemoveSubTotalsHandler aFunc;
1993     for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
1994     {
1995         const sc::CellStoreType& rCells = aCol[nCol].maCells;
1996         sc::ParseFormula(rCells.begin(), rCells, nStartRow, nEndRow, aFunc);
1997     }
1998 
1999     auto& aRows = aFunc.aRemoved;
2000 
2001     std::for_each(aRows.rbegin(), aRows.rend(), [this](const SCROW nRow) {
2002             RemoveRowBreak(nRow+1, false, true);
2003             rDocument.DeleteRow(0, nTab, rDocument.MaxCol(), nTab, nRow, 1);
2004         });
2005 
2006     rParam.nRow2 -= aRows.size();
2007 }
2008 
2009 //  Delete hard number formats (for result formulas)
2010 
2011 static void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow )
2012 {
2013     const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow );
2014     if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, false )
2015             == SfxItemState::SET )
2016     {
2017         auto pNewPattern = std::make_unique<ScPatternAttr>( *pPattern );
2018         SfxItemSet& rSet = pNewPattern->GetItemSet();
2019         rSet.ClearItem( ATTR_VALUE_FORMAT );
2020         rSet.ClearItem( ATTR_LANGUAGE_FORMAT );
2021         pTab->SetPattern( nCol, nRow, std::move(pNewPattern) );
2022     }
2023 }
2024 
2025 namespace {
2026 
2027 struct RowEntry
2028 {
2029     sal_uInt16  nGroupNo;
2030     SCROW   nSubStartRow;
2031     SCROW   nDestRow;
2032     SCROW   nFuncStart;
2033     SCROW   nFuncEnd;
2034 };
2035 
2036 }
2037 
2038 static TranslateId lcl_GetSubTotalStrId(int id)
2039 {
2040     switch ( id )
2041     {
2042         case SUBTOTAL_FUNC_AVE:     return STR_FUN_TEXT_AVG;
2043         case SUBTOTAL_FUNC_CNT:
2044         case SUBTOTAL_FUNC_CNT2:    return STR_FUN_TEXT_COUNT;
2045         case SUBTOTAL_FUNC_MAX:     return STR_FUN_TEXT_MAX;
2046         case SUBTOTAL_FUNC_MIN:     return STR_FUN_TEXT_MIN;
2047         case SUBTOTAL_FUNC_PROD:    return STR_FUN_TEXT_PRODUCT;
2048         case SUBTOTAL_FUNC_STD:
2049         case SUBTOTAL_FUNC_STDP:    return STR_FUN_TEXT_STDDEV;
2050         case SUBTOTAL_FUNC_SUM:     return STR_FUN_TEXT_SUM;
2051         case SUBTOTAL_FUNC_VAR:
2052         case SUBTOTAL_FUNC_VARP:    return STR_FUN_TEXT_VAR;
2053         default:
2054         {
2055              return STR_EMPTYDATA;
2056             // added to avoid warnings
2057         }
2058     }
2059 }
2060 
2061 //      new intermediate results
2062 //      rParam.nRow2 is changed!
2063 
2064 bool ScTable::DoSubTotals( ScSubTotalParam& rParam )
2065 {
2066     SCCOL nStartCol = rParam.nCol1;
2067     SCROW nStartRow = rParam.nRow1 + 1;     // Header
2068     SCCOL nEndCol   = rParam.nCol2;
2069     SCROW nEndRow    = rParam.nRow2;        // will change
2070     sal_uInt16 i;
2071 
2072     //  Remove empty rows at the end
2073     //  so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#)
2074     //  If sorted, all empty rows are at the end.
2075     SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM );
2076     nEndRow -= nEmpty;
2077 
2078     sal_uInt16 nLevelCount = 0;             // Number of levels
2079     bool bDoThis = true;
2080     for (i=0; i<MAXSUBTOTAL && bDoThis; i++)
2081         if (rParam.bGroupActive[i])
2082             nLevelCount = i+1;
2083         else
2084             bDoThis = false;
2085 
2086     if (nLevelCount==0)                 // do nothing
2087         return true;
2088 
2089     SCCOL*          nGroupCol = rParam.nField;  // columns which will be used when grouping
2090 
2091     //  With (blank) as a separate category, subtotal rows from
2092     //  the other columns must always be tested
2093     //  (previously only when a column occurred more than once)
2094     bool bTestPrevSub = ( nLevelCount > 1 );
2095 
2096     OUString  aSubString;
2097 
2098     bool bIgnoreCase = !rParam.bCaseSens;
2099 
2100     OUString aCompString[MAXSUBTOTAL];
2101 
2102                                 //TODO: sort?
2103 
2104     ScStyleSheet* pStyle = static_cast<ScStyleSheet*>(rDocument.GetStyleSheetPool()->Find(
2105                                 ScResId(STR_STYLENAME_RESULT), SfxStyleFamily::Para ));
2106 
2107     bool bSpaceLeft = true;                                         // Success when inserting?
2108 
2109     // For performance reasons collect formula entries so their
2110     // references don't have to be tested for updates each time a new row is
2111     // inserted
2112     RowEntry aRowEntry;
2113     ::std::vector< RowEntry > aRowVector;
2114 
2115     for (sal_uInt16 nLevel=0; nLevel<nLevelCount && bSpaceLeft; nLevel++)
2116     {
2117         aRowEntry.nGroupNo = nLevelCount - nLevel - 1;
2118 
2119         // how many results per level
2120         SCCOL nResCount         = rParam.nSubTotals[aRowEntry.nGroupNo];
2121         // result functions
2122         ScSubTotalFunc* pResFunc = rParam.pFunctions[aRowEntry.nGroupNo].get();
2123 
2124         if (nResCount > 0)                                      // otherwise only sort
2125         {
2126             for (i=0; i<=aRowEntry.nGroupNo; i++)
2127             {
2128                 aSubString = GetString( nGroupCol[i], nStartRow );
2129                 if ( bIgnoreCase )
2130                     aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2131                 else
2132                     aCompString[i] = aSubString;
2133             }                                                   // aSubString stays on the last
2134 
2135             bool bBlockVis = false;             // group visible?
2136             aRowEntry.nSubStartRow = nStartRow;
2137             for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++)
2138             {
2139                 bool bChanged;
2140                 if (nRow>nEndRow)
2141                     bChanged = true;
2142                 else
2143                 {
2144                     bChanged = false;
2145                     OUString aString;
2146                     for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++)
2147                     {
2148                         aString = GetString( nGroupCol[i], nRow );
2149                         if (bIgnoreCase)
2150                             aString = ScGlobal::getCharClass().uppercase(aString);
2151                         //  when sorting, blanks are separate group
2152                         //  otherwise blank cells are allowed below
2153                         bChanged = ( ( !aString.isEmpty() || rParam.bDoSort ) &&
2154                                         aString != aCompString[i] );
2155                     }
2156                     if ( bChanged && bTestPrevSub )
2157                     {
2158                         // No group change on rows that will contain subtotal formulas
2159                         bChanged = std::none_of(aRowVector.begin(), aRowVector.end(),
2160                             [&nRow](const RowEntry& rEntry) { return rEntry.nDestRow == nRow; });
2161                     }
2162                 }
2163                 if ( bChanged )
2164                 {
2165                     aRowEntry.nDestRow   = nRow;
2166                     aRowEntry.nFuncStart = aRowEntry.nSubStartRow;
2167                     aRowEntry.nFuncEnd   = nRow-1;
2168 
2169                     bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab,
2170                             aRowEntry.nDestRow, 1 );
2171                     DBShowRow( aRowEntry.nDestRow, bBlockVis );
2172                     if ( rParam.bPagebreak && nRow < rDocument.MaxRow() &&
2173                             aRowEntry.nSubStartRow != nStartRow && nLevel == 0)
2174                         SetRowBreak(aRowEntry.nSubStartRow, false, true);
2175 
2176                     if (bSpaceLeft)
2177                     {
2178                         for ( auto& rRowEntry : aRowVector)
2179                         {
2180                             if ( aRowEntry.nDestRow <= rRowEntry.nSubStartRow )
2181                                 ++rRowEntry.nSubStartRow;
2182                             if ( aRowEntry.nDestRow <= rRowEntry.nDestRow )
2183                                 ++rRowEntry.nDestRow;
2184                             if ( aRowEntry.nDestRow <= rRowEntry.nFuncStart )
2185                                 ++rRowEntry.nFuncStart;
2186                             if ( aRowEntry.nDestRow <= rRowEntry.nFuncEnd )
2187                                 ++rRowEntry.nFuncEnd;
2188                         }
2189                         // collect formula positions
2190                         aRowVector.push_back( aRowEntry );
2191 
2192                         OUString aOutString = aSubString;
2193                         if (aOutString.isEmpty())
2194                             aOutString = ScResId( STR_EMPTYDATA );
2195                         aOutString += " ";
2196                         TranslateId pStrId = STR_TABLE_ERGEBNIS;
2197                         if ( nResCount == 1 )
2198                             pStrId = lcl_GetSubTotalStrId(pResFunc[0]);
2199                         aOutString += ScResId(pStrId);
2200                         SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString );
2201                         ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle );
2202 
2203                         ++nRow;
2204                         ++nEndRow;
2205                         aRowEntry.nSubStartRow = nRow;
2206                         for (i=0; i<=aRowEntry.nGroupNo; i++)
2207                         {
2208                             aSubString = GetString( nGroupCol[i], nRow );
2209                             if ( bIgnoreCase )
2210                                 aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString );
2211                             else
2212                                 aCompString[i] = aSubString;
2213                         }
2214                     }
2215                 }
2216                 bBlockVis = !RowFiltered(nRow);
2217             }
2218         }
2219     }
2220 
2221     if (!aRowVector.empty())
2222     {
2223         // generate global total
2224         SCROW nGlobalStartRow = aRowVector[0].nSubStartRow;
2225         SCROW nGlobalStartFunc = aRowVector[0].nFuncStart;
2226         SCROW nGlobalEndRow = 0;
2227         SCROW nGlobalEndFunc = 0;
2228         for (const auto& rRowEntry : aRowVector)
2229         {
2230             nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow;
2231             nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow;
2232         }
2233 
2234         for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++)
2235         {
2236             const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1;
2237             const ScSubTotalFunc* pResFunc = rParam.pFunctions[nGroupNo].get();
2238             if (!pResFunc)
2239             {
2240                 // No subtotal function given for this group => no formula or
2241                 // label and do not insert a row.
2242                 continue;
2243             }
2244 
2245             // increment end row
2246             nGlobalEndRow++;
2247 
2248             // add row entry for formula
2249             aRowEntry.nGroupNo = nGroupNo;
2250             aRowEntry.nSubStartRow = nGlobalStartRow;
2251             aRowEntry.nFuncStart = nGlobalStartFunc;
2252             aRowEntry.nDestRow = nGlobalEndRow;
2253             aRowEntry.nFuncEnd = nGlobalEndFunc;
2254 
2255             // increment row
2256             nGlobalEndFunc++;
2257 
2258             bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1);
2259 
2260             if (bSpaceLeft)
2261             {
2262                 aRowVector.push_back(aRowEntry);
2263                 nEndRow++;
2264                 DBShowRow(aRowEntry.nDestRow, true);
2265 
2266                 // insert label
2267                 OUString label = ScResId(STR_TABLE_GRAND) + " " + ScResId(lcl_GetSubTotalStrId(pResFunc[0]));
2268                 SetString(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, label);
2269                 ApplyStyle(nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, pStyle);
2270             }
2271         }
2272     }
2273 
2274     // now insert the formulas
2275     ScComplexRefData aRef;
2276     aRef.InitFlags();
2277     aRef.Ref1.SetAbsTab(nTab);
2278     aRef.Ref2.SetAbsTab(nTab);
2279     for (const auto& rRowEntry : aRowVector)
2280     {
2281         SCCOL nResCount         = rParam.nSubTotals[rRowEntry.nGroupNo];
2282         SCCOL* nResCols         = rParam.pSubTotals[rRowEntry.nGroupNo].get();
2283         ScSubTotalFunc* pResFunc = rParam.pFunctions[rRowEntry.nGroupNo].get();
2284         for ( SCCOL nResult=0; nResult < nResCount; ++nResult )
2285         {
2286             aRef.Ref1.SetAbsCol(nResCols[nResult]);
2287             aRef.Ref1.SetAbsRow(rRowEntry.nFuncStart);
2288             aRef.Ref2.SetAbsCol(nResCols[nResult]);
2289             aRef.Ref2.SetAbsRow(rRowEntry.nFuncEnd);
2290 
2291             ScTokenArray aArr(rDocument);
2292             aArr.AddOpCode( ocSubTotal );
2293             aArr.AddOpCode( ocOpen );
2294             aArr.AddDouble( static_cast<double>(pResFunc[nResult]) );
2295             aArr.AddOpCode( ocSep );
2296             aArr.AddDoubleReference( aRef );
2297             aArr.AddOpCode( ocClose );
2298             aArr.AddOpCode( ocStop );
2299             ScFormulaCell* pCell = new ScFormulaCell(
2300                 rDocument, ScAddress(nResCols[nResult], rRowEntry.nDestRow, nTab), aArr);
2301             if ( rParam.bIncludePattern )
2302                 pCell->SetNeedNumberFormat(true);
2303 
2304             SetFormulaCell(nResCols[nResult], rRowEntry.nDestRow, pCell);
2305             if ( nResCols[nResult] != nGroupCol[rRowEntry.nGroupNo] )
2306             {
2307                 ApplyStyle( nResCols[nResult], rRowEntry.nDestRow, pStyle );
2308 
2309                 lcl_RemoveNumberFormat( this, nResCols[nResult], rRowEntry.nDestRow );
2310             }
2311         }
2312 
2313     }
2314 
2315     //TODO: according to setting, shift intermediate-sum rows up?
2316 
2317     //TODO: create Outlines directly?
2318 
2319     if (bSpaceLeft)
2320         DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
2321 
2322     rParam.nRow2 = nEndRow;                 // new end
2323     return bSpaceLeft;
2324 }
2325 
2326 void ScTable::TopTenQuery( ScQueryParam& rParam )
2327 {
2328     bool bSortCollatorInitialized = false;
2329     SCSIZE nEntryCount = rParam.GetEntryCount();
2330     SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1);
2331     SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1);
2332     for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ )
2333     {
2334         ScQueryEntry& rEntry = rParam.GetEntry(i);
2335         ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2336 
2337         for (ScQueryEntry::Item& rItem : rItems)
2338         {
2339             switch (rEntry.eOp)
2340             {
2341                 case SC_TOPVAL:
2342                 case SC_BOTVAL:
2343                 case SC_TOPPERC:
2344                 case SC_BOTPERC:
2345                 {
2346                     ScSortParam aLocalSortParam(rParam, static_cast<SCCOL>(rEntry.nField));
2347                     aSortParam = aLocalSortParam;       // used in CreateSortInfoArray, Compare
2348                     if (!bSortCollatorInitialized)
2349                     {
2350                         bSortCollatorInitialized = true;
2351                         InitSortCollator(aLocalSortParam);
2352                     }
2353                     std::unique_ptr<ScSortInfoArray> pArray(CreateSortInfoArray(aSortParam, nRow1, rParam.nRow2, bGlobalKeepQuery, false));
2354                     DecoladeRow(pArray.get(), nRow1, rParam.nRow2);
2355                     QuickSort(pArray.get(), nRow1, rParam.nRow2);
2356                     std::unique_ptr<ScSortInfo[]> const& ppInfo = pArray->GetFirstArray();
2357                     SCSIZE nValidCount = nCount;
2358                     // Don't count note or blank cells, they are sorted to the end
2359                     while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.isEmpty())
2360                         nValidCount--;
2361                     // Don't count Strings, they are between Value and blank
2362                     while (nValidCount > 0 && ppInfo[nValidCount - 1].maCell.hasString())
2363                         nValidCount--;
2364                     if (nValidCount > 0)
2365                     {
2366                         if (rItem.meType == ScQueryEntry::ByString)
2367                         {   // by string ain't going to work
2368                             rItem.meType = ScQueryEntry::ByValue;
2369                             rItem.mfVal = 10;   // 10 and 10% respectively
2370                         }
2371                         SCSIZE nVal = (rItem.mfVal >= 1 ? static_cast<SCSIZE>(rItem.mfVal) : 1);
2372                         SCSIZE nOffset = 0;
2373                         switch (rEntry.eOp)
2374                         {
2375                         case SC_TOPVAL:
2376                         {
2377                             rEntry.eOp = SC_GREATER_EQUAL;
2378                             if (nVal > nValidCount)
2379                                 nVal = nValidCount;
2380                             nOffset = nValidCount - nVal;   // 1 <= nVal <= nValidCount
2381                         }
2382                         break;
2383                         case SC_BOTVAL:
2384                         {
2385                             rEntry.eOp = SC_LESS_EQUAL;
2386                             if (nVal > nValidCount)
2387                                 nVal = nValidCount;
2388                             nOffset = nVal - 1;     // 1 <= nVal <= nValidCount
2389                         }
2390                         break;
2391                         case SC_TOPPERC:
2392                         {
2393                             rEntry.eOp = SC_GREATER_EQUAL;
2394                             if (nVal > 100)
2395                                 nVal = 100;
2396                             nOffset = nValidCount - (nValidCount * nVal / 100);
2397                             if (nOffset >= nValidCount)
2398                                 nOffset = nValidCount - 1;
2399                         }
2400                         break;
2401                         case SC_BOTPERC:
2402                         {
2403                             rEntry.eOp = SC_LESS_EQUAL;
2404                             if (nVal > 100)
2405                                 nVal = 100;
2406                             nOffset = (nValidCount * nVal / 100);
2407                             if (nOffset >= nValidCount)
2408                                 nOffset = nValidCount - 1;
2409                         }
2410                         break;
2411                         default:
2412                         {
2413                             // added to avoid warnings
2414                         }
2415                         }
2416                         ScRefCellValue aCell = ppInfo[nOffset].maCell;
2417                         if (aCell.hasNumeric())
2418                             rItem.mfVal = aCell.getValue();
2419                         else
2420                         {
2421                             OSL_FAIL("TopTenQuery: pCell no ValueData");
2422                             rEntry.eOp = SC_GREATER_EQUAL;
2423                             rItem.mfVal = 0;
2424                         }
2425                     }
2426                     else
2427                     {
2428                         rEntry.eOp = SC_GREATER_EQUAL;
2429                         rItem.meType = ScQueryEntry::ByValue;
2430                         rItem.mfVal = 0;
2431                     }
2432                 }
2433                 break;
2434                 default:
2435                 {
2436                     // added to avoid warnings
2437                 }
2438             }
2439         }
2440     }
2441     if ( bSortCollatorInitialized )
2442         DestroySortCollator();
2443 }
2444 
2445 namespace {
2446 
2447 bool CanOptimizeQueryStringToNumber( const SvNumberFormatter* pFormatter, sal_uInt32 nFormatIndex, bool& bDateFormat )
2448 {
2449     // tdf#105629: ScQueryEntry::ByValue queries are faster than ScQueryEntry::ByString.
2450     // The problem with this optimization is that the autofilter dialog apparently converts
2451     // the value to text and then converts that back to a number for filtering.
2452     // If that leads to any change of value (such as when time is rounded to seconds),
2453     // even matching values will be filtered out. Therefore query by value only for formats
2454     // where no such change should occur.
2455     if(const SvNumberformat* pEntry = pFormatter->GetEntry(nFormatIndex))
2456     {
2457         switch(pEntry->GetType())
2458         {
2459         case SvNumFormatType::NUMBER:
2460         case SvNumFormatType::FRACTION:
2461         case SvNumFormatType::SCIENTIFIC:
2462             return true;
2463         case SvNumFormatType::DATE:
2464         case SvNumFormatType::DATETIME:
2465             bDateFormat = true;
2466             break;
2467         default:
2468             break;
2469         }
2470     }
2471     return false;
2472 }
2473 
2474 class PrepareQueryItem
2475 {
2476     const ScDocument& mrDoc;
2477     const bool mbRoundForFilter;
2478 public:
2479     explicit PrepareQueryItem(const ScDocument& rDoc, bool bRoundForFilter) :
2480         mrDoc(rDoc), mbRoundForFilter(bRoundForFilter) {}
2481 
2482     void operator() (ScQueryEntry::Item& rItem)
2483     {
2484         rItem.mbRoundForFilter = mbRoundForFilter;
2485 
2486         if (rItem.meType != ScQueryEntry::ByString && rItem.meType != ScQueryEntry::ByDate)
2487             return;
2488 
2489         sal_uInt32 nIndex = 0;
2490         bool bNumber = mrDoc.GetFormatTable()->
2491             IsNumberFormat(rItem.maString.getString(), nIndex, rItem.mfVal);
2492 
2493         // Advanced Filter creates only ByString queries that need to be
2494         // converted to ByValue if appropriate. rItem.mfVal now holds the value
2495         // if bNumber==true.
2496 
2497         if (rItem.meType == ScQueryEntry::ByString)
2498         {
2499             bool bDateFormat = false;
2500             if (bNumber && CanOptimizeQueryStringToNumber( mrDoc.GetFormatTable(), nIndex, bDateFormat ))
2501                 rItem.meType = ScQueryEntry::ByValue;
2502             if (!bDateFormat)
2503                 return;
2504         }
2505 
2506         // Double-check if the query by date is really appropriate.
2507 
2508         if (bNumber && ((nIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0))
2509         {
2510             const SvNumberformat* pEntry = mrDoc.GetFormatTable()->GetEntry(nIndex);
2511             if (pEntry)
2512             {
2513                 SvNumFormatType nNumFmtType = pEntry->GetType();
2514                 if (!(nNumFmtType & SvNumFormatType::DATE) || (nNumFmtType & SvNumFormatType::TIME))
2515                     rItem.meType = ScQueryEntry::ByValue;    // not a date only
2516                 else
2517                     rItem.meType = ScQueryEntry::ByDate;    // date only
2518             }
2519             else
2520                 rItem.meType = ScQueryEntry::ByValue;    // what the ... not a date
2521         }
2522         else
2523             rItem.meType = ScQueryEntry::ByValue;    // not a date
2524     }
2525 };
2526 
2527 void lcl_PrepareQuery( const ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, bool bRoundForFilter )
2528 {
2529     bool bTopTen = false;
2530     SCSIZE nEntryCount = rParam.GetEntryCount();
2531 
2532     for ( SCSIZE i = 0; i < nEntryCount; ++i )
2533     {
2534         ScQueryEntry& rEntry = rParam.GetEntry(i);
2535         if (!rEntry.bDoQuery)
2536             continue;
2537 
2538         ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
2539         std::for_each(rItems.begin(), rItems.end(), PrepareQueryItem(*pDoc, bRoundForFilter));
2540 
2541         if ( !bTopTen )
2542         {
2543             switch ( rEntry.eOp )
2544             {
2545                 case SC_TOPVAL:
2546                 case SC_BOTVAL:
2547                 case SC_TOPPERC:
2548                 case SC_BOTPERC:
2549                 {
2550                     bTopTen = true;
2551                 }
2552                 break;
2553                 default:
2554                 {
2555                 }
2556             }
2557         }
2558     }
2559 
2560     if ( bTopTen )
2561     {
2562         pTab->TopTenQuery( rParam );
2563     }
2564 }
2565 
2566 }
2567 
2568 void ScTable::PrepareQuery( ScQueryParam& rQueryParam )
2569 {
2570     lcl_PrepareQuery(&rDocument, this, rQueryParam, false);
2571 }
2572 
2573 SCSIZE ScTable::Query(const ScQueryParam& rParamOrg, bool bKeepSub)
2574 {
2575     ScQueryParam    aParam( rParamOrg );
2576     typedef std::unordered_set<OUString> StrSetType;
2577     StrSetType aStrSet;
2578 
2579     bool    bStarted = false;
2580     bool    bOldResult = true;
2581     SCROW   nOldStart = 0;
2582     SCROW   nOldEnd = 0;
2583 
2584     SCSIZE nCount   = 0;
2585     SCROW nOutRow   = 0;
2586     SCROW nHeader   = aParam.bHasHeader ? 1 : 0;
2587 
2588     lcl_PrepareQuery(&rDocument, this, aParam, true);
2589 
2590     if (!aParam.bInplace)
2591     {
2592         nOutRow = aParam.nDestRow + nHeader;
2593         if (nHeader > 0)
2594             CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1,
2595                             aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
2596     }
2597 
2598     sc::TableColumnBlockPositionSet blockPos( GetDoc(), nTab ); // cache mdds access
2599     ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
2600 
2601     SCROW nRealRow2 = aParam.nRow2;
2602     for (SCROW j = aParam.nRow1 + nHeader; j <= nRealRow2; ++j)
2603     {
2604         bool bResult;                                   // Filter result
2605         bool bValid = queryEvaluator.ValidQuery(j, nullptr, &blockPos);
2606         if (!bValid && bKeepSub)                        // Keep subtotals
2607         {
2608             for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++)
2609             {
2610                 ScRefCellValue aCell = GetCellValue(nCol, j);
2611                 if (aCell.getType() != CELLTYPE_FORMULA)
2612                     continue;
2613 
2614                 if (!aCell.getFormula()->IsSubTotal())
2615                     continue;
2616 
2617                 if (RefVisible(aCell.getFormula()))
2618                     bValid = true;
2619             }
2620         }
2621         if (bValid)
2622         {
2623             if (aParam.bDuplicate)
2624                 bResult = true;
2625             else
2626             {
2627                 OUStringBuffer aStr;
2628                 for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++)
2629                 {
2630                     OUString aCellStr = GetString(k, j);
2631                     aStr.append(aCellStr + u"\x0001");
2632                 }
2633 
2634                 bResult = aStrSet.insert(aStr.makeStringAndClear()).second; // unique if inserted.
2635             }
2636         }
2637         else
2638             bResult = false;
2639 
2640         if (aParam.bInplace)
2641         {
2642             if (bResult == bOldResult && bStarted)
2643                 nOldEnd = j;
2644             else
2645             {
2646                 if (bStarted)
2647                     DBShowRows(nOldStart,nOldEnd, bOldResult);
2648                 nOldStart = nOldEnd = j;
2649                 bOldResult = bResult;
2650             }
2651             bStarted = true;
2652         }
2653         else
2654         {
2655             if (bResult)
2656             {
2657                 CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab );
2658                 if( nTab == aParam.nDestTab ) // copy to self, changes may invalidate caching position hints
2659                     blockPos.invalidate();
2660                 ++nOutRow;
2661             }
2662         }
2663         if (bResult)
2664             ++nCount;
2665     }
2666 
2667     if (aParam.bInplace && bStarted)
2668         DBShowRows(nOldStart,nOldEnd, bOldResult);
2669 
2670     if (aParam.bInplace)
2671         SetDrawPageSize();
2672 
2673     return nCount;
2674 }
2675 
2676 bool ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2677 {
2678     bool    bValid = true;
2679     std::unique_ptr<SCCOL[]> pFields(new SCCOL[nCol2-nCol1+1]);
2680     OUString  aCellStr;
2681     SCCOL   nCol = nCol1;
2682     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
2683     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
2684     SCROW   nDBRow1 = rQueryParam.nRow1;
2685     SCCOL   nDBCol2 = rQueryParam.nCol2;
2686     // First row must be column headers
2687     while (bValid && (nCol <= nCol2))
2688     {
2689         OUString aQueryStr = GetUpperCellString(nCol, nRow1);
2690         bool bFound = false;
2691         SCCOL i = rQueryParam.nCol1;
2692         while (!bFound && (i <= nDBCol2))
2693         {
2694             if ( nTab == nDBTab )
2695                 aCellStr = GetUpperCellString(i, nDBRow1);
2696             else
2697                 aCellStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
2698             bFound = (aCellStr == aQueryStr);
2699             if (!bFound) i++;
2700         }
2701         if (bFound)
2702             pFields[nCol - nCol1] = i;
2703         else
2704             bValid = false;
2705         nCol++;
2706     }
2707     if (bValid)
2708     {
2709         sal_uLong nVisible = 0;
2710         for ( nCol=nCol1; nCol<=ClampToAllocatedColumns(nCol2); nCol++ )
2711             nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 );
2712 
2713         if ( nVisible > SCSIZE_MAX / sizeof(void*) )
2714         {
2715             OSL_FAIL("too many filter criteria");
2716             nVisible = 0;
2717         }
2718 
2719         SCSIZE nNewEntries = nVisible;
2720         rQueryParam.Resize( nNewEntries );
2721 
2722         SCSIZE nIndex = 0;
2723         SCROW nRow = nRow1 + 1;
2724         svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2725         while (nRow <= nRow2)
2726         {
2727             nCol = nCol1;
2728             while (nCol <= nCol2)
2729             {
2730                 aCellStr = GetInputString( nCol, nRow );
2731                 if (!aCellStr.isEmpty())
2732                 {
2733                     if (nIndex < nNewEntries)
2734                     {
2735                         rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1];
2736                         rQueryParam.FillInExcelSyntax(rPool, aCellStr, nIndex, nullptr);
2737                         nIndex++;
2738                         if (nIndex < nNewEntries)
2739                             rQueryParam.GetEntry(nIndex).eConnect = SC_AND;
2740                     }
2741                     else
2742                         bValid = false;
2743                 }
2744                 nCol++;
2745             }
2746             nRow++;
2747             if (nIndex < nNewEntries)
2748                 rQueryParam.GetEntry(nIndex).eConnect = SC_OR;
2749         }
2750     }
2751     return bValid;
2752 }
2753 
2754 bool ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2755 {
2756     // A valid StarQuery must be at least 4 columns wide. To be precise it
2757     // should be exactly 4 columns ...
2758     // Additionally, if this wasn't checked, a formula pointing to a valid 1-3
2759     // column Excel style query range immediately left to itself would result
2760     // in a circular reference when the field name or operator or value (first
2761     // to third query range column) is obtained (#i58354#). Furthermore, if the
2762     // range wasn't sufficiently specified data changes wouldn't flag formula
2763     // cells for recalculation.
2764     if (nCol2 - nCol1 < 3)
2765         return false;
2766 
2767     bool bValid;
2768     OUString aCellStr;
2769     SCSIZE nIndex = 0;
2770     SCROW nRow = nRow1;
2771     OSL_ENSURE( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" );
2772     SCTAB   nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab);
2773     SCROW   nDBRow1 = rQueryParam.nRow1;
2774     SCCOL   nDBCol2 = rQueryParam.nCol2;
2775 
2776     SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1);
2777     rQueryParam.Resize( nNewEntries );
2778     svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2779 
2780     do
2781     {
2782         ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex);
2783 
2784         bValid = false;
2785         // First column AND/OR
2786         if (nIndex > 0)
2787         {
2788             aCellStr = GetUpperCellString(nCol1, nRow);
2789             if ( aCellStr == ScResId(STR_TABLE_AND) )
2790             {
2791                 rEntry.eConnect = SC_AND;
2792                 bValid = true;
2793             }
2794             else if ( aCellStr == ScResId(STR_TABLE_OR) )
2795             {
2796                 rEntry.eConnect = SC_OR;
2797                 bValid = true;
2798             }
2799         }
2800         // Second column field name
2801         if ((nIndex < 1) || bValid)
2802         {
2803             bool bFound = false;
2804             aCellStr = GetUpperCellString(nCol1 + 1, nRow);
2805             for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++)
2806             {
2807                 OUString aFieldStr;
2808                 if ( nTab == nDBTab )
2809                     aFieldStr = GetUpperCellString(i, nDBRow1);
2810                 else
2811                     aFieldStr = rDocument.GetUpperCellString(i, nDBRow1, nDBTab);
2812                 bFound = (aCellStr == aFieldStr);
2813                 if (bFound)
2814                 {
2815                     rEntry.nField = i;
2816                     bValid = true;
2817                 }
2818                 else
2819                     bValid = false;
2820             }
2821         }
2822         // Third column operator =<>...
2823         if (bValid)
2824         {
2825             aCellStr = GetUpperCellString(nCol1 + 2, nRow);
2826             if (aCellStr.startsWith("<"))
2827             {
2828                 if (aCellStr[1] == '>')
2829                     rEntry.eOp = SC_NOT_EQUAL;
2830                 else if (aCellStr[1] == '=')
2831                     rEntry.eOp = SC_LESS_EQUAL;
2832                 else
2833                     rEntry.eOp = SC_LESS;
2834             }
2835             else if (aCellStr.startsWith(">"))
2836             {
2837                 if (aCellStr[1] == '=')
2838                     rEntry.eOp = SC_GREATER_EQUAL;
2839                 else
2840                     rEntry.eOp = SC_GREATER;
2841             }
2842             else if (aCellStr.startsWith("="))
2843                 rEntry.eOp = SC_EQUAL;
2844 
2845         }
2846         // Fourth column values
2847         if (bValid)
2848         {
2849             OUString aStr = GetString(nCol1 + 3, nRow);
2850             rEntry.GetQueryItem().maString = rPool.intern(aStr);
2851             rEntry.bDoQuery = true;
2852         }
2853         nIndex++;
2854         nRow++;
2855     }
2856     while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ );
2857     return bValid;
2858 }
2859 
2860 bool ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam)
2861 {
2862     SCSIZE i, nCount;
2863     PutInOrder(nCol1, nCol2);
2864     PutInOrder(nRow1, nRow2);
2865 
2866     nCount = rQueryParam.GetEntryCount();
2867     for (i=0; i < nCount; i++)
2868         rQueryParam.GetEntry(i).Clear();
2869 
2870     // Standard query table
2871     bool bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2872     // Excel Query table
2873     if (!bValid)
2874         bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam);
2875 
2876     SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
2877     nCount = rQueryParam.GetEntryCount();
2878     if (bValid)
2879     {
2880         //  query type must be set
2881         for (i=0; i < nCount; i++)
2882         {
2883             ScQueryEntry::Item& rItem = rQueryParam.GetEntry(i).GetQueryItem();
2884             sal_uInt32 nIndex = 0;
2885             bool bNumber = pFormatter->IsNumberFormat(
2886                 rItem.maString.getString(), nIndex, rItem.mfVal);
2887             bool bDateFormat = false;
2888             rItem.meType = bNumber && CanOptimizeQueryStringToNumber( pFormatter, nIndex, bDateFormat )
2889                 ? ScQueryEntry::ByValue : (bDateFormat ? ScQueryEntry::ByDate : ScQueryEntry::ByString);
2890         }
2891     }
2892     else
2893     {
2894         for (i=0; i < nCount; i++)
2895             rQueryParam.GetEntry(i).Clear();
2896     }
2897     return bValid;
2898 }
2899 
2900 bool ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
2901 {
2902     if (nStartRow == nEndRow)
2903         // Assume only data.
2904         /* XXX NOTE: previous behavior still checked this one row and could
2905          * evaluate it has header row, but that doesn't make much sense. */
2906         return false;
2907 
2908     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2909     {
2910         CellType eType = GetCellType( nCol, nStartRow );
2911         // Any non-text cell in first row => not headers.
2912         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2913             return false;
2914     }
2915 
2916     // First row all text cells, any non-text cell in second row => headers.
2917     SCROW nTestRow = nStartRow + 1;
2918     for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
2919     {
2920         CellType eType = GetCellType( nCol, nTestRow );
2921         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2922             return true;
2923     }
2924 
2925     // Also second row all text cells => first row not headers.
2926     return false;
2927 }
2928 
2929 bool ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
2930 {
2931     if (nStartCol == nEndCol)
2932         // Assume only data.
2933         /* XXX NOTE: previous behavior still checked this one column and could
2934          * evaluate it has header column, but that doesn't make much sense. */
2935         return false;
2936 
2937     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
2938     {
2939         CellType eType = GetCellType( nStartCol, nRow );
2940         // Any non-text cell in first column => not headers.
2941         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2942             return false;
2943     }
2944 
2945     // First column all text cells, any non-text cell in second column => headers.
2946     SCCOL nTestCol = nStartCol + 1;
2947     for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++)
2948     {
2949         CellType eType = GetCellType( nRow, nTestCol );
2950         if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
2951             return true;
2952     }
2953 
2954     // Also second column all text cells => first column not headers.
2955     return false;
2956 }
2957 
2958 void ScTable::GetFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, ScFilterEntries& rFilterEntries, bool bFiltering )
2959 {
2960     if (nCol >= aCol.size())
2961         return;
2962 
2963     sc::ColumnBlockConstPosition aBlockPos;
2964     aCol[nCol].InitBlockPosition(aBlockPos);
2965     aCol[nCol].GetFilterEntries(aBlockPos, nRow1, nRow2, rFilterEntries, bFiltering, false /*bFilteredRow*/);
2966 }
2967 
2968 void ScTable::GetFilteredFilterEntries(
2969     SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, ScFilterEntries& rFilterEntries, bool bFiltering )
2970 {
2971     if (nCol >= aCol.size())
2972         return;
2973 
2974     sc::ColumnBlockConstPosition aBlockPos;
2975     aCol[nCol].InitBlockPosition(aBlockPos);
2976 
2977     // remove the entry for this column from the query parameter
2978     ScQueryParam aParam( rParam );
2979     aParam.RemoveEntryByField(nCol);
2980 
2981     lcl_PrepareQuery(&rDocument, this, aParam, true);
2982     ScQueryEvaluator queryEvaluator(GetDoc(), *this, aParam);
2983     for ( SCROW j = nRow1; j <= nRow2; ++j )
2984     {
2985         if (queryEvaluator.ValidQuery(j))
2986         {
2987             aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, false/*bFilteredRow*/);
2988         }
2989         else
2990         {
2991             aCol[nCol].GetFilterEntries(aBlockPos, j, j, rFilterEntries, bFiltering, true/*bFilteredRow*/);
2992         }
2993     }
2994 }
2995 
2996 bool ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, std::set<ScTypedStrData>& rStrings)
2997 {
2998     if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
2999         return false;
3000     return aCol[nCol].GetDataEntries( nRow, rStrings);
3001 }
3002 
3003 sal_uInt64 ScTable::GetCellCount() const
3004 {
3005     sal_uInt64 nCellCount = 0;
3006 
3007     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3008         nCellCount += aCol[nCol].GetCellCount();
3009 
3010     return nCellCount;
3011 }
3012 
3013 sal_uInt64 ScTable::GetWeightedCount() const
3014 {
3015     sal_uInt64 nCellCount = 0;
3016 
3017     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3018         nCellCount += aCol[nCol].GetWeightedCount();
3019 
3020     return nCellCount;
3021 }
3022 
3023 sal_uInt64 ScTable::GetWeightedCount(SCROW nStartRow, SCROW nEndRow) const
3024 {
3025     sal_uInt64 nCellCount = 0;
3026 
3027     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3028         nCellCount += aCol[nCol].GetWeightedCount(nStartRow, nEndRow);
3029 
3030     return nCellCount;
3031 }
3032 
3033 sal_uInt64 ScTable::GetCodeCount() const
3034 {
3035     sal_uInt64 nCodeCount = 0;
3036 
3037     for ( SCCOL nCol=0; nCol < aCol.size(); nCol++ )
3038         if ( aCol[nCol].GetCellCount() )
3039             nCodeCount += aCol[nCol].GetCodeCount();
3040 
3041     return nCodeCount;
3042 }
3043 
3044 sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart,
3045         SCROW nRowEnd, rtl_TextEncoding eCharSet ) const
3046 {
3047     if ( IsColValid( nCol ) )
3048         return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet );
3049     else
3050         return 0;
3051 }
3052 
3053 sal_Int32 ScTable::GetMaxNumberStringLen(
3054     sal_uInt16& nPrecision, SCCOL nCol, SCROW nRowStart, SCROW nRowEnd ) const
3055 {
3056     if ( IsColValid( nCol ) )
3057         return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd );
3058     else
3059         return 0;
3060 }
3061 
3062 void ScTable::UpdateSelectionFunction( ScFunctionData& rData, const ScMarkData& rMark )
3063 {
3064     ScRangeList aRanges = rMark.GetMarkedRangesForTab( nTab );
3065     ScRange aMarkArea( ScAddress::UNINITIALIZED );
3066     if (rMark.IsMultiMarked())
3067         aMarkArea = rMark.GetMultiMarkArea();
3068     else if (rMark.IsMarked())
3069         aMarkArea = rMark.GetMarkArea();
3070     else
3071     {
3072         assert(!"ScTable::UpdateSelectionFunction - called without anything marked");
3073         aMarkArea.aStart.SetCol(0);
3074         aMarkArea.aEnd.SetCol(rDocument.MaxCol());
3075     }
3076     const SCCOL nStartCol = aMarkArea.aStart.Col();
3077     const SCCOL nEndCol = ClampToAllocatedColumns(aMarkArea.aEnd.Col());
3078     for (SCCOL nCol = nStartCol; nCol <= nEndCol && !rData.getError(); ++nCol)
3079     {
3080         if (mpColFlags && ColHidden(nCol))
3081             continue;
3082 
3083         aCol[nCol].UpdateSelectionFunction(aRanges, rData, *mpHiddenRows);
3084     }
3085 }
3086 
3087 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3088